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 and included
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 autodisplay_cmd(UAContext *ua, const char *cmd);
51 extern int gui_cmd(UAContext *ua, const char *cmd);
52 extern int label_cmd(UAContext *ua, const char *cmd);
53 extern int list_cmd(UAContext *ua, const char *cmd);
54 extern int llist_cmd(UAContext *ua, const char *cmd);
55 extern int messagescmd(UAContext *ua, const char *cmd);
56 extern int prunecmd(UAContext *ua, const char *cmd);
57 extern int purgecmd(UAContext *ua, const char *cmd);
58 extern int querycmd(UAContext *ua, const char *cmd);
59 extern int relabel_cmd(UAContext *ua, const char *cmd);
60 extern int restore_cmd(UAContext *ua, const char *cmd);
61 extern int retentioncmd(UAContext *ua, const char *cmd);
62 extern int show_cmd(UAContext *ua, const char *cmd);
63 extern int sqlquerycmd(UAContext *ua, const char *cmd);
64 extern int status_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 automount_cmd(UAContext *ua, const char *cmd);
70 static int cancel_cmd(UAContext *ua, const char *cmd);
71 static int create_cmd(UAContext *ua, const char *cmd);
72 static int delete_cmd(UAContext *ua, const char *cmd);
73 static int disable_cmd(UAContext *ua, const char *cmd);
74 static int enable_cmd(UAContext *ua, const char *cmd);
75 static int estimate_cmd(UAContext *ua, const char *cmd);
76 static int help_cmd(UAContext *ua, const char *cmd);
77 static int memory_cmd(UAContext *ua, const char *cmd);
78 static int mount_cmd(UAContext *ua, const char *cmd);
79 static int python_cmd(UAContext *ua, const char *cmd);
80 static int release_cmd(UAContext *ua, const char *cmd);
81 static int reload_cmd(UAContext *ua, const char *cmd);
82 static int setdebug_cmd(UAContext *ua, const char *cmd);
83 static int setip_cmd(UAContext *ua, const char *cmd);
84 static int time_cmd(UAContext *ua, const char *cmd);
85 static int trace_cmd(UAContext *ua, const char *cmd);
86 static int unmount_cmd(UAContext *ua, const char *cmd);
87 static int use_cmd(UAContext *ua, const char *cmd);
88 static int var_cmd(UAContext *ua, const char *cmd);
89 static int version_cmd(UAContext *ua, const char *cmd);
90 static int wait_cmd(UAContext *ua, const char *cmd);
92 static void do_job_delete(UAContext *ua, JobId_t JobId);
93 static void delete_job_id_range(UAContext *ua, char *tok);
94 static int delete_volume(UAContext *ua);
95 static int delete_pool(UAContext *ua);
96 static void delete_job(UAContext *ua);
98 int qhelp_cmd(UAContext *ua, const char *cmd);
99 int quit_cmd(UAContext *ua, const char *cmd);
102 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
103 static struct cmdstruct commands[] = {
104 { NT_("add"), add_cmd, _("add media to a pool")},
105 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
106 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
107 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
108 { NT_("create"), create_cmd, _("create DB Pool from resource")},
109 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
110 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
111 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
112 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
113 { NT_("exit"), quit_cmd, _("exit = quit")},
114 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
115 { NT_("help"), help_cmd, _("print this command")},
116 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
117 { NT_("label"), label_cmd, _("label a tape")},
118 { NT_("llist"), llist_cmd, _("full or long list like list command")},
119 { NT_("memory"), memory_cmd, _("print current memory usage")},
120 { NT_("messages"), messagescmd, _("messages")},
121 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
122 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
123 { NT_("purge"), purgecmd, _("purge records from catalog")},
124 { NT_("python"), python_cmd, _("python control commands")},
125 { NT_("quit"), quit_cmd, _("quit")},
126 { NT_("query"), querycmd, _("query catalog")},
127 { NT_("restore"), restore_cmd, _("restore files")},
128 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
129 { NT_("release"), release_cmd, _("release <storage-name>")},
130 { NT_("reload"), reload_cmd, _("reload conf file")},
131 { NT_("run"), run_cmd, _("run <job-name>")},
132 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
133 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
134 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
135 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
136 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
137 { NT_("time"), time_cmd, _("print current time")},
138 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
139 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
140 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
141 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
142 { NT_("use"), use_cmd, _("use catalog xxx")},
143 { NT_("var"), var_cmd, _("does variable expansion")},
144 { NT_("version"), version_cmd, _("print Director version")},
145 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
147 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
150 * Execute a command from the UA
152 int do_a_command(UAContext *ua, const char *cmd)
158 BSOCK *user = ua->UA_sock;
162 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
167 while (ua->jcr->wstorage->size()) {
168 ua->jcr->wstorage->remove(0);
171 len = strlen(ua->argk[0]);
172 for (i=0; i<comsize; i++) { /* search for command */
173 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
174 /* Check if command permitted, but "quit" is always OK */
175 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
176 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
179 if (ua->api) user->signal(BNET_CMD_BEGIN);
180 ok = (*commands[i].func)(ua, cmd); /* go execute command */
186 user->fsend(_("%s: is an invalid command.\n"), ua->argk[0]);
188 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
193 * This is a common routine used to stuff the Pool DB record defaults
194 * into the Media DB record just before creating a media (Volume)
197 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
199 mr->PoolId = pr->PoolId;
200 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
201 mr->Recycle = pr->Recycle;
202 mr->VolRetention = pr->VolRetention;
203 mr->VolUseDuration = pr->VolUseDuration;
204 mr->RecyclePoolId = pr->RecyclePoolId;
205 mr->MaxVolJobs = pr->MaxVolJobs;
206 mr->MaxVolFiles = pr->MaxVolFiles;
207 mr->MaxVolBytes = pr->MaxVolBytes;
208 mr->LabelType = pr->LabelType;
214 * Add Volumes to an existing Pool
216 static int add_cmd(UAContext *ua, const char *cmd)
220 int num, i, max, startnum;
222 char name[MAX_NAME_LENGTH];
224 int Slot = 0, InChanger = 0;
227 "You probably don't want to be using this command since it\n"
228 "creates database records without labeling the Volumes.\n"
229 "You probably want to use the \"label\" command.\n\n"));
231 if (!open_client_db(ua)) {
235 memset(&pr, 0, sizeof(pr));
236 memset(&mr, 0, sizeof(mr));
238 if (!get_pool_dbr(ua, &pr)) {
242 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
243 pr.MaxVols, pr.PoolType);
245 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
246 ua->warning_msg(_("Pool already has maximum volumes=%d\n"), pr.MaxVols);
248 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
251 pr.MaxVols = ua->pint32_val;
256 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
257 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
258 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
262 if (pr.MaxVols == 0) {
265 max = pr.MaxVols - pr.NumVols;
269 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
270 if (!get_pint(ua, buf)) {
273 num = ua->pint32_val;
274 if (num < 0 || num > max) {
275 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
282 if (!get_cmd(ua, _("Enter Volume name: "))) {
286 if (!get_cmd(ua, _("Enter base volume name: "))) {
290 /* Don't allow | in Volume name because it is the volume separator character */
291 if (!is_volume_name_legal(ua, ua->cmd)) {
294 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
295 ua->warning_msg(_("Volume name too long.\n"));
298 if (strlen(ua->cmd) == 0) {
299 ua->warning_msg(_("Volume name must be at least one character long.\n"));
303 bstrncpy(name, ua->cmd, sizeof(name));
305 bstrncat(name, "%04d", sizeof(name));
308 if (!get_pint(ua, _("Enter the starting number: "))) {
311 startnum = ua->pint32_val;
313 ua->warning_msg(_("Start number must be greater than zero.\n"));
323 if (store && store->autochanger) {
324 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
327 Slot = ua->pint32_val;
328 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
331 InChanger = ua->pint32_val;
334 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
335 for (i=startnum; i < num+startnum; i++) {
336 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
338 mr.InChanger = InChanger;
339 mr.StorageId = store->StorageId;
341 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
342 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
343 ua->error_msg("%s", db_strerror(ua->db));
347 first_id = mr.PoolId;
351 Dmsg0(200, "Update pool record.\n");
352 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
353 ua->warning_msg("%s", db_strerror(ua->db));
356 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
362 * Turn auto mount on/off
367 int automount_cmd(UAContext *ua, const char *cmd)
372 if (!get_cmd(ua, _("Turn on or off? "))) {
380 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
388 static int cancel_cmd(UAContext *ua, const char *cmd)
393 char JobName[MAX_NAME_LENGTH];
395 for (i=1; i<ua->argc; i++) {
396 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
401 JobId = str_to_int64(ua->argv[i]);
402 if (!(jcr=get_jcr_by_id(JobId))) {
403 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
407 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
411 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
412 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
413 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
414 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
417 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
421 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
422 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
423 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
424 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
431 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
432 ua->error_msg(_("Unauthorized command from this console.\n"));
437 * If we still do not have a jcr,
438 * throw up a list and ask the user to select one.
441 int tjobs = 0; /* total # number jobs */
442 /* Count Jobs running */
444 if (jcr->JobId == 0) { /* this is us */
447 tjobs++; /* count of all jobs */
448 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
449 continue; /* skip not authorized */
451 njobs++; /* count of authorized jobs */
455 if (njobs == 0) { /* no authorized */
457 ua->send_msg(_("No Jobs running.\n"));
459 ua->send_msg(_("None of your jobs are running.\n"));
464 start_prompt(ua, _("Select Job:\n"));
467 if (jcr->JobId == 0) { /* this is us */
470 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
471 continue; /* skip not authorized */
473 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
478 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
482 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
486 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
487 jcr = get_jcr_by_full_name(JobName);
489 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
494 ret = cancel_job(ua, jcr);
500 * This is a common routine to create or update a
501 * Pool DB base record from a Pool Resource. We handle
502 * the setting of MaxVols and NumVols slightly differently
503 * depending on if we are creating the Pool or we are
504 * simply bringing it into agreement with the resource (updage).
506 * Caution : RecyclePoolId isn't setup in this function.
507 * You can use set_pooldbr_recyclepoolid();
510 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
512 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
513 if (op == POOL_OP_CREATE) {
514 pr->MaxVols = pool->max_volumes;
516 } else { /* update pool */
517 if (pr->MaxVols != pool->max_volumes) {
518 pr->MaxVols = pool->max_volumes;
520 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
521 pr->MaxVols = pr->NumVols;
524 pr->LabelType = pool->LabelType;
525 pr->UseOnce = pool->use_volume_once;
526 pr->UseCatalog = pool->use_catalog;
527 pr->Recycle = pool->Recycle;
528 pr->VolRetention = pool->VolRetention;
529 pr->VolUseDuration = pool->VolUseDuration;
530 pr->MaxVolJobs = pool->MaxVolJobs;
531 pr->MaxVolFiles = pool->MaxVolFiles;
532 pr->MaxVolBytes = pool->MaxVolBytes;
533 pr->AutoPrune = pool->AutoPrune;
534 pr->Recycle = pool->Recycle;
535 if (pool->label_format) {
536 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
538 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
542 /* set/update Pool.RecyclePoolId in Catalog */
543 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
547 if (!pool->RecyclePool) {
551 memset(&pr, 0, sizeof(POOL_DBR));
552 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
554 if (!db_get_pool_record(jcr, db, &pr)) {
555 return -1; /* not exists in database */
558 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
560 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
561 return -1; /* error */
564 if (!db_update_pool_record(jcr, db, &pr)) {
565 return -1; /* error */
570 /* set POOL_DBR.RecyclePoolId from Pool resource
571 * works with set_pooldbr_from_poolres
573 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
578 if (pool->RecyclePool) {
579 memset(&rpool, 0, sizeof(POOL_DBR));
581 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
582 if (db_get_pool_record(jcr, db, &rpool)) {
583 pr->RecyclePoolId = rpool.PoolId;
585 Jmsg(jcr, M_WARNING, 0,
586 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
587 "Try to update it with 'update pool=%s'\n"),
588 pool->name(), rpool.Name, rpool.Name,pool->name());
592 } else { /* no RecyclePool used, set it to 0 */
593 pr->RecyclePoolId = 0;
600 * Create a pool record from a given Pool resource
601 * Also called from backup.c
602 * Returns: -1 on error
603 * 0 record already exists
607 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
611 memset(&pr, 0, sizeof(POOL_DBR));
613 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
615 if (db_get_pool_record(jcr, db, &pr)) {
617 if (op == POOL_OP_UPDATE) { /* update request */
618 set_pooldbr_from_poolres(&pr, pool, op);
619 db_update_pool_record(jcr, db, &pr);
621 return 0; /* exists */
624 set_pooldbr_from_poolres(&pr, pool, op);
626 if (!db_create_pool_record(jcr, db, &pr)) {
627 return -1; /* error */
635 * Create a Pool Record in the database.
636 * It is always created from the Resource record.
638 static int create_cmd(UAContext *ua, const char *cmd)
642 if (!open_client_db(ua)) {
646 pool = get_pool_resource(ua);
651 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
653 ua->error_msg(_("Error: Pool %s already exists.\n"
654 "Use update to change it.\n"), pool->name());
658 ua->error_msg("%s", db_strerror(ua->db));
664 ua->send_msg(_("Pool %s created.\n"), pool->name());
669 extern DIRRES *director;
672 * Python control command
673 * python restart (restarts interpreter)
675 static int python_cmd(UAContext *ua, const char *cmd)
677 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
678 term_python_interpreter();
679 init_python_interpreter(director->name(),
680 director->scripts_directory, "DirStartUp");
681 ua->send_msg(_("Python interpreter restarted.\n"));
683 ua->warning_msg(_("Nothing done.\n"));
690 * Set a new address in a Client resource. We do this only
691 * if the Console name is the same as the Client name
692 * and the Console can access the client.
694 static int setip_cmd(UAContext *ua, const char *cmd)
698 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
699 ua->error_msg(_("Unauthorized command from this console.\n"));
703 client = GetClientResWithName(ua->cons->name());
706 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
709 if (client->address) {
710 free(client->address);
712 /* MA Bug 6 remove ifdef */
713 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
714 client->address = bstrdup(buf);
715 ua->send_msg(_("Client \"%s\" address set to %s\n"),
716 client->name(), client->address);
723 static void do_en_disable_cmd(UAContext *ua, bool setting)
728 i = find_arg_with_value(ua, NT_("job"));
730 job = select_job_resource(ua);
736 job = GetJobResWithName(ua->argv[i]);
740 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
744 if (!acl_access_ok(ua, Job_ACL, job->name())) {
745 ua->error_msg(_("Unauthorized command from this console.\n"));
748 job->enabled = setting;
749 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
753 static int enable_cmd(UAContext *ua, const char *cmd)
755 do_en_disable_cmd(ua, true);
759 static int disable_cmd(UAContext *ua, const char *cmd)
761 do_en_disable_cmd(ua, false);
766 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
772 lstore.store = store;
773 pm_strcpy(lstore.store_source, _("unknown source"));
774 set_wstorage(jcr, &lstore);
775 /* Try connecting for up to 15 seconds */
776 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
777 store->name(), store->address, store->SDport);
778 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
779 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
782 Dmsg0(120, _("Connected to storage daemon\n"));
783 sd = jcr->store_bsock;
784 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
785 if (bnet_recv(sd) >= 0) {
786 ua->send_msg("%s", sd->msg);
788 bnet_sig(sd, BNET_TERMINATE);
790 jcr->store_bsock = NULL;
794 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
798 /* Connect to File daemon */
800 ua->jcr->client = client;
801 /* Try to connect for 15 seconds */
802 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
803 client->name(), client->address, client->FDport);
804 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
805 ua->error_msg(_("Failed to connect to Client.\n"));
808 Dmsg0(120, "Connected to file daemon\n");
809 fd = ua->jcr->file_bsock;
810 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
811 if (bnet_recv(fd) >= 0) {
812 ua->send_msg("%s", fd->msg);
814 bnet_sig(fd, BNET_TERMINATE);
816 ua->jcr->file_bsock = NULL;
821 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
823 STORE *store, **unique_store;
824 CLIENT *client, **unique_client;
830 /* Count Storage items */
834 foreach_res(store, R_STORAGE) {
837 unique_store = (STORE **) malloc(i * sizeof(STORE));
838 /* Find Unique Storage address/port */
839 store = (STORE *)GetNextRes(R_STORAGE, NULL);
841 unique_store[i++] = store;
842 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
844 for (j=0; j<i; j++) {
845 if (strcmp(unique_store[j]->address, store->address) == 0 &&
846 unique_store[j]->SDport == store->SDport) {
852 unique_store[i++] = store;
853 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
858 /* Call each unique Storage daemon */
859 for (j=0; j<i; j++) {
860 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
864 /* Count Client items */
868 foreach_res(client, R_CLIENT) {
871 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
872 /* Find Unique Client address/port */
873 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
875 unique_client[i++] = client;
876 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
878 for (j=0; j<i; j++) {
879 if (strcmp(unique_client[j]->address, client->address) == 0 &&
880 unique_client[j]->FDport == client->FDport) {
886 unique_client[i++] = client;
887 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
892 /* Call each unique File daemon */
893 for (j=0; j<i; j++) {
894 do_client_setdebug(ua, unique_client[j], level, trace_flag);
900 * setdebug level=nn all trace=1/0
902 static int setdebug_cmd(UAContext *ua, const char *cmd)
910 if (!open_client_db(ua)) {
913 Dmsg1(120, "setdebug:%s:\n", cmd);
916 i = find_arg_with_value(ua, "level");
918 level = atoi(ua->argv[i]);
921 if (!get_pint(ua, _("Enter new debug level: "))) {
924 level = ua->pint32_val;
927 /* Look for trace flag. -1 => not change */
928 i = find_arg_with_value(ua, "trace");
930 trace_flag = atoi(ua->argv[i]);
931 if (trace_flag > 0) {
937 for (i=1; i<ua->argc; i++) {
938 if (strcasecmp(ua->argk[i], "all") == 0) {
939 do_all_setdebug(ua, level, trace_flag);
942 if (strcasecmp(ua->argk[i], "dir") == 0 ||
943 strcasecmp(ua->argk[i], "director") == 0) {
945 set_trace(trace_flag);
948 if (strcasecmp(ua->argk[i], "client") == 0 ||
949 strcasecmp(ua->argk[i], "fd") == 0) {
952 client = GetClientResWithName(ua->argv[i]);
954 do_client_setdebug(ua, client, level, trace_flag);
958 client = select_client_resource(ua);
960 do_client_setdebug(ua, client, level, trace_flag);
965 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
966 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
967 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
970 store = GetStoreResWithName(ua->argv[i]);
972 do_storage_setdebug(ua, store, level, trace_flag);
976 store = get_storage_resource(ua, false/*no default*/);
978 do_storage_setdebug(ua, store, level, trace_flag);
984 * We didn't find an appropriate keyword above, so
987 start_prompt(ua, _("Available daemons are: \n"));
988 add_prompt(ua, _("Director"));
989 add_prompt(ua, _("Storage"));
990 add_prompt(ua, _("Client"));
991 add_prompt(ua, _("All"));
992 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
993 case 0: /* Director */
995 set_trace(trace_flag);
998 store = get_storage_resource(ua, false/*no default*/);
1000 do_storage_setdebug(ua, store, level, trace_flag);
1004 client = select_client_resource(ua);
1006 do_client_setdebug(ua, client, level, trace_flag);
1010 do_all_setdebug(ua, level, trace_flag);
1019 * Turn debug tracing to file on/off
1021 static int trace_cmd(UAContext *ua, const char *cmd)
1025 if (ua->argc != 2) {
1026 if (!get_cmd(ua, _("Turn on or off? "))) {
1031 onoff = ua->argk[1];
1034 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1039 static int var_cmd(UAContext *ua, const char *cmd)
1041 POOLMEM *val = get_pool_memory(PM_FNAME);
1044 if (!open_client_db(ua)) {
1047 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1050 while (*var == ' ') { /* skip spaces */
1053 Dmsg1(100, "Var=%s:\n", var);
1054 variable_expansion(ua->jcr, var, &val);
1055 ua->send_msg("%s\n", val);
1056 free_pool_memory(val);
1060 static int estimate_cmd(UAContext *ua, const char *cmd)
1063 CLIENT *client = NULL;
1064 FILESET *fileset = NULL;
1066 char since[MAXSTRING];
1069 jcr->JobLevel = L_FULL;
1070 for (int i=1; i<ua->argc; i++) {
1071 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1072 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1074 client = GetClientResWithName(ua->argv[i]);
1078 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1080 job = GetJobResWithName(ua->argv[i]);
1081 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1082 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1088 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1090 fileset = GetFileSetResWithName(ua->argv[i]);
1091 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1092 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1098 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1102 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1103 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1104 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1109 if (!job && !(client && fileset)) {
1110 if (!(job = select_job_resource(ua))) {
1115 job = GetJobResWithName(ua->argk[1]);
1117 ua->error_msg(_("No job specified.\n"));
1120 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1121 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1126 client = job->client;
1129 fileset = job->fileset;
1131 jcr->client = client;
1132 jcr->fileset = fileset;
1134 ua->catalog = client->catalog;
1141 jcr->JobType = JT_BACKUP;
1142 init_jcr_job_record(jcr);
1144 if (!get_or_create_client_record(jcr)) {
1147 if (!get_or_create_fileset_record(jcr)) {
1151 get_level_since_time(ua->jcr, since, sizeof(since));
1153 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1154 job->client->name(), job->client->address, job->client->FDport);
1155 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1156 ua->error_msg(_("Failed to connect to Client.\n"));
1160 if (!send_include_list(jcr)) {
1161 ua->error_msg(_("Error sending include list.\n"));
1165 if (!send_exclude_list(jcr)) {
1166 ua->error_msg(_("Error sending exclude list.\n"));
1170 if (!send_level_command(jcr)) {
1174 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1175 while (bnet_recv(jcr->file_bsock) >= 0) {
1176 ua->send_msg("%s", jcr->file_bsock->msg);
1180 if (jcr->file_bsock) {
1181 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1182 bnet_close(jcr->file_bsock);
1183 jcr->file_bsock = NULL;
1192 static int time_cmd(UAContext *ua, const char *cmd)
1195 time_t ttime = time(NULL);
1197 (void)localtime_r(&ttime, &tm);
1198 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1199 ua->send_msg("%s\n", sdt);
1204 * reload the conf file
1206 extern "C" void reload_config(int sig);
1208 static int reload_cmd(UAContext *ua, const char *cmd)
1215 * Delete Pool records (should purge Media with it).
1217 * delete pool=<pool-name>
1218 * delete volume pool=<pool-name> volume=<name>
1221 static int delete_cmd(UAContext *ua, const char *cmd)
1223 static const char *keywords[] = {
1229 if (!open_client_db(ua)) {
1233 switch (find_arg_keyword(ua, keywords)) {
1242 while ((i=find_arg(ua, "jobid")) > 0) {
1244 *ua->argk[i] = 0; /* zap keyword already visited */
1252 "In general it is not a good idea to delete either a\n"
1253 "Pool or a Volume since they may contain data.\n\n"));
1255 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1266 ua->warning_msg(_("Nothing done.\n"));
1274 * delete_job has been modified to parse JobID lists like the
1276 * delete JobID=3,4,6,7-11,14
1278 * Thanks to Phil Stracchino for the above addition.
1281 static void delete_job(UAContext *ua)
1286 int i = find_arg_with_value(ua, NT_("jobid"));
1288 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1289 s = bstrdup(ua->argv[i]);
1292 * We could use strtok() here. But we're not going to, because:
1293 * (a) strtok() is deprecated, having been replaced by strsep();
1294 * (b) strtok() is broken in significant ways.
1295 * we could use strsep() instead, but it's not universally available.
1296 * so we grow our own using strchr().
1298 sep = strchr(tok, ',');
1299 while (sep != NULL) {
1301 if (strchr(tok, '-')) {
1302 delete_job_id_range(ua, tok);
1304 JobId = str_to_int64(tok);
1305 do_job_delete(ua, JobId);
1308 sep = strchr(tok, ',');
1310 /* pick up the last token */
1311 if (strchr(tok, '-')) {
1312 delete_job_id_range(ua, tok);
1314 JobId = str_to_int64(tok);
1315 do_job_delete(ua, JobId);
1320 JobId = str_to_int64(ua->argv[i]);
1321 do_job_delete(ua, JobId);
1323 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1326 JobId = ua->int64_val;
1327 do_job_delete(ua, JobId);
1332 * we call delete_job_id_range to parse range tokens and iterate over ranges
1334 static void delete_job_id_range(UAContext *ua, char *tok)
1339 tok2 = strchr(tok, '-');
1342 j1 = str_to_int64(tok);
1343 j2 = str_to_int64(tok2);
1344 for (j=j1; j<=j2; j++) {
1345 do_job_delete(ua, j);
1350 * do_job_delete now performs the actual delete operation atomically
1352 static void do_job_delete(UAContext *ua, JobId_t JobId)
1356 edit_int64(JobId, ed1);
1357 purge_jobs_from_catalog(ua, ed1);
1358 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1362 * Delete media records from database -- dangerous
1364 static int delete_volume(UAContext *ua)
1368 if (!select_media_dbr(ua, &mr)) {
1371 ua->warning_msg(_("\nThis command will delete volume %s\n"
1372 "and all Jobs saved on that volume from the Catalog\n"),
1375 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1378 if (ua->pint32_val) {
1379 db_delete_media_record(ua->jcr, ua->db, &mr);
1385 * Delete a pool record from the database -- dangerous
1387 static int delete_pool(UAContext *ua)
1391 memset(&pr, 0, sizeof(pr));
1393 if (!get_pool_dbr(ua, &pr)) {
1396 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1399 if (ua->pint32_val) {
1400 db_delete_pool_record(ua->jcr, ua->db, &pr);
1405 int memory_cmd(UAContext *ua, const char *cmd)
1407 list_dir_status_header(ua);
1408 sm_dump(false, true);
1412 static void do_mount_cmd(UAContext *ua, const char *command)
1417 char dev_name[MAX_NAME_LENGTH];
1421 if (!open_client_db(ua)) {
1424 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1426 store.store = get_storage_resource(ua, true/*arg is storage*/);
1430 pm_strcpy(store.store_source, _("unknown source"));
1431 set_wstorage(jcr, &store);
1432 drive = get_storage_drive(ua, store.store);
1433 if (strcmp(command, "mount") == 0) {
1434 slot = get_storage_slot(ua, store.store);
1437 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1438 store.store->media_type, store.store->dev_name(), drive);
1440 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1441 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1444 sd = jcr->store_bsock;
1445 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1446 bash_spaces(dev_name);
1448 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1450 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1452 while (bnet_recv(sd) >= 0) {
1453 ua->send_msg("%s", sd->msg);
1455 bnet_sig(sd, BNET_TERMINATE);
1457 jcr->store_bsock = NULL;
1461 * mount [storage=<name>] [drive=nn] [slot=mm]
1463 static int mount_cmd(UAContext *ua, const char *cmd)
1465 do_mount_cmd(ua, "mount"); /* mount */
1471 * unmount [storage=<name>] [drive=nn]
1473 static int unmount_cmd(UAContext *ua, const char *cmd)
1475 do_mount_cmd(ua, "unmount"); /* unmount */
1481 * release [storage=<name>] [drive=nn]
1483 static int release_cmd(UAContext *ua, const char *cmd)
1485 do_mount_cmd(ua, "release"); /* release */
1492 * use catalog=<name>
1494 static int use_cmd(UAContext *ua, const char *cmd)
1496 CAT *oldcatalog, *catalog;
1499 close_db(ua); /* close any previously open db */
1500 oldcatalog = ua->catalog;
1502 if (!(catalog = get_catalog_resource(ua))) {
1503 ua->catalog = oldcatalog;
1505 ua->catalog = catalog;
1508 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1509 ua->catalog->name(), ua->catalog->db_name);
1514 int quit_cmd(UAContext *ua, const char *cmd)
1520 /* Handler to get job status */
1521 static int status_handler(void *ctx, int num_fields, char **row)
1523 char *val = (char *)ctx;
1528 *val = '?'; /* Unknown by default */
1535 * Wait until no job is running
1537 int wait_cmd(UAContext *ua, const char *cmd)
1542 * Wait until no job is running
1544 if (ua->argc == 1) {
1545 bmicrosleep(0, 200000); /* let job actually start */
1546 for (bool running=true; running; ) {
1549 if (jcr->JobId != 0) {
1563 /* we have jobid, jobname or ujobid argument */
1565 uint32_t jobid = 0 ;
1567 if (!open_client_db(ua)) {
1568 ua->error_msg(_("ERR: Can't open db\n")) ;
1572 for (int i=1; i<ua->argc; i++) {
1573 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1577 jobid = str_to_int64(ua->argv[i]);
1579 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1580 strcasecmp(ua->argk[i], "job") == 0) {
1584 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1586 jobid = jcr->JobId ;
1590 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1594 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1596 jobid = jcr->JobId ;
1604 ua->error_msg(_("ERR: Job was not found\n"));
1609 * We wait the end of job
1612 bmicrosleep(0, 200000); /* let job actually start */
1613 for (bool running=true; running; ) {
1616 jcr=get_jcr_by_id(jobid) ;
1629 * We have to get JobStatus
1633 char jobstatus = '?'; /* Unknown by default */
1636 bsnprintf(buf, sizeof(buf),
1637 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1640 db_sql_query(ua->db, buf,
1641 status_handler, (void *)&jobstatus);
1643 switch (jobstatus) {
1645 status = 1 ; /* Warning */
1649 case JS_ErrorTerminated:
1651 status = 2 ; /* Critical */
1655 status = 0 ; /* Ok */
1659 status = 3 ; /* Unknown */
1663 ua->send_msg("JobId=%i\n", jobid) ;
1664 ua->send_msg("JobStatus=%s (%c)\n",
1665 job_status_to_str(jobstatus),
1668 if (ua->gui || ua->api) {
1669 ua->send_msg("ExitStatus=%i\n", status) ;
1676 static int help_cmd(UAContext *ua, const char *cmd)
1680 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1681 for (i=0; i<comsize; i++) {
1682 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1684 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1688 int qhelp_cmd(UAContext *ua, const char *cmd)
1692 for (i=0; i<comsize; i++) {
1693 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1699 static int version_cmd(UAContext *ua, const char *cmd)
1701 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1702 HOST_OS, DISTNAME, DISTVER);
1707 * Test code -- turned on only for debug testing
1709 static int version_cmd(UAContext *ua, const char *cmd)
1712 POOL_MEM query(PM_MESSAGE);
1714 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1715 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1716 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1717 for (int i=0; i < ids.num_ids; i++) {
1718 ua->send_msg("id=%d\n", ids.DBId[i]);
1726 * This call explicitly checks for a catalog=xxx and
1727 * if given, opens that catalog. It also checks for
1728 * client=xxx and if found, opens the catalog
1729 * corresponding to that client. If we still don't
1730 * have a catalog, look for a Job keyword and get the
1731 * catalog from its client record.
1733 bool open_client_db(UAContext *ua)
1740 /* Try for catalog keyword */
1741 i = find_arg_with_value(ua, NT_("catalog"));
1743 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1744 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1747 catalog = GetCatalogResWithName(ua->argv[i]);
1749 if (ua->catalog && ua->catalog != catalog) {
1752 ua->catalog = catalog;
1757 /* Try for client keyword */
1758 i = find_arg_with_value(ua, NT_("client"));
1760 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1761 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1764 client = GetClientResWithName(ua->argv[i]);
1766 catalog = client->catalog;
1767 if (ua->catalog && ua->catalog != catalog) {
1770 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1771 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1774 ua->catalog = catalog;
1779 /* Try for Job keyword */
1780 i = find_arg_with_value(ua, NT_("job"));
1782 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1783 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1786 job = GetJobResWithName(ua->argv[i]);
1788 catalog = job->client->catalog;
1789 if (ua->catalog && ua->catalog != catalog) {
1792 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1793 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1796 ua->catalog = catalog;
1806 * Open the catalog database.
1808 bool open_db(UAContext *ua)
1814 ua->catalog = get_catalog_resource(ua);
1816 ua->error_msg( _("Could not find a Catalog resource\n"));
1821 ua->jcr->catalog = ua->catalog;
1823 Dmsg0(100, "UA Open database\n");
1824 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1825 ua->catalog->db_password, ua->catalog->db_address,
1826 ua->catalog->db_port, ua->catalog->db_socket,
1827 ua->catalog->mult_db_connections);
1828 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1829 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1830 ua->catalog->db_name);
1832 ua->error_msg("%s", db_strerror(ua->db));
1837 ua->jcr->db = ua->db;
1839 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1841 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1845 void close_db(UAContext *ua)
1848 db_close_database(ua->jcr, ua->db);