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_("messages"), messagescmd, _("messages")},
120 { NT_("memory"), memory_cmd, _("print current memory usage")},
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) {
481 if (ua->api && njobs == 1) {
483 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
484 _("Confirm cancel?"));
485 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
490 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
495 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
496 jcr = get_jcr_by_full_name(JobName);
498 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
503 ret = cancel_job(ua, jcr);
509 * This is a common routine to create or update a
510 * Pool DB base record from a Pool Resource. We handle
511 * the setting of MaxVols and NumVols slightly differently
512 * depending on if we are creating the Pool or we are
513 * simply bringing it into agreement with the resource (updage).
515 * Caution : RecyclePoolId isn't setup in this function.
516 * You can use set_pooldbr_recyclepoolid();
519 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
521 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
522 if (op == POOL_OP_CREATE) {
523 pr->MaxVols = pool->max_volumes;
525 } else { /* update pool */
526 if (pr->MaxVols != pool->max_volumes) {
527 pr->MaxVols = pool->max_volumes;
529 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
530 pr->MaxVols = pr->NumVols;
533 pr->LabelType = pool->LabelType;
534 pr->UseOnce = pool->use_volume_once;
535 pr->UseCatalog = pool->use_catalog;
536 pr->Recycle = pool->Recycle;
537 pr->VolRetention = pool->VolRetention;
538 pr->VolUseDuration = pool->VolUseDuration;
539 pr->MaxVolJobs = pool->MaxVolJobs;
540 pr->MaxVolFiles = pool->MaxVolFiles;
541 pr->MaxVolBytes = pool->MaxVolBytes;
542 pr->AutoPrune = pool->AutoPrune;
543 pr->Recycle = pool->Recycle;
544 if (pool->label_format) {
545 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
547 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
551 /* set/update Pool.RecyclePoolId in Catalog */
552 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
556 if (!pool->RecyclePool) {
560 memset(&pr, 0, sizeof(POOL_DBR));
561 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
563 if (!db_get_pool_record(jcr, db, &pr)) {
564 return -1; /* not exists in database */
567 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
569 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
570 return -1; /* error */
573 if (!db_update_pool_record(jcr, db, &pr)) {
574 return -1; /* error */
579 /* set POOL_DBR.RecyclePoolId from Pool resource
580 * works with set_pooldbr_from_poolres
582 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
587 if (pool->RecyclePool) {
588 memset(&rpool, 0, sizeof(POOL_DBR));
590 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
591 if (db_get_pool_record(jcr, db, &rpool)) {
592 pr->RecyclePoolId = rpool.PoolId;
594 Jmsg(jcr, M_WARNING, 0,
595 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
596 "Try to update it with 'update pool=%s'\n"),
597 pool->name(), rpool.Name, rpool.Name,pool->name());
601 } else { /* no RecyclePool used, set it to 0 */
602 pr->RecyclePoolId = 0;
609 * Create a pool record from a given Pool resource
610 * Also called from backup.c
611 * Returns: -1 on error
612 * 0 record already exists
616 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
620 memset(&pr, 0, sizeof(POOL_DBR));
622 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
624 if (db_get_pool_record(jcr, db, &pr)) {
626 if (op == POOL_OP_UPDATE) { /* update request */
627 set_pooldbr_from_poolres(&pr, pool, op);
628 db_update_pool_record(jcr, db, &pr);
630 return 0; /* exists */
633 set_pooldbr_from_poolres(&pr, pool, op);
635 if (!db_create_pool_record(jcr, db, &pr)) {
636 return -1; /* error */
644 * Create a Pool Record in the database.
645 * It is always created from the Resource record.
647 static int create_cmd(UAContext *ua, const char *cmd)
651 if (!open_client_db(ua)) {
655 pool = get_pool_resource(ua);
660 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
662 ua->error_msg(_("Error: Pool %s already exists.\n"
663 "Use update to change it.\n"), pool->name());
667 ua->error_msg("%s", db_strerror(ua->db));
673 ua->send_msg(_("Pool %s created.\n"), pool->name());
678 extern DIRRES *director;
681 * Python control command
682 * python restart (restarts interpreter)
684 static int python_cmd(UAContext *ua, const char *cmd)
686 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
687 term_python_interpreter();
688 init_python_interpreter(director->name(),
689 director->scripts_directory, "DirStartUp");
690 ua->send_msg(_("Python interpreter restarted.\n"));
692 ua->warning_msg(_("Nothing done.\n"));
699 * Set a new address in a Client resource. We do this only
700 * if the Console name is the same as the Client name
701 * and the Console can access the client.
703 static int setip_cmd(UAContext *ua, const char *cmd)
707 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
708 ua->error_msg(_("Unauthorized command from this console.\n"));
712 client = GetClientResWithName(ua->cons->name());
715 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
718 if (client->address) {
719 free(client->address);
721 /* MA Bug 6 remove ifdef */
722 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
723 client->address = bstrdup(buf);
724 ua->send_msg(_("Client \"%s\" address set to %s\n"),
725 client->name(), client->address);
732 static void do_en_disable_cmd(UAContext *ua, bool setting)
737 i = find_arg_with_value(ua, NT_("job"));
739 job = select_job_resource(ua);
745 job = GetJobResWithName(ua->argv[i]);
749 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
753 if (!acl_access_ok(ua, Job_ACL, job->name())) {
754 ua->error_msg(_("Unauthorized command from this console.\n"));
757 job->enabled = setting;
758 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
762 static int enable_cmd(UAContext *ua, const char *cmd)
764 do_en_disable_cmd(ua, true);
768 static int disable_cmd(UAContext *ua, const char *cmd)
770 do_en_disable_cmd(ua, false);
775 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
781 lstore.store = store;
782 pm_strcpy(lstore.store_source, _("unknown source"));
783 set_wstorage(jcr, &lstore);
784 /* Try connecting for up to 15 seconds */
785 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
786 store->name(), store->address, store->SDport);
787 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
788 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
791 Dmsg0(120, _("Connected to storage daemon\n"));
792 sd = jcr->store_bsock;
793 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
794 if (bnet_recv(sd) >= 0) {
795 ua->send_msg("%s", sd->msg);
797 bnet_sig(sd, BNET_TERMINATE);
799 jcr->store_bsock = NULL;
803 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
807 /* Connect to File daemon */
809 ua->jcr->client = client;
810 /* Try to connect for 15 seconds */
811 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
812 client->name(), client->address, client->FDport);
813 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
814 ua->error_msg(_("Failed to connect to Client.\n"));
817 Dmsg0(120, "Connected to file daemon\n");
818 fd = ua->jcr->file_bsock;
819 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
820 if (bnet_recv(fd) >= 0) {
821 ua->send_msg("%s", fd->msg);
823 bnet_sig(fd, BNET_TERMINATE);
825 ua->jcr->file_bsock = NULL;
830 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
832 STORE *store, **unique_store;
833 CLIENT *client, **unique_client;
839 /* Count Storage items */
843 foreach_res(store, R_STORAGE) {
846 unique_store = (STORE **) malloc(i * sizeof(STORE));
847 /* Find Unique Storage address/port */
848 store = (STORE *)GetNextRes(R_STORAGE, NULL);
850 unique_store[i++] = store;
851 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
853 for (j=0; j<i; j++) {
854 if (strcmp(unique_store[j]->address, store->address) == 0 &&
855 unique_store[j]->SDport == store->SDport) {
861 unique_store[i++] = store;
862 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
867 /* Call each unique Storage daemon */
868 for (j=0; j<i; j++) {
869 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
873 /* Count Client items */
877 foreach_res(client, R_CLIENT) {
880 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
881 /* Find Unique Client address/port */
882 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
884 unique_client[i++] = client;
885 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
887 for (j=0; j<i; j++) {
888 if (strcmp(unique_client[j]->address, client->address) == 0 &&
889 unique_client[j]->FDport == client->FDport) {
895 unique_client[i++] = client;
896 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
901 /* Call each unique File daemon */
902 for (j=0; j<i; j++) {
903 do_client_setdebug(ua, unique_client[j], level, trace_flag);
909 * setdebug level=nn all trace=1/0
911 static int setdebug_cmd(UAContext *ua, const char *cmd)
919 if (!open_client_db(ua)) {
922 Dmsg1(120, "setdebug:%s:\n", cmd);
925 i = find_arg_with_value(ua, "level");
927 level = atoi(ua->argv[i]);
930 if (!get_pint(ua, _("Enter new debug level: "))) {
933 level = ua->pint32_val;
936 /* Look for trace flag. -1 => not change */
937 i = find_arg_with_value(ua, "trace");
939 trace_flag = atoi(ua->argv[i]);
940 if (trace_flag > 0) {
946 for (i=1; i<ua->argc; i++) {
947 if (strcasecmp(ua->argk[i], "all") == 0) {
948 do_all_setdebug(ua, level, trace_flag);
951 if (strcasecmp(ua->argk[i], "dir") == 0 ||
952 strcasecmp(ua->argk[i], "director") == 0) {
954 set_trace(trace_flag);
957 if (strcasecmp(ua->argk[i], "client") == 0 ||
958 strcasecmp(ua->argk[i], "fd") == 0) {
961 client = GetClientResWithName(ua->argv[i]);
963 do_client_setdebug(ua, client, level, trace_flag);
967 client = select_client_resource(ua);
969 do_client_setdebug(ua, client, level, trace_flag);
974 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
975 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
976 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
979 store = GetStoreResWithName(ua->argv[i]);
981 do_storage_setdebug(ua, store, level, trace_flag);
985 store = get_storage_resource(ua, false/*no default*/);
987 do_storage_setdebug(ua, store, level, trace_flag);
993 * We didn't find an appropriate keyword above, so
996 start_prompt(ua, _("Available daemons are: \n"));
997 add_prompt(ua, _("Director"));
998 add_prompt(ua, _("Storage"));
999 add_prompt(ua, _("Client"));
1000 add_prompt(ua, _("All"));
1001 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1002 case 0: /* Director */
1003 debug_level = level;
1004 set_trace(trace_flag);
1007 store = get_storage_resource(ua, false/*no default*/);
1009 do_storage_setdebug(ua, store, level, trace_flag);
1013 client = select_client_resource(ua);
1015 do_client_setdebug(ua, client, level, trace_flag);
1019 do_all_setdebug(ua, level, trace_flag);
1028 * Turn debug tracing to file on/off
1030 static int trace_cmd(UAContext *ua, const char *cmd)
1034 if (ua->argc != 2) {
1035 if (!get_cmd(ua, _("Turn on or off? "))) {
1040 onoff = ua->argk[1];
1043 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1048 static int var_cmd(UAContext *ua, const char *cmd)
1050 POOLMEM *val = get_pool_memory(PM_FNAME);
1053 if (!open_client_db(ua)) {
1056 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1059 while (*var == ' ') { /* skip spaces */
1062 Dmsg1(100, "Var=%s:\n", var);
1063 variable_expansion(ua->jcr, var, &val);
1064 ua->send_msg("%s\n", val);
1065 free_pool_memory(val);
1069 static int estimate_cmd(UAContext *ua, const char *cmd)
1072 CLIENT *client = NULL;
1073 FILESET *fileset = NULL;
1075 char since[MAXSTRING];
1078 jcr->JobLevel = L_FULL;
1079 for (int i=1; i<ua->argc; i++) {
1080 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1081 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1083 client = GetClientResWithName(ua->argv[i]);
1087 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1089 job = GetJobResWithName(ua->argv[i]);
1090 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1091 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1097 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1099 fileset = GetFileSetResWithName(ua->argv[i]);
1100 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1101 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1107 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1111 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1112 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1113 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1118 if (!job && !(client && fileset)) {
1119 if (!(job = select_job_resource(ua))) {
1124 job = GetJobResWithName(ua->argk[1]);
1126 ua->error_msg(_("No job specified.\n"));
1129 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1130 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1135 client = job->client;
1138 fileset = job->fileset;
1140 jcr->client = client;
1141 jcr->fileset = fileset;
1143 ua->catalog = client->catalog;
1150 jcr->JobType = JT_BACKUP;
1151 init_jcr_job_record(jcr);
1153 if (!get_or_create_client_record(jcr)) {
1156 if (!get_or_create_fileset_record(jcr)) {
1160 get_level_since_time(ua->jcr, since, sizeof(since));
1162 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1163 job->client->name(), job->client->address, job->client->FDport);
1164 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1165 ua->error_msg(_("Failed to connect to Client.\n"));
1169 if (!send_include_list(jcr)) {
1170 ua->error_msg(_("Error sending include list.\n"));
1174 if (!send_exclude_list(jcr)) {
1175 ua->error_msg(_("Error sending exclude list.\n"));
1179 if (!send_level_command(jcr)) {
1183 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1184 while (bnet_recv(jcr->file_bsock) >= 0) {
1185 ua->send_msg("%s", jcr->file_bsock->msg);
1189 if (jcr->file_bsock) {
1190 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1191 bnet_close(jcr->file_bsock);
1192 jcr->file_bsock = NULL;
1201 static int time_cmd(UAContext *ua, const char *cmd)
1204 time_t ttime = time(NULL);
1206 (void)localtime_r(&ttime, &tm);
1207 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1208 ua->send_msg("%s\n", sdt);
1213 * reload the conf file
1215 extern "C" void reload_config(int sig);
1217 static int reload_cmd(UAContext *ua, const char *cmd)
1224 * Delete Pool records (should purge Media with it).
1226 * delete pool=<pool-name>
1227 * delete volume pool=<pool-name> volume=<name>
1230 static int delete_cmd(UAContext *ua, const char *cmd)
1232 static const char *keywords[] = {
1238 if (!open_client_db(ua)) {
1242 switch (find_arg_keyword(ua, keywords)) {
1251 while ((i=find_arg(ua, "jobid")) > 0) {
1253 *ua->argk[i] = 0; /* zap keyword already visited */
1261 "In general it is not a good idea to delete either a\n"
1262 "Pool or a Volume since they may contain data.\n\n"));
1264 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1275 ua->warning_msg(_("Nothing done.\n"));
1283 * delete_job has been modified to parse JobID lists like the
1285 * delete JobID=3,4,6,7-11,14
1287 * Thanks to Phil Stracchino for the above addition.
1290 static void delete_job(UAContext *ua)
1295 int i = find_arg_with_value(ua, NT_("jobid"));
1297 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1298 s = bstrdup(ua->argv[i]);
1301 * We could use strtok() here. But we're not going to, because:
1302 * (a) strtok() is deprecated, having been replaced by strsep();
1303 * (b) strtok() is broken in significant ways.
1304 * we could use strsep() instead, but it's not universally available.
1305 * so we grow our own using strchr().
1307 sep = strchr(tok, ',');
1308 while (sep != NULL) {
1310 if (strchr(tok, '-')) {
1311 delete_job_id_range(ua, tok);
1313 JobId = str_to_int64(tok);
1314 do_job_delete(ua, JobId);
1317 sep = strchr(tok, ',');
1319 /* pick up the last token */
1320 if (strchr(tok, '-')) {
1321 delete_job_id_range(ua, tok);
1323 JobId = str_to_int64(tok);
1324 do_job_delete(ua, JobId);
1329 JobId = str_to_int64(ua->argv[i]);
1330 do_job_delete(ua, JobId);
1332 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1335 JobId = ua->int64_val;
1336 do_job_delete(ua, JobId);
1341 * we call delete_job_id_range to parse range tokens and iterate over ranges
1343 static void delete_job_id_range(UAContext *ua, char *tok)
1348 tok2 = strchr(tok, '-');
1351 j1 = str_to_int64(tok);
1352 j2 = str_to_int64(tok2);
1353 for (j=j1; j<=j2; j++) {
1354 do_job_delete(ua, j);
1359 * do_job_delete now performs the actual delete operation atomically
1361 static void do_job_delete(UAContext *ua, JobId_t JobId)
1365 edit_int64(JobId, ed1);
1366 purge_jobs_from_catalog(ua, ed1);
1367 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1371 * Delete media records from database -- dangerous
1373 static int delete_volume(UAContext *ua)
1378 if (!select_media_dbr(ua, &mr)) {
1381 ua->warning_msg(_("\nThis command will delete volume %s\n"
1382 "and all Jobs saved on that volume from the Catalog\n"),
1385 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1387 if (!get_yesno(ua, buf)) {
1390 if (ua->pint32_val) {
1391 db_delete_media_record(ua->jcr, ua->db, &mr);
1397 * Delete a pool record from the database -- dangerous
1399 static int delete_pool(UAContext *ua)
1404 memset(&pr, 0, sizeof(pr));
1406 if (!get_pool_dbr(ua, &pr)) {
1409 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1411 if (!get_yesno(ua, buf)) {
1414 if (ua->pint32_val) {
1415 db_delete_pool_record(ua->jcr, ua->db, &pr);
1420 int memory_cmd(UAContext *ua, const char *cmd)
1422 list_dir_status_header(ua);
1423 sm_dump(false, true);
1427 static void do_mount_cmd(UAContext *ua, const char *command)
1432 char dev_name[MAX_NAME_LENGTH];
1436 if (!open_client_db(ua)) {
1439 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1441 store.store = get_storage_resource(ua, true/*arg is storage*/);
1445 pm_strcpy(store.store_source, _("unknown source"));
1446 set_wstorage(jcr, &store);
1447 drive = get_storage_drive(ua, store.store);
1448 if (strcmp(command, "mount") == 0) {
1449 slot = get_storage_slot(ua, store.store);
1452 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1453 store.store->media_type, store.store->dev_name(), drive);
1455 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1456 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1459 sd = jcr->store_bsock;
1460 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1461 bash_spaces(dev_name);
1463 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1465 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1467 while (bnet_recv(sd) >= 0) {
1468 ua->send_msg("%s", sd->msg);
1470 bnet_sig(sd, BNET_TERMINATE);
1472 jcr->store_bsock = NULL;
1476 * mount [storage=<name>] [drive=nn] [slot=mm]
1478 static int mount_cmd(UAContext *ua, const char *cmd)
1480 do_mount_cmd(ua, "mount"); /* mount */
1486 * unmount [storage=<name>] [drive=nn]
1488 static int unmount_cmd(UAContext *ua, const char *cmd)
1490 do_mount_cmd(ua, "unmount"); /* unmount */
1496 * release [storage=<name>] [drive=nn]
1498 static int release_cmd(UAContext *ua, const char *cmd)
1500 do_mount_cmd(ua, "release"); /* release */
1507 * use catalog=<name>
1509 static int use_cmd(UAContext *ua, const char *cmd)
1511 CAT *oldcatalog, *catalog;
1514 close_db(ua); /* close any previously open db */
1515 oldcatalog = ua->catalog;
1517 if (!(catalog = get_catalog_resource(ua))) {
1518 ua->catalog = oldcatalog;
1520 ua->catalog = catalog;
1523 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1524 ua->catalog->name(), ua->catalog->db_name);
1529 int quit_cmd(UAContext *ua, const char *cmd)
1535 /* Handler to get job status */
1536 static int status_handler(void *ctx, int num_fields, char **row)
1538 char *val = (char *)ctx;
1543 *val = '?'; /* Unknown by default */
1550 * Wait until no job is running
1552 int wait_cmd(UAContext *ua, const char *cmd)
1557 * Wait until no job is running
1559 if (ua->argc == 1) {
1560 bmicrosleep(0, 200000); /* let job actually start */
1561 for (bool running=true; running; ) {
1564 if (jcr->JobId != 0) {
1578 /* we have jobid, jobname or ujobid argument */
1580 uint32_t jobid = 0 ;
1582 if (!open_client_db(ua)) {
1583 ua->error_msg(_("ERR: Can't open db\n")) ;
1587 for (int i=1; i<ua->argc; i++) {
1588 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1592 jobid = str_to_int64(ua->argv[i]);
1594 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1595 strcasecmp(ua->argk[i], "job") == 0) {
1599 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1601 jobid = jcr->JobId ;
1605 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1609 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1611 jobid = jcr->JobId ;
1619 ua->error_msg(_("ERR: Job was not found\n"));
1624 * We wait the end of job
1627 bmicrosleep(0, 200000); /* let job actually start */
1628 for (bool running=true; running; ) {
1631 jcr=get_jcr_by_id(jobid) ;
1644 * We have to get JobStatus
1648 char jobstatus = '?'; /* Unknown by default */
1651 bsnprintf(buf, sizeof(buf),
1652 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1655 db_sql_query(ua->db, buf,
1656 status_handler, (void *)&jobstatus);
1658 switch (jobstatus) {
1660 status = 1 ; /* Warning */
1664 case JS_ErrorTerminated:
1666 status = 2 ; /* Critical */
1670 status = 0 ; /* Ok */
1674 status = 3 ; /* Unknown */
1678 ua->send_msg("JobId=%i\n", jobid) ;
1679 ua->send_msg("JobStatus=%s (%c)\n",
1680 job_status_to_str(jobstatus),
1683 if (ua->gui || ua->api) {
1684 ua->send_msg("ExitStatus=%i\n", status) ;
1691 static int help_cmd(UAContext *ua, const char *cmd)
1695 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1696 for (i=0; i<comsize; i++) {
1697 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1699 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1703 int qhelp_cmd(UAContext *ua, const char *cmd)
1707 for (i=0; i<comsize; i++) {
1708 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1714 static int version_cmd(UAContext *ua, const char *cmd)
1716 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1717 HOST_OS, DISTNAME, DISTVER);
1722 * Test code -- turned on only for debug testing
1724 static int version_cmd(UAContext *ua, const char *cmd)
1727 POOL_MEM query(PM_MESSAGE);
1729 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1730 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1731 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1732 for (int i=0; i < ids.num_ids; i++) {
1733 ua->send_msg("id=%d\n", ids.DBId[i]);
1741 * This call explicitly checks for a catalog=xxx and
1742 * if given, opens that catalog. It also checks for
1743 * client=xxx and if found, opens the catalog
1744 * corresponding to that client. If we still don't
1745 * have a catalog, look for a Job keyword and get the
1746 * catalog from its client record.
1748 bool open_client_db(UAContext *ua)
1755 /* Try for catalog keyword */
1756 i = find_arg_with_value(ua, NT_("catalog"));
1758 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1759 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1762 catalog = GetCatalogResWithName(ua->argv[i]);
1764 if (ua->catalog && ua->catalog != catalog) {
1767 ua->catalog = catalog;
1772 /* Try for client keyword */
1773 i = find_arg_with_value(ua, NT_("client"));
1775 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1776 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1779 client = GetClientResWithName(ua->argv[i]);
1781 catalog = client->catalog;
1782 if (ua->catalog && ua->catalog != catalog) {
1785 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1786 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1789 ua->catalog = catalog;
1794 /* Try for Job keyword */
1795 i = find_arg_with_value(ua, NT_("job"));
1797 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1798 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1801 job = GetJobResWithName(ua->argv[i]);
1803 catalog = job->client->catalog;
1804 if (ua->catalog && ua->catalog != catalog) {
1807 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1808 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1811 ua->catalog = catalog;
1821 * Open the catalog database.
1823 bool open_db(UAContext *ua)
1829 ua->catalog = get_catalog_resource(ua);
1831 ua->error_msg( _("Could not find a Catalog resource\n"));
1836 ua->jcr->catalog = ua->catalog;
1838 Dmsg0(100, "UA Open database\n");
1839 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1840 ua->catalog->db_password, ua->catalog->db_address,
1841 ua->catalog->db_port, ua->catalog->db_socket,
1842 ua->catalog->mult_db_connections);
1843 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1844 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1845 ua->catalog->db_name);
1847 ua->error_msg("%s", db_strerror(ua->db));
1852 ua->jcr->db = ua->db;
1854 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1856 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1860 void close_db(UAContext *ua)
1863 db_close_database(ua->jcr, ua->db);