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 bool do_a_command(UAContext *ua)
158 BSOCK *user = ua->UA_sock;
161 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
166 while (ua->jcr->wstorage->size()) {
167 ua->jcr->wstorage->remove(0);
170 len = strlen(ua->argk[0]);
171 for (i=0; i<comsize; i++) { /* search for command */
172 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
173 /* Check if command permitted, but "quit" is always OK */
174 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
175 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
178 if (ua->api) user->signal(BNET_CMD_BEGIN);
179 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
185 ua->error_msg(_("%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);
247 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
250 pr.MaxVols = ua->pint32_val;
254 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
255 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
256 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
260 if (pr.MaxVols == 0) {
263 max = pr.MaxVols - pr.NumVols;
267 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
268 if (!get_pint(ua, buf)) {
271 num = ua->pint32_val;
272 if (num < 0 || num > max) {
273 ua->warning_msg(_("The number must be between 0 and %d\n"), max);
281 if (!get_cmd(ua, _("Enter Volume name: "))) {
285 if (!get_cmd(ua, _("Enter base volume name: "))) {
289 /* Don't allow | in Volume name because it is the volume separator character */
290 if (!is_volume_name_legal(ua, ua->cmd)) {
293 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
294 ua->warning_msg(_("Volume name too long.\n"));
297 if (strlen(ua->cmd) == 0) {
298 ua->warning_msg(_("Volume name must be at least one character long.\n"));
304 bstrncpy(name, ua->cmd, sizeof(name));
306 bstrncat(name, "%04d", sizeof(name));
309 if (!get_pint(ua, _("Enter the starting number: "))) {
312 startnum = ua->pint32_val;
314 ua->warning_msg(_("Start number must be greater than zero.\n"));
324 if (store && store->autochanger) {
325 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
328 Slot = ua->pint32_val;
329 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
332 InChanger = ua->pint32_val;
335 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
336 for (i=startnum; i < num+startnum; i++) {
337 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
339 mr.InChanger = InChanger;
340 mr.StorageId = store->StorageId;
342 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
343 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
344 ua->error_msg("%s", db_strerror(ua->db));
348 first_id = mr.PoolId;
352 Dmsg0(200, "Update pool record.\n");
353 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
354 ua->warning_msg("%s", db_strerror(ua->db));
357 ua->send_msg(_("%d Volumes created in pool %s\n"), num, pr.Name);
363 * Turn auto mount on/off
368 int automount_cmd(UAContext *ua, const char *cmd)
373 if (!get_cmd(ua, _("Turn on or off? "))) {
381 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
389 static int cancel_cmd(UAContext *ua, const char *cmd)
394 char JobName[MAX_NAME_LENGTH];
396 for (i=1; i<ua->argc; i++) {
397 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
402 JobId = str_to_int64(ua->argv[i]);
403 if (!(jcr=get_jcr_by_id(JobId))) {
404 ua->error_msg(_("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
408 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
412 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
413 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
414 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
415 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
418 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
422 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
423 ua->warning_msg(_("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
424 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
425 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
432 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
433 ua->error_msg(_("Unauthorized command from this console.\n"));
438 * If we still do not have a jcr,
439 * throw up a list and ask the user to select one.
442 int tjobs = 0; /* total # number jobs */
443 /* Count Jobs running */
445 if (jcr->JobId == 0) { /* this is us */
448 tjobs++; /* count of all jobs */
449 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
450 continue; /* skip not authorized */
452 njobs++; /* count of authorized jobs */
456 if (njobs == 0) { /* no authorized */
458 ua->send_msg(_("No Jobs running.\n"));
460 ua->send_msg(_("None of your jobs are running.\n"));
465 start_prompt(ua, _("Select Job:\n"));
468 if (jcr->JobId == 0) { /* this is us */
471 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
472 continue; /* skip not authorized */
474 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
479 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
482 if (ua->api && njobs == 1) {
484 bsnprintf(nbuf, sizeof(nbuf), _("Cancel: %s\n\n%s"), buf,
485 _("Confirm cancel?"));
486 if (!get_yesno(ua, nbuf) || ua->pint32_val == 0) {
491 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
496 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
497 jcr = get_jcr_by_full_name(JobName);
499 ua->warning_msg(_("Job \"%s\" not found.\n"), JobName);
504 ret = cancel_job(ua, jcr);
510 * This is a common routine to create or update a
511 * Pool DB base record from a Pool Resource. We handle
512 * the setting of MaxVols and NumVols slightly differently
513 * depending on if we are creating the Pool or we are
514 * simply bringing it into agreement with the resource (updage).
516 * Caution : RecyclePoolId isn't setup in this function.
517 * You can use set_pooldbr_recyclepoolid();
520 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
522 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
523 if (op == POOL_OP_CREATE) {
524 pr->MaxVols = pool->max_volumes;
526 } else { /* update pool */
527 if (pr->MaxVols != pool->max_volumes) {
528 pr->MaxVols = pool->max_volumes;
530 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
531 pr->MaxVols = pr->NumVols;
534 pr->LabelType = pool->LabelType;
535 pr->UseOnce = pool->use_volume_once;
536 pr->UseCatalog = pool->use_catalog;
537 pr->Recycle = pool->Recycle;
538 pr->VolRetention = pool->VolRetention;
539 pr->VolUseDuration = pool->VolUseDuration;
540 pr->MaxVolJobs = pool->MaxVolJobs;
541 pr->MaxVolFiles = pool->MaxVolFiles;
542 pr->MaxVolBytes = pool->MaxVolBytes;
543 pr->AutoPrune = pool->AutoPrune;
544 pr->Recycle = pool->Recycle;
545 if (pool->label_format) {
546 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
548 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
552 /* set/update Pool.RecyclePoolId in Catalog */
553 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
557 if (!pool->RecyclePool) {
561 memset(&pr, 0, sizeof(POOL_DBR));
562 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
564 if (!db_get_pool_record(jcr, db, &pr)) {
565 return -1; /* not exists in database */
568 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
570 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
571 return -1; /* error */
574 if (!db_update_pool_record(jcr, db, &pr)) {
575 return -1; /* error */
580 /* set POOL_DBR.RecyclePoolId from Pool resource
581 * works with set_pooldbr_from_poolres
583 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
588 if (pool->RecyclePool) {
589 memset(&rpool, 0, sizeof(POOL_DBR));
591 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
592 if (db_get_pool_record(jcr, db, &rpool)) {
593 pr->RecyclePoolId = rpool.PoolId;
595 Jmsg(jcr, M_WARNING, 0,
596 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
597 "Try to update it with 'update pool=%s'\n"),
598 pool->name(), rpool.Name, rpool.Name,pool->name());
602 } else { /* no RecyclePool used, set it to 0 */
603 pr->RecyclePoolId = 0;
610 * Create a pool record from a given Pool resource
611 * Also called from backup.c
612 * Returns: -1 on error
613 * 0 record already exists
617 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
621 memset(&pr, 0, sizeof(POOL_DBR));
623 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
625 if (db_get_pool_record(jcr, db, &pr)) {
627 if (op == POOL_OP_UPDATE) { /* update request */
628 set_pooldbr_from_poolres(&pr, pool, op);
629 db_update_pool_record(jcr, db, &pr);
631 return 0; /* exists */
634 set_pooldbr_from_poolres(&pr, pool, op);
636 if (!db_create_pool_record(jcr, db, &pr)) {
637 return -1; /* error */
645 * Create a Pool Record in the database.
646 * It is always created from the Resource record.
648 static int create_cmd(UAContext *ua, const char *cmd)
652 if (!open_client_db(ua)) {
656 pool = get_pool_resource(ua);
661 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
663 ua->error_msg(_("Error: Pool %s already exists.\n"
664 "Use update to change it.\n"), pool->name());
668 ua->error_msg("%s", db_strerror(ua->db));
674 ua->send_msg(_("Pool %s created.\n"), pool->name());
679 extern DIRRES *director;
682 * Python control command
683 * python restart (restarts interpreter)
685 static int python_cmd(UAContext *ua, const char *cmd)
687 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
688 term_python_interpreter();
689 init_python_interpreter(director->name(),
690 director->scripts_directory, "DirStartUp");
691 ua->send_msg(_("Python interpreter restarted.\n"));
693 ua->warning_msg(_("Nothing done.\n"));
700 * Set a new address in a Client resource. We do this only
701 * if the Console name is the same as the Client name
702 * and the Console can access the client.
704 static int setip_cmd(UAContext *ua, const char *cmd)
708 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
709 ua->error_msg(_("Unauthorized command from this console.\n"));
713 client = GetClientResWithName(ua->cons->name());
716 ua->error_msg(_("Client \"%s\" not found.\n"), ua->cons->name());
719 if (client->address) {
720 free(client->address);
722 /* MA Bug 6 remove ifdef */
723 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
724 client->address = bstrdup(buf);
725 ua->send_msg(_("Client \"%s\" address set to %s\n"),
726 client->name(), client->address);
733 static void do_en_disable_cmd(UAContext *ua, bool setting)
738 i = find_arg_with_value(ua, NT_("job"));
740 job = select_job_resource(ua);
746 job = GetJobResWithName(ua->argv[i]);
750 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
754 if (!acl_access_ok(ua, Job_ACL, job->name())) {
755 ua->error_msg(_("Unauthorized command from this console.\n"));
758 job->enabled = setting;
759 ua->send_msg(_("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
763 static int enable_cmd(UAContext *ua, const char *cmd)
765 do_en_disable_cmd(ua, true);
769 static int disable_cmd(UAContext *ua, const char *cmd)
771 do_en_disable_cmd(ua, false);
776 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
782 lstore.store = store;
783 pm_strcpy(lstore.store_source, _("unknown source"));
784 set_wstorage(jcr, &lstore);
785 /* Try connecting for up to 15 seconds */
786 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
787 store->name(), store->address, store->SDport);
788 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
789 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
792 Dmsg0(120, _("Connected to storage daemon\n"));
793 sd = jcr->store_bsock;
794 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
795 if (bnet_recv(sd) >= 0) {
796 ua->send_msg("%s", sd->msg);
798 bnet_sig(sd, BNET_TERMINATE);
800 jcr->store_bsock = NULL;
804 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
808 /* Connect to File daemon */
810 ua->jcr->client = client;
811 /* Try to connect for 15 seconds */
812 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
813 client->name(), client->address, client->FDport);
814 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
815 ua->error_msg(_("Failed to connect to Client.\n"));
818 Dmsg0(120, "Connected to file daemon\n");
819 fd = ua->jcr->file_bsock;
820 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
821 if (bnet_recv(fd) >= 0) {
822 ua->send_msg("%s", fd->msg);
824 bnet_sig(fd, BNET_TERMINATE);
826 ua->jcr->file_bsock = NULL;
831 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
833 STORE *store, **unique_store;
834 CLIENT *client, **unique_client;
840 /* Count Storage items */
844 foreach_res(store, R_STORAGE) {
847 unique_store = (STORE **) malloc(i * sizeof(STORE));
848 /* Find Unique Storage address/port */
849 store = (STORE *)GetNextRes(R_STORAGE, NULL);
851 unique_store[i++] = store;
852 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
854 for (j=0; j<i; j++) {
855 if (strcmp(unique_store[j]->address, store->address) == 0 &&
856 unique_store[j]->SDport == store->SDport) {
862 unique_store[i++] = store;
863 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
868 /* Call each unique Storage daemon */
869 for (j=0; j<i; j++) {
870 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
874 /* Count Client items */
878 foreach_res(client, R_CLIENT) {
881 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
882 /* Find Unique Client address/port */
883 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
885 unique_client[i++] = client;
886 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
888 for (j=0; j<i; j++) {
889 if (strcmp(unique_client[j]->address, client->address) == 0 &&
890 unique_client[j]->FDport == client->FDport) {
896 unique_client[i++] = client;
897 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
902 /* Call each unique File daemon */
903 for (j=0; j<i; j++) {
904 do_client_setdebug(ua, unique_client[j], level, trace_flag);
910 * setdebug level=nn all trace=1/0
912 static int setdebug_cmd(UAContext *ua, const char *cmd)
920 if (!open_client_db(ua)) {
923 Dmsg1(120, "setdebug:%s:\n", cmd);
926 i = find_arg_with_value(ua, "level");
928 level = atoi(ua->argv[i]);
931 if (!get_pint(ua, _("Enter new debug level: "))) {
934 level = ua->pint32_val;
937 /* Look for trace flag. -1 => not change */
938 i = find_arg_with_value(ua, "trace");
940 trace_flag = atoi(ua->argv[i]);
941 if (trace_flag > 0) {
947 for (i=1; i<ua->argc; i++) {
948 if (strcasecmp(ua->argk[i], "all") == 0) {
949 do_all_setdebug(ua, level, trace_flag);
952 if (strcasecmp(ua->argk[i], "dir") == 0 ||
953 strcasecmp(ua->argk[i], "director") == 0) {
955 set_trace(trace_flag);
958 if (strcasecmp(ua->argk[i], "client") == 0 ||
959 strcasecmp(ua->argk[i], "fd") == 0) {
962 client = GetClientResWithName(ua->argv[i]);
964 do_client_setdebug(ua, client, level, trace_flag);
968 client = select_client_resource(ua);
970 do_client_setdebug(ua, client, level, trace_flag);
975 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
976 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
977 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
980 store = GetStoreResWithName(ua->argv[i]);
982 do_storage_setdebug(ua, store, level, trace_flag);
986 store = get_storage_resource(ua, false/*no default*/);
988 do_storage_setdebug(ua, store, level, trace_flag);
994 * We didn't find an appropriate keyword above, so
997 start_prompt(ua, _("Available daemons are: \n"));
998 add_prompt(ua, _("Director"));
999 add_prompt(ua, _("Storage"));
1000 add_prompt(ua, _("Client"));
1001 add_prompt(ua, _("All"));
1002 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
1003 case 0: /* Director */
1004 debug_level = level;
1005 set_trace(trace_flag);
1008 store = get_storage_resource(ua, false/*no default*/);
1010 do_storage_setdebug(ua, store, level, trace_flag);
1014 client = select_client_resource(ua);
1016 do_client_setdebug(ua, client, level, trace_flag);
1020 do_all_setdebug(ua, level, trace_flag);
1029 * Turn debug tracing to file on/off
1031 static int trace_cmd(UAContext *ua, const char *cmd)
1035 if (ua->argc != 2) {
1036 if (!get_cmd(ua, _("Turn on or off? "))) {
1041 onoff = ua->argk[1];
1044 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1049 static int var_cmd(UAContext *ua, const char *cmd)
1051 POOLMEM *val = get_pool_memory(PM_FNAME);
1054 if (!open_client_db(ua)) {
1057 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1060 while (*var == ' ') { /* skip spaces */
1063 Dmsg1(100, "Var=%s:\n", var);
1064 variable_expansion(ua->jcr, var, &val);
1065 ua->send_msg("%s\n", val);
1066 free_pool_memory(val);
1070 static int estimate_cmd(UAContext *ua, const char *cmd)
1073 CLIENT *client = NULL;
1074 FILESET *fileset = NULL;
1076 char since[MAXSTRING];
1079 jcr->JobLevel = L_FULL;
1080 for (int i=1; i<ua->argc; i++) {
1081 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1082 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1084 client = GetClientResWithName(ua->argv[i]);
1088 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1090 job = GetJobResWithName(ua->argv[i]);
1091 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1092 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1098 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1100 fileset = GetFileSetResWithName(ua->argv[i]);
1101 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1102 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1108 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1112 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1113 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1114 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1119 if (!job && !(client && fileset)) {
1120 if (!(job = select_job_resource(ua))) {
1125 job = GetJobResWithName(ua->argk[1]);
1127 ua->error_msg(_("No job specified.\n"));
1130 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1131 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1136 client = job->client;
1139 fileset = job->fileset;
1141 jcr->client = client;
1142 jcr->fileset = fileset;
1144 if (job->pool->catalog) {
1145 ua->catalog = job->pool->catalog;
1147 ua->catalog = client->catalog;
1155 jcr->JobType = JT_BACKUP;
1156 init_jcr_job_record(jcr);
1158 if (!get_or_create_client_record(jcr)) {
1161 if (!get_or_create_fileset_record(jcr)) {
1165 get_level_since_time(ua->jcr, since, sizeof(since));
1167 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1168 job->client->name(), job->client->address, job->client->FDport);
1169 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1170 ua->error_msg(_("Failed to connect to Client.\n"));
1174 if (!send_include_list(jcr)) {
1175 ua->error_msg(_("Error sending include list.\n"));
1179 if (!send_exclude_list(jcr)) {
1180 ua->error_msg(_("Error sending exclude list.\n"));
1184 if (!send_level_command(jcr)) {
1188 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1189 while (bnet_recv(jcr->file_bsock) >= 0) {
1190 ua->send_msg("%s", jcr->file_bsock->msg);
1194 if (jcr->file_bsock) {
1195 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1196 bnet_close(jcr->file_bsock);
1197 jcr->file_bsock = NULL;
1206 static int time_cmd(UAContext *ua, const char *cmd)
1209 time_t ttime = time(NULL);
1211 (void)localtime_r(&ttime, &tm);
1212 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1213 ua->send_msg("%s\n", sdt);
1218 * reload the conf file
1220 extern "C" void reload_config(int sig);
1222 static int reload_cmd(UAContext *ua, const char *cmd)
1229 * Delete Pool records (should purge Media with it).
1231 * delete pool=<pool-name>
1232 * delete volume pool=<pool-name> volume=<name>
1235 static int delete_cmd(UAContext *ua, const char *cmd)
1237 static const char *keywords[] = {
1243 if (!open_client_db(ua)) {
1247 switch (find_arg_keyword(ua, keywords)) {
1256 while ((i=find_arg(ua, "jobid")) > 0) {
1258 *ua->argk[i] = 0; /* zap keyword already visited */
1266 "In general it is not a good idea to delete either a\n"
1267 "Pool or a Volume since they may contain data.\n\n"));
1269 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1280 ua->warning_msg(_("Nothing done.\n"));
1288 * delete_job has been modified to parse JobID lists like the
1290 * delete JobID=3,4,6,7-11,14
1292 * Thanks to Phil Stracchino for the above addition.
1295 static void delete_job(UAContext *ua)
1300 int i = find_arg_with_value(ua, NT_("jobid"));
1302 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1303 s = bstrdup(ua->argv[i]);
1306 * We could use strtok() here. But we're not going to, because:
1307 * (a) strtok() is deprecated, having been replaced by strsep();
1308 * (b) strtok() is broken in significant ways.
1309 * we could use strsep() instead, but it's not universally available.
1310 * so we grow our own using strchr().
1312 sep = strchr(tok, ',');
1313 while (sep != NULL) {
1315 if (strchr(tok, '-')) {
1316 delete_job_id_range(ua, tok);
1318 JobId = str_to_int64(tok);
1319 do_job_delete(ua, JobId);
1322 sep = strchr(tok, ',');
1324 /* pick up the last token */
1325 if (strchr(tok, '-')) {
1326 delete_job_id_range(ua, tok);
1328 JobId = str_to_int64(tok);
1329 do_job_delete(ua, JobId);
1334 JobId = str_to_int64(ua->argv[i]);
1335 do_job_delete(ua, JobId);
1337 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1340 JobId = ua->int64_val;
1341 do_job_delete(ua, JobId);
1346 * we call delete_job_id_range to parse range tokens and iterate over ranges
1348 static void delete_job_id_range(UAContext *ua, char *tok)
1353 tok2 = strchr(tok, '-');
1356 j1 = str_to_int64(tok);
1357 j2 = str_to_int64(tok2);
1358 for (j=j1; j<=j2; j++) {
1359 do_job_delete(ua, j);
1364 * do_job_delete now performs the actual delete operation atomically
1366 static void do_job_delete(UAContext *ua, JobId_t JobId)
1370 edit_int64(JobId, ed1);
1371 purge_jobs_from_catalog(ua, ed1);
1372 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1376 * Delete media records from database -- dangerous
1378 static int delete_volume(UAContext *ua)
1383 if (!select_media_dbr(ua, &mr)) {
1386 ua->warning_msg(_("\nThis command will delete volume %s\n"
1387 "and all Jobs saved on that volume from the Catalog\n"),
1390 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1392 if (!get_yesno(ua, buf)) {
1395 if (ua->pint32_val) {
1396 db_delete_media_record(ua->jcr, ua->db, &mr);
1402 * Delete a pool record from the database -- dangerous
1404 static int delete_pool(UAContext *ua)
1409 memset(&pr, 0, sizeof(pr));
1411 if (!get_pool_dbr(ua, &pr)) {
1414 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1416 if (!get_yesno(ua, buf)) {
1419 if (ua->pint32_val) {
1420 db_delete_pool_record(ua->jcr, ua->db, &pr);
1425 int memory_cmd(UAContext *ua, const char *cmd)
1427 list_dir_status_header(ua);
1428 sm_dump(false, true);
1432 static void do_mount_cmd(UAContext *ua, const char *command)
1437 char dev_name[MAX_NAME_LENGTH];
1441 if (!open_client_db(ua)) {
1444 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1446 store.store = get_storage_resource(ua, true/*arg is storage*/);
1450 pm_strcpy(store.store_source, _("unknown source"));
1451 set_wstorage(jcr, &store);
1452 drive = get_storage_drive(ua, store.store);
1453 if (strcmp(command, "mount") == 0) {
1454 slot = get_storage_slot(ua, store.store);
1457 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1458 store.store->media_type, store.store->dev_name(), drive);
1460 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1461 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1464 sd = jcr->store_bsock;
1465 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1466 bash_spaces(dev_name);
1468 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1470 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1472 while (bnet_recv(sd) >= 0) {
1473 ua->send_msg("%s", sd->msg);
1475 bnet_sig(sd, BNET_TERMINATE);
1477 jcr->store_bsock = NULL;
1481 * mount [storage=<name>] [drive=nn] [slot=mm]
1483 static int mount_cmd(UAContext *ua, const char *cmd)
1485 do_mount_cmd(ua, "mount"); /* mount */
1491 * unmount [storage=<name>] [drive=nn]
1493 static int unmount_cmd(UAContext *ua, const char *cmd)
1495 do_mount_cmd(ua, "unmount"); /* unmount */
1501 * release [storage=<name>] [drive=nn]
1503 static int release_cmd(UAContext *ua, const char *cmd)
1505 do_mount_cmd(ua, "release"); /* release */
1512 * use catalog=<name>
1514 static int use_cmd(UAContext *ua, const char *cmd)
1516 CAT *oldcatalog, *catalog;
1519 close_db(ua); /* close any previously open db */
1520 oldcatalog = ua->catalog;
1522 if (!(catalog = get_catalog_resource(ua))) {
1523 ua->catalog = oldcatalog;
1525 ua->catalog = catalog;
1528 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1529 ua->catalog->name(), ua->catalog->db_name);
1534 int quit_cmd(UAContext *ua, const char *cmd)
1540 /* Handler to get job status */
1541 static int status_handler(void *ctx, int num_fields, char **row)
1543 char *val = (char *)ctx;
1548 *val = '?'; /* Unknown by default */
1555 * Wait until no job is running
1557 int wait_cmd(UAContext *ua, const char *cmd)
1563 * Wait until no job is running
1565 if (ua->argc == 1) {
1566 bmicrosleep(0, 200000); /* let job actually start */
1567 for (bool running=true; running; ) {
1570 if (jcr->JobId != 0) {
1584 /* we have jobid, jobname or ujobid argument */
1586 uint32_t jobid = 0 ;
1588 if (!open_client_db(ua)) {
1589 ua->error_msg(_("ERR: Can't open db\n")) ;
1593 for (int i=1; i<ua->argc; i++) {
1594 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1598 jobid = str_to_int64(ua->argv[i]);
1600 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1601 strcasecmp(ua->argk[i], "job") == 0) {
1605 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1607 jobid = jcr->JobId ;
1611 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1615 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1617 jobid = jcr->JobId ;
1625 ua->error_msg(_("ERR: Job was not found\n"));
1630 * We wait the end of a specific job
1633 bmicrosleep(0, 200000); /* let job actually start */
1634 for (bool running=true; running; ) {
1637 jcr=get_jcr_by_id(jobid) ;
1650 * We have to get JobStatus
1654 char jobstatus = '?'; /* Unknown by default */
1657 bsnprintf(buf, sizeof(buf),
1658 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1661 db_sql_query(ua->db, buf,
1662 status_handler, (void *)&jobstatus);
1664 switch (jobstatus) {
1666 status = 1 ; /* Warning */
1670 case JS_ErrorTerminated:
1672 status = 2 ; /* Critical */
1676 status = 0 ; /* Ok */
1680 status = 3 ; /* Unknown */
1684 ua->send_msg("JobId=%i\n", jobid) ;
1685 ua->send_msg("JobStatus=%s (%c)\n",
1686 job_status_to_str(jobstatus),
1689 if (ua->gui || ua->api) {
1690 ua->send_msg("ExitStatus=%i\n", status) ;
1697 static int help_cmd(UAContext *ua, const char *cmd)
1701 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1702 for (i=0; i<comsize; i++) {
1703 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1705 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1709 int qhelp_cmd(UAContext *ua, const char *cmd)
1713 for (i=0; i<comsize; i++) {
1714 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1720 static int version_cmd(UAContext *ua, const char *cmd)
1722 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1723 HOST_OS, DISTNAME, DISTVER);
1728 * Test code -- turned on only for debug testing
1730 static int version_cmd(UAContext *ua, const char *cmd)
1733 POOL_MEM query(PM_MESSAGE);
1735 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1736 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1737 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1738 for (int i=0; i < ids.num_ids; i++) {
1739 ua->send_msg("id=%d\n", ids.DBId[i]);
1747 * This call explicitly checks for a catalog=xxx and
1748 * if given, opens that catalog. It also checks for
1749 * client=xxx and if found, opens the catalog
1750 * corresponding to that client. If we still don't
1751 * have a catalog, look for a Job keyword and get the
1752 * catalog from its client record.
1754 bool open_client_db(UAContext *ua)
1761 /* Try for catalog keyword */
1762 i = find_arg_with_value(ua, NT_("catalog"));
1764 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1765 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1768 catalog = GetCatalogResWithName(ua->argv[i]);
1770 if (ua->catalog && ua->catalog != catalog) {
1773 ua->catalog = catalog;
1778 /* Try for client keyword */
1779 i = find_arg_with_value(ua, NT_("client"));
1781 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1782 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1785 client = GetClientResWithName(ua->argv[i]);
1787 catalog = client->catalog;
1788 if (ua->catalog && ua->catalog != catalog) {
1791 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1792 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1795 ua->catalog = catalog;
1800 /* Try for Job keyword */
1801 i = find_arg_with_value(ua, NT_("job"));
1803 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1804 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1807 job = GetJobResWithName(ua->argv[i]);
1809 catalog = job->client->catalog;
1810 if (ua->catalog && ua->catalog != catalog) {
1813 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1814 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1817 ua->catalog = catalog;
1827 * Open the catalog database.
1829 bool open_db(UAContext *ua)
1835 ua->catalog = get_catalog_resource(ua);
1837 ua->error_msg( _("Could not find a Catalog resource\n"));
1842 ua->jcr->catalog = ua->catalog;
1844 Dmsg0(100, "UA Open database\n");
1845 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
1846 ua->catalog->db_user,
1847 ua->catalog->db_password, ua->catalog->db_address,
1848 ua->catalog->db_port, ua->catalog->db_socket,
1849 ua->catalog->mult_db_connections);
1850 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1851 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1852 ua->catalog->db_name);
1854 ua->error_msg("%s", db_strerror(ua->db));
1859 ua->jcr->db = ua->db;
1861 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1863 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1867 void close_db(UAContext *ua)
1870 db_close_database(ua->jcr, ua->db);