2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Commands
32 * Kern Sibbald, September MM
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)
155 BSOCK *user = ua->UA_sock;
159 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
164 while (ua->jcr->wstorage->size()) {
165 ua->jcr->wstorage->remove(0);
168 len = strlen(ua->argk[0]);
169 for (i=0; i<comsize; i++) { /* search for command */
170 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
171 /* Check if command permitted, but "quit" is always OK */
172 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
173 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
176 if (ua->api) user->signal(BNET_CMD_BEGIN);
177 ok = (*commands[i].func)(ua, cmd); /* go execute command */
183 user->fsend(_("%s: is an invalid command.\n"), ua->argk[0]);
185 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
190 * This is a common routine used to stuff the Pool DB record defaults
191 * into the Media DB record just before creating a media (Volume)
194 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
196 mr->PoolId = pr->PoolId;
197 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
198 mr->Recycle = pr->Recycle;
199 mr->VolRetention = pr->VolRetention;
200 mr->VolUseDuration = pr->VolUseDuration;
201 mr->RecyclePoolId = pr->RecyclePoolId;
202 mr->MaxVolJobs = pr->MaxVolJobs;
203 mr->MaxVolFiles = pr->MaxVolFiles;
204 mr->MaxVolBytes = pr->MaxVolBytes;
205 mr->LabelType = pr->LabelType;
211 * Add Volumes to an existing Pool
213 static int add_cmd(UAContext *ua, const char *cmd)
217 int num, i, max, startnum;
219 char name[MAX_NAME_LENGTH];
221 int Slot = 0, InChanger = 0;
224 "You probably don't want to be using this command since it\n"
225 "creates database records without labeling the Volumes.\n"
226 "You probably want to use the \"label\" command.\n\n"));
228 if (!open_client_db(ua)) {
232 memset(&pr, 0, sizeof(pr));
233 memset(&mr, 0, sizeof(mr));
235 if (!get_pool_dbr(ua, &pr)) {
239 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
240 pr.MaxVols, pr.PoolType);
242 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
243 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
245 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
248 pr.MaxVols = ua->pint32_val;
253 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
254 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
255 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
259 if (pr.MaxVols == 0) {
262 max = pr.MaxVols - pr.NumVols;
266 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
267 if (!get_pint(ua, buf)) {
270 num = ua->pint32_val;
271 if (num < 0 || num > max) {
272 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
279 if (!get_cmd(ua, _("Enter Volume name: "))) {
283 if (!get_cmd(ua, _("Enter base volume name: "))) {
287 /* Don't allow | in Volume name because it is the volume separator character */
288 if (!is_volume_name_legal(ua, ua->cmd)) {
291 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
292 bsendmsg(ua, _("Volume name too long.\n"));
295 if (strlen(ua->cmd) == 0) {
296 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
300 bstrncpy(name, ua->cmd, sizeof(name));
302 bstrncat(name, "%04d", sizeof(name));
305 if (!get_pint(ua, _("Enter the starting number: "))) {
308 startnum = ua->pint32_val;
310 bsendmsg(ua, _("Start number must be greater than zero.\n"));
320 if (store && store->autochanger) {
321 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
324 Slot = ua->pint32_val;
325 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
328 InChanger = ua->pint32_val;
331 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
332 for (i=startnum; i < num+startnum; i++) {
333 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
335 mr.InChanger = InChanger;
336 mr.StorageId = store->StorageId;
338 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
339 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
340 bsendmsg(ua, "%s", db_strerror(ua->db));
344 first_id = mr.PoolId;
348 Dmsg0(200, "Update pool record.\n");
349 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
350 bsendmsg(ua, "%s", db_strerror(ua->db));
353 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
359 * Turn auto mount on/off
364 int automount_cmd(UAContext *ua, const char *cmd)
369 if (!get_cmd(ua, _("Turn on or off? "))) {
377 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
385 static int cancel_cmd(UAContext *ua, const char *cmd)
390 char JobName[MAX_NAME_LENGTH];
392 for (i=1; i<ua->argc; i++) {
393 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
398 JobId = str_to_int64(ua->argv[i]);
399 if (!(jcr=get_jcr_by_id(JobId))) {
400 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
404 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
408 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
409 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
410 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
411 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
414 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
418 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
419 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
420 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
421 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
428 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
429 bsendmsg(ua, _("Unauthorized command from this console.\n"));
434 * If we still do not have a jcr,
435 * throw up a list and ask the user to select one.
438 int tjobs = 0; /* total # number jobs */
439 /* Count Jobs running */
441 if (jcr->JobId == 0) { /* this is us */
444 tjobs++; /* count of all jobs */
445 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
446 continue; /* skip not authorized */
448 njobs++; /* count of authorized jobs */
452 if (njobs == 0) { /* no authorized */
454 bsendmsg(ua, _("No Jobs running.\n"));
456 bsendmsg(ua, _("None of your jobs are running.\n"));
461 start_prompt(ua, _("Select Job:\n"));
464 if (jcr->JobId == 0) { /* this is us */
467 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
468 continue; /* skip not authorized */
470 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
475 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
479 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
483 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
484 jcr = get_jcr_by_full_name(JobName);
486 bsendmsg(ua, _("Job \"%s\" not found.\n"), JobName);
491 ret = cancel_job(ua, jcr);
497 * This is a common routine to create or update a
498 * Pool DB base record from a Pool Resource. We handle
499 * the setting of MaxVols and NumVols slightly differently
500 * depending on if we are creating the Pool or we are
501 * simply bringing it into agreement with the resource (updage).
503 * Caution : RecyclePoolId isn't setup in this function.
504 * You can use set_pooldbr_recyclepoolid();
507 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
509 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
510 if (op == POOL_OP_CREATE) {
511 pr->MaxVols = pool->max_volumes;
513 } else { /* update pool */
514 if (pr->MaxVols != pool->max_volumes) {
515 pr->MaxVols = pool->max_volumes;
517 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
518 pr->MaxVols = pr->NumVols;
521 pr->LabelType = pool->LabelType;
522 pr->UseOnce = pool->use_volume_once;
523 pr->UseCatalog = pool->use_catalog;
524 pr->Recycle = pool->Recycle;
525 pr->VolRetention = pool->VolRetention;
526 pr->VolUseDuration = pool->VolUseDuration;
527 pr->MaxVolJobs = pool->MaxVolJobs;
528 pr->MaxVolFiles = pool->MaxVolFiles;
529 pr->MaxVolBytes = pool->MaxVolBytes;
530 pr->AutoPrune = pool->AutoPrune;
531 pr->Recycle = pool->Recycle;
532 if (pool->label_format) {
533 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
535 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
539 /* set/update Pool.RecyclePoolId in Catalog */
540 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
544 if (!pool->RecyclePool) {
548 memset(&pr, 0, sizeof(POOL_DBR));
549 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
551 if (!db_get_pool_record(jcr, db, &pr)) {
552 return -1; /* not exists in database */
555 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
557 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
558 return -1; /* error */
561 if (!db_update_pool_record(jcr, db, &pr)) {
562 return -1; /* error */
567 /* set POOL_DBR.RecyclePoolId from Pool resource
568 * works with set_pooldbr_from_poolres
570 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
575 if (pool->RecyclePool) {
576 memset(&rpool, 0, sizeof(POOL_DBR));
578 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
579 if (db_get_pool_record(jcr, db, &rpool)) {
580 pr->RecyclePoolId = rpool.PoolId;
582 Jmsg(jcr, M_WARNING, 0,
583 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
584 "Try to update it with 'update pool=%s'\n"),
585 pool->name(), rpool.Name, rpool.Name,pool->name());
589 } else { /* no RecyclePool used, set it to 0 */
590 pr->RecyclePoolId = 0;
597 * Create a pool record from a given Pool resource
598 * Also called from backup.c
599 * Returns: -1 on error
600 * 0 record already exists
604 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
608 memset(&pr, 0, sizeof(POOL_DBR));
610 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
612 if (db_get_pool_record(jcr, db, &pr)) {
614 if (op == POOL_OP_UPDATE) { /* update request */
615 set_pooldbr_from_poolres(&pr, pool, op);
616 db_update_pool_record(jcr, db, &pr);
618 return 0; /* exists */
621 set_pooldbr_from_poolres(&pr, pool, op);
623 if (!db_create_pool_record(jcr, db, &pr)) {
624 return -1; /* error */
632 * Create a Pool Record in the database.
633 * It is always created from the Resource record.
635 static int create_cmd(UAContext *ua, const char *cmd)
639 if (!open_client_db(ua)) {
643 pool = get_pool_resource(ua);
648 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
650 bsendmsg(ua, _("Error: Pool %s already exists.\n"
651 "Use update to change it.\n"), pool->name());
655 bsendmsg(ua, "%s", db_strerror(ua->db));
661 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
666 extern DIRRES *director;
669 * Python control command
670 * python restart (restarts interpreter)
672 static int python_cmd(UAContext *ua, const char *cmd)
674 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
675 term_python_interpreter();
676 init_python_interpreter(director->name(),
677 director->scripts_directory, "DirStartUp");
678 bsendmsg(ua, _("Python interpreter restarted.\n"));
680 bsendmsg(ua, _("Nothing done.\n"));
687 * Set a new address in a Client resource. We do this only
688 * if the Console name is the same as the Client name
689 * and the Console can access the client.
691 static int setip_cmd(UAContext *ua, const char *cmd)
695 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
696 bsendmsg(ua, _("Unauthorized command from this console.\n"));
700 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
703 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
706 if (client->address) {
707 free(client->address);
709 /* MA Bug 6 remove ifdef */
710 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
711 client->address = bstrdup(buf);
712 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
713 client->name(), client->address);
720 static void do_en_disable_cmd(UAContext *ua, bool setting)
725 i = find_arg_with_value(ua, NT_("job"));
727 job = select_job_resource(ua);
733 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
737 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
741 if (!acl_access_ok(ua, Job_ACL, job->name())) {
742 bsendmsg(ua, _("Unauthorized command from this console.\n"));
745 job->enabled = setting;
746 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
750 static int enable_cmd(UAContext *ua, const char *cmd)
752 do_en_disable_cmd(ua, true);
756 static int disable_cmd(UAContext *ua, const char *cmd)
758 do_en_disable_cmd(ua, false);
763 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
769 lstore.store = store;
770 pm_strcpy(lstore.store_source, _("unknown source"));
771 set_wstorage(jcr, &lstore);
772 /* Try connecting for up to 15 seconds */
773 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
774 store->name(), store->address, store->SDport);
775 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
776 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
779 Dmsg0(120, _("Connected to storage daemon\n"));
780 sd = jcr->store_bsock;
781 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
782 if (bnet_recv(sd) >= 0) {
783 bsendmsg(ua, "%s", sd->msg);
785 bnet_sig(sd, BNET_TERMINATE);
787 jcr->store_bsock = NULL;
791 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
795 /* Connect to File daemon */
797 ua->jcr->client = client;
798 /* Try to connect for 15 seconds */
799 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
800 client->name(), client->address, client->FDport);
801 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
802 bsendmsg(ua, _("Failed to connect to Client.\n"));
805 Dmsg0(120, "Connected to file daemon\n");
806 fd = ua->jcr->file_bsock;
807 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
808 if (bnet_recv(fd) >= 0) {
809 bsendmsg(ua, "%s", fd->msg);
811 bnet_sig(fd, BNET_TERMINATE);
813 ua->jcr->file_bsock = NULL;
818 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
820 STORE *store, **unique_store;
821 CLIENT *client, **unique_client;
827 /* Count Storage items */
831 foreach_res(store, R_STORAGE) {
834 unique_store = (STORE **) malloc(i * sizeof(STORE));
835 /* Find Unique Storage address/port */
836 store = (STORE *)GetNextRes(R_STORAGE, NULL);
838 unique_store[i++] = store;
839 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
841 for (j=0; j<i; j++) {
842 if (strcmp(unique_store[j]->address, store->address) == 0 &&
843 unique_store[j]->SDport == store->SDport) {
849 unique_store[i++] = store;
850 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
855 /* Call each unique Storage daemon */
856 for (j=0; j<i; j++) {
857 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
861 /* Count Client items */
865 foreach_res(client, R_CLIENT) {
868 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
869 /* Find Unique Client address/port */
870 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
872 unique_client[i++] = client;
873 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
875 for (j=0; j<i; j++) {
876 if (strcmp(unique_client[j]->address, client->address) == 0 &&
877 unique_client[j]->FDport == client->FDport) {
883 unique_client[i++] = client;
884 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
889 /* Call each unique File daemon */
890 for (j=0; j<i; j++) {
891 do_client_setdebug(ua, unique_client[j], level, trace_flag);
897 * setdebug level=nn all trace=1/0
899 static int setdebug_cmd(UAContext *ua, const char *cmd)
907 if (!open_client_db(ua)) {
910 Dmsg1(120, "setdebug:%s:\n", cmd);
913 i = find_arg_with_value(ua, "level");
915 level = atoi(ua->argv[i]);
918 if (!get_pint(ua, _("Enter new debug level: "))) {
921 level = ua->pint32_val;
924 /* Look for trace flag. -1 => not change */
925 i = find_arg_with_value(ua, "trace");
927 trace_flag = atoi(ua->argv[i]);
928 if (trace_flag > 0) {
934 for (i=1; i<ua->argc; i++) {
935 if (strcasecmp(ua->argk[i], "all") == 0) {
936 do_all_setdebug(ua, level, trace_flag);
939 if (strcasecmp(ua->argk[i], "dir") == 0 ||
940 strcasecmp(ua->argk[i], "director") == 0) {
942 set_trace(trace_flag);
945 if (strcasecmp(ua->argk[i], "client") == 0 ||
946 strcasecmp(ua->argk[i], "fd") == 0) {
949 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
951 do_client_setdebug(ua, client, level, trace_flag);
955 client = select_client_resource(ua);
957 do_client_setdebug(ua, client, level, trace_flag);
962 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
963 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
964 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
967 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
969 do_storage_setdebug(ua, store, level, trace_flag);
973 store = get_storage_resource(ua, false/*no default*/);
975 do_storage_setdebug(ua, store, level, trace_flag);
981 * We didn't find an appropriate keyword above, so
984 start_prompt(ua, _("Available daemons are: \n"));
985 add_prompt(ua, _("Director"));
986 add_prompt(ua, _("Storage"));
987 add_prompt(ua, _("Client"));
988 add_prompt(ua, _("All"));
989 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
990 case 0: /* Director */
992 set_trace(trace_flag);
995 store = get_storage_resource(ua, false/*no default*/);
997 do_storage_setdebug(ua, store, level, trace_flag);
1001 client = select_client_resource(ua);
1003 do_client_setdebug(ua, client, level, trace_flag);
1007 do_all_setdebug(ua, level, trace_flag);
1016 * Turn debug tracing to file on/off
1018 static int trace_cmd(UAContext *ua, const char *cmd)
1022 if (ua->argc != 2) {
1023 if (!get_cmd(ua, _("Turn on or off? "))) {
1028 onoff = ua->argk[1];
1031 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1036 static int var_cmd(UAContext *ua, const char *cmd)
1038 POOLMEM *val = get_pool_memory(PM_FNAME);
1041 if (!open_client_db(ua)) {
1044 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1047 while (*var == ' ') { /* skip spaces */
1050 Dmsg1(100, "Var=%s:\n", var);
1051 variable_expansion(ua->jcr, var, &val);
1052 bsendmsg(ua, "%s\n", val);
1053 free_pool_memory(val);
1057 static int estimate_cmd(UAContext *ua, const char *cmd)
1060 CLIENT *client = NULL;
1061 FILESET *fileset = NULL;
1063 char since[MAXSTRING];
1066 jcr->JobLevel = L_FULL;
1067 for (int i=1; i<ua->argc; i++) {
1068 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1069 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1071 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1075 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1077 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1078 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1079 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1085 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1087 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1088 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1089 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1095 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1099 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1100 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1101 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1106 if (!job && !(client && fileset)) {
1107 if (!(job = select_job_resource(ua))) {
1112 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1114 bsendmsg(ua, _("No job specified.\n"));
1117 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1118 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1123 client = job->client;
1126 fileset = job->fileset;
1128 jcr->client = client;
1129 jcr->fileset = fileset;
1131 ua->catalog = client->catalog;
1138 jcr->JobType = JT_BACKUP;
1139 init_jcr_job_record(jcr);
1141 if (!get_or_create_client_record(jcr)) {
1144 if (!get_or_create_fileset_record(jcr)) {
1148 get_level_since_time(ua->jcr, since, sizeof(since));
1150 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1151 job->client->name(), job->client->address, job->client->FDport);
1152 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1153 bsendmsg(ua, _("Failed to connect to Client.\n"));
1157 if (!send_include_list(jcr)) {
1158 bsendmsg(ua, _("Error sending include list.\n"));
1162 if (!send_exclude_list(jcr)) {
1163 bsendmsg(ua, _("Error sending exclude list.\n"));
1167 if (!send_level_command(jcr)) {
1171 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1172 while (bnet_recv(jcr->file_bsock) >= 0) {
1173 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1177 if (jcr->file_bsock) {
1178 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1179 bnet_close(jcr->file_bsock);
1180 jcr->file_bsock = NULL;
1189 static int time_cmd(UAContext *ua, const char *cmd)
1192 time_t ttime = time(NULL);
1194 (void)localtime_r(&ttime, &tm);
1195 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1196 bsendmsg(ua, "%s\n", sdt);
1201 * reload the conf file
1203 extern "C" void reload_config(int sig);
1205 static int reload_cmd(UAContext *ua, const char *cmd)
1212 * Delete Pool records (should purge Media with it).
1214 * delete pool=<pool-name>
1215 * delete volume pool=<pool-name> volume=<name>
1218 static int delete_cmd(UAContext *ua, const char *cmd)
1220 static const char *keywords[] = {
1226 if (!open_client_db(ua)) {
1230 switch (find_arg_keyword(ua, keywords)) {
1239 while ((i=find_arg(ua, "jobid")) > 0) {
1241 *ua->argk[i] = 0; /* zap keyword already visited */
1249 "In general it is not a good idea to delete either a\n"
1250 "Pool or a Volume since they may contain data.\n\n"));
1252 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1263 bsendmsg(ua, _("Nothing done.\n"));
1271 * delete_job has been modified to parse JobID lists like the
1273 * delete JobID=3,4,6,7-11,14
1275 * Thanks to Phil Stracchino for the above addition.
1278 static void delete_job(UAContext *ua)
1283 int i = find_arg_with_value(ua, NT_("jobid"));
1285 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1286 s = bstrdup(ua->argv[i]);
1289 * We could use strtok() here. But we're not going to, because:
1290 * (a) strtok() is deprecated, having been replaced by strsep();
1291 * (b) strtok() is broken in significant ways.
1292 * we could use strsep() instead, but it's not universally available.
1293 * so we grow our own using strchr().
1295 sep = strchr(tok, ',');
1296 while (sep != NULL) {
1298 if (strchr(tok, '-')) {
1299 delete_job_id_range(ua, tok);
1301 JobId = str_to_int64(tok);
1302 do_job_delete(ua, JobId);
1305 sep = strchr(tok, ',');
1307 /* pick up the last token */
1308 if (strchr(tok, '-')) {
1309 delete_job_id_range(ua, tok);
1311 JobId = str_to_int64(tok);
1312 do_job_delete(ua, JobId);
1317 JobId = str_to_int64(ua->argv[i]);
1318 do_job_delete(ua, JobId);
1320 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1323 JobId = ua->int64_val;
1324 do_job_delete(ua, JobId);
1329 * we call delete_job_id_range to parse range tokens and iterate over ranges
1331 static void delete_job_id_range(UAContext *ua, char *tok)
1336 tok2 = strchr(tok, '-');
1339 j1 = str_to_int64(tok);
1340 j2 = str_to_int64(tok2);
1341 for (j=j1; j<=j2; j++) {
1342 do_job_delete(ua, j);
1347 * do_job_delete now performs the actual delete operation atomically
1350 static void do_job_delete(UAContext *ua, JobId_t JobId)
1352 POOL_MEM query(PM_MESSAGE);
1355 purge_files_from_job(ua, JobId);
1356 purge_job_from_catalog(ua, JobId);
1357 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1361 * Delete media records from database -- dangerous
1363 static int delete_volume(UAContext *ua)
1367 if (!select_media_dbr(ua, &mr)) {
1370 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1371 "and all Jobs saved on that volume from the Catalog\n"),
1374 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1377 if (ua->pint32_val) {
1378 db_delete_media_record(ua->jcr, ua->db, &mr);
1384 * Delete a pool record from the database -- dangerous
1386 static int delete_pool(UAContext *ua)
1390 memset(&pr, 0, sizeof(pr));
1392 if (!get_pool_dbr(ua, &pr)) {
1395 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1398 if (ua->pint32_val) {
1399 db_delete_pool_record(ua->jcr, ua->db, &pr);
1405 static void do_mount_cmd(UAContext *ua, const char *command)
1410 char dev_name[MAX_NAME_LENGTH];
1414 if (!open_client_db(ua)) {
1417 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1419 store.store = get_storage_resource(ua, true/*arg is storage*/);
1420 pm_strcpy(store.store_source, _("unknown source"));
1424 set_wstorage(jcr, &store);
1425 drive = get_storage_drive(ua, store.store);
1426 if (strcmp(command, "mount") == 0) {
1427 slot = get_storage_slot(ua, store.store);
1430 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1431 store.store->media_type, store.store->dev_name(), drive);
1433 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1434 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1437 sd = jcr->store_bsock;
1438 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1439 bash_spaces(dev_name);
1441 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1443 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1445 while (bnet_recv(sd) >= 0) {
1446 bsendmsg(ua, "%s", sd->msg);
1448 bnet_sig(sd, BNET_TERMINATE);
1450 jcr->store_bsock = NULL;
1454 * mount [storage=<name>] [drive=nn] [slot=mm]
1456 static int mount_cmd(UAContext *ua, const char *cmd)
1458 do_mount_cmd(ua, "mount"); /* mount */
1464 * unmount [storage=<name>] [drive=nn]
1466 static int unmount_cmd(UAContext *ua, const char *cmd)
1468 do_mount_cmd(ua, "unmount"); /* unmount */
1474 * release [storage=<name>] [drive=nn]
1476 static int release_cmd(UAContext *ua, const char *cmd)
1478 do_mount_cmd(ua, "release"); /* release */
1485 * use catalog=<name>
1487 static int use_cmd(UAContext *ua, const char *cmd)
1489 CAT *oldcatalog, *catalog;
1492 close_db(ua); /* close any previously open db */
1493 oldcatalog = ua->catalog;
1495 if (!(catalog = get_catalog_resource(ua))) {
1496 ua->catalog = oldcatalog;
1498 ua->catalog = catalog;
1501 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1502 ua->catalog->name(), ua->catalog->db_name);
1507 int quit_cmd(UAContext *ua, const char *cmd)
1513 /* Handler to get job status */
1514 static int status_handler(void *ctx, int num_fields, char **row)
1516 char *val = (char *)ctx;
1521 *val = '?'; /* Unknown by default */
1528 * Wait until no job is running
1530 int wait_cmd(UAContext *ua, const char *cmd)
1535 * Wait until no job is running
1537 if (ua->argc == 1) {
1538 bmicrosleep(0, 200000); /* let job actually start */
1539 for (bool running=true; running; ) {
1542 if (jcr->JobId != 0) {
1556 /* we have jobid, jobname or ujobid argument */
1558 uint32_t jobid = 0 ;
1560 if (!open_client_db(ua)) {
1561 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1565 for (int i=1; i<ua->argc; i++) {
1566 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1570 jobid = str_to_int64(ua->argv[i]);
1572 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1573 strcasecmp(ua->argk[i], "job") == 0) {
1577 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1579 jobid = jcr->JobId ;
1583 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1587 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1589 jobid = jcr->JobId ;
1597 bsendmsg(ua, _("ERR: Job was not found\n"));
1602 * We wait the end of job
1605 bmicrosleep(0, 200000); /* let job actually start */
1606 for (bool running=true; running; ) {
1609 jcr=get_jcr_by_id(jobid) ;
1622 * We have to get JobStatus
1626 char jobstatus = '?'; /* Unknown by default */
1629 bsnprintf(buf, sizeof(buf),
1630 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1633 db_sql_query(ua->db, buf,
1634 status_handler, (void *)&jobstatus);
1636 switch (jobstatus) {
1638 status = 1 ; /* Warning */
1642 case JS_ErrorTerminated:
1644 status = 2 ; /* Critical */
1648 status = 0 ; /* Ok */
1652 status = 3 ; /* Unknown */
1656 bsendmsg(ua, "JobId=%i\n", jobid) ;
1657 bsendmsg(ua, "JobStatus=%s (%c)\n",
1658 job_status_to_str(jobstatus),
1662 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1669 static int help_cmd(UAContext *ua, const char *cmd)
1673 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1674 for (i=0; i<comsize; i++) {
1675 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1677 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1681 int qhelp_cmd(UAContext *ua, const char *cmd)
1685 for (i=0; i<comsize; i++) {
1686 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1691 static int version_cmd(UAContext *ua, const char *cmd)
1693 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1698 * This call explicitly checks for a catalog=xxx and
1699 * if given, opens that catalog. It also checks for
1700 * client=xxx and if found, opens the catalog
1701 * corresponding to that client. If we still don't
1702 * have a catalog, look for a Job keyword and get the
1703 * catalog from its client record.
1705 bool open_client_db(UAContext *ua)
1712 /* Try for catalog keyword */
1713 i = find_arg_with_value(ua, NT_("catalog"));
1715 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1716 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1719 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1721 if (ua->catalog && ua->catalog != catalog) {
1724 ua->catalog = catalog;
1729 /* Try for client keyword */
1730 i = find_arg_with_value(ua, NT_("client"));
1732 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1733 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1736 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1738 catalog = client->catalog;
1739 if (ua->catalog && ua->catalog != catalog) {
1742 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1743 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1746 ua->catalog = catalog;
1751 /* Try for Job keyword */
1752 i = find_arg_with_value(ua, NT_("job"));
1754 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1755 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1758 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1760 catalog = job->client->catalog;
1761 if (ua->catalog && ua->catalog != catalog) {
1764 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1765 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1768 ua->catalog = catalog;
1778 * Open the catalog database.
1780 bool open_db(UAContext *ua)
1786 ua->catalog = get_catalog_resource(ua);
1788 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1793 ua->jcr->catalog = ua->catalog;
1795 Dmsg0(150, "Open database\n");
1796 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1797 ua->catalog->db_password, ua->catalog->db_address,
1798 ua->catalog->db_port, ua->catalog->db_socket,
1799 ua->catalog->mult_db_connections);
1800 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1801 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1802 ua->catalog->db_name);
1804 bsendmsg(ua, "%s", db_strerror(ua->db));
1809 ua->jcr->db = ua->db;
1810 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1811 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1815 void close_db(UAContext *ua)
1818 db_close_database(ua->jcr, ua->db);