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 ua->catalog = client->catalog;
1151 jcr->JobType = JT_BACKUP;
1152 init_jcr_job_record(jcr);
1154 if (!get_or_create_client_record(jcr)) {
1157 if (!get_or_create_fileset_record(jcr)) {
1161 get_level_since_time(ua->jcr, since, sizeof(since));
1163 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1164 job->client->name(), job->client->address, job->client->FDport);
1165 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1166 ua->error_msg(_("Failed to connect to Client.\n"));
1170 if (!send_include_list(jcr)) {
1171 ua->error_msg(_("Error sending include list.\n"));
1175 if (!send_exclude_list(jcr)) {
1176 ua->error_msg(_("Error sending exclude list.\n"));
1180 if (!send_level_command(jcr)) {
1184 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1185 while (bnet_recv(jcr->file_bsock) >= 0) {
1186 ua->send_msg("%s", jcr->file_bsock->msg);
1190 if (jcr->file_bsock) {
1191 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1192 bnet_close(jcr->file_bsock);
1193 jcr->file_bsock = NULL;
1202 static int time_cmd(UAContext *ua, const char *cmd)
1205 time_t ttime = time(NULL);
1207 (void)localtime_r(&ttime, &tm);
1208 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1209 ua->send_msg("%s\n", sdt);
1214 * reload the conf file
1216 extern "C" void reload_config(int sig);
1218 static int reload_cmd(UAContext *ua, const char *cmd)
1225 * Delete Pool records (should purge Media with it).
1227 * delete pool=<pool-name>
1228 * delete volume pool=<pool-name> volume=<name>
1231 static int delete_cmd(UAContext *ua, const char *cmd)
1233 static const char *keywords[] = {
1239 if (!open_client_db(ua)) {
1243 switch (find_arg_keyword(ua, keywords)) {
1252 while ((i=find_arg(ua, "jobid")) > 0) {
1254 *ua->argk[i] = 0; /* zap keyword already visited */
1262 "In general it is not a good idea to delete either a\n"
1263 "Pool or a Volume since they may contain data.\n\n"));
1265 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1276 ua->warning_msg(_("Nothing done.\n"));
1284 * delete_job has been modified to parse JobID lists like the
1286 * delete JobID=3,4,6,7-11,14
1288 * Thanks to Phil Stracchino for the above addition.
1291 static void delete_job(UAContext *ua)
1296 int i = find_arg_with_value(ua, NT_("jobid"));
1298 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1299 s = bstrdup(ua->argv[i]);
1302 * We could use strtok() here. But we're not going to, because:
1303 * (a) strtok() is deprecated, having been replaced by strsep();
1304 * (b) strtok() is broken in significant ways.
1305 * we could use strsep() instead, but it's not universally available.
1306 * so we grow our own using strchr().
1308 sep = strchr(tok, ',');
1309 while (sep != NULL) {
1311 if (strchr(tok, '-')) {
1312 delete_job_id_range(ua, tok);
1314 JobId = str_to_int64(tok);
1315 do_job_delete(ua, JobId);
1318 sep = strchr(tok, ',');
1320 /* pick up the last token */
1321 if (strchr(tok, '-')) {
1322 delete_job_id_range(ua, tok);
1324 JobId = str_to_int64(tok);
1325 do_job_delete(ua, JobId);
1330 JobId = str_to_int64(ua->argv[i]);
1331 do_job_delete(ua, JobId);
1333 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1336 JobId = ua->int64_val;
1337 do_job_delete(ua, JobId);
1342 * we call delete_job_id_range to parse range tokens and iterate over ranges
1344 static void delete_job_id_range(UAContext *ua, char *tok)
1349 tok2 = strchr(tok, '-');
1352 j1 = str_to_int64(tok);
1353 j2 = str_to_int64(tok2);
1354 for (j=j1; j<=j2; j++) {
1355 do_job_delete(ua, j);
1360 * do_job_delete now performs the actual delete operation atomically
1362 static void do_job_delete(UAContext *ua, JobId_t JobId)
1366 edit_int64(JobId, ed1);
1367 purge_jobs_from_catalog(ua, ed1);
1368 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1372 * Delete media records from database -- dangerous
1374 static int delete_volume(UAContext *ua)
1379 if (!select_media_dbr(ua, &mr)) {
1382 ua->warning_msg(_("\nThis command will delete volume %s\n"
1383 "and all Jobs saved on that volume from the Catalog\n"),
1386 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1388 if (!get_yesno(ua, buf)) {
1391 if (ua->pint32_val) {
1392 db_delete_media_record(ua->jcr, ua->db, &mr);
1398 * Delete a pool record from the database -- dangerous
1400 static int delete_pool(UAContext *ua)
1405 memset(&pr, 0, sizeof(pr));
1407 if (!get_pool_dbr(ua, &pr)) {
1410 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1412 if (!get_yesno(ua, buf)) {
1415 if (ua->pint32_val) {
1416 db_delete_pool_record(ua->jcr, ua->db, &pr);
1421 int memory_cmd(UAContext *ua, const char *cmd)
1423 list_dir_status_header(ua);
1424 sm_dump(false, true);
1428 static void do_mount_cmd(UAContext *ua, const char *command)
1433 char dev_name[MAX_NAME_LENGTH];
1437 if (!open_client_db(ua)) {
1440 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1442 store.store = get_storage_resource(ua, true/*arg is storage*/);
1446 pm_strcpy(store.store_source, _("unknown source"));
1447 set_wstorage(jcr, &store);
1448 drive = get_storage_drive(ua, store.store);
1449 if (strcmp(command, "mount") == 0) {
1450 slot = get_storage_slot(ua, store.store);
1453 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1454 store.store->media_type, store.store->dev_name(), drive);
1456 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1457 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1460 sd = jcr->store_bsock;
1461 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1462 bash_spaces(dev_name);
1464 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1466 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1468 while (bnet_recv(sd) >= 0) {
1469 ua->send_msg("%s", sd->msg);
1471 bnet_sig(sd, BNET_TERMINATE);
1473 jcr->store_bsock = NULL;
1477 * mount [storage=<name>] [drive=nn] [slot=mm]
1479 static int mount_cmd(UAContext *ua, const char *cmd)
1481 do_mount_cmd(ua, "mount"); /* mount */
1487 * unmount [storage=<name>] [drive=nn]
1489 static int unmount_cmd(UAContext *ua, const char *cmd)
1491 do_mount_cmd(ua, "unmount"); /* unmount */
1497 * release [storage=<name>] [drive=nn]
1499 static int release_cmd(UAContext *ua, const char *cmd)
1501 do_mount_cmd(ua, "release"); /* release */
1508 * use catalog=<name>
1510 static int use_cmd(UAContext *ua, const char *cmd)
1512 CAT *oldcatalog, *catalog;
1515 close_db(ua); /* close any previously open db */
1516 oldcatalog = ua->catalog;
1518 if (!(catalog = get_catalog_resource(ua))) {
1519 ua->catalog = oldcatalog;
1521 ua->catalog = catalog;
1524 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1525 ua->catalog->name(), ua->catalog->db_name);
1530 int quit_cmd(UAContext *ua, const char *cmd)
1536 /* Handler to get job status */
1537 static int status_handler(void *ctx, int num_fields, char **row)
1539 char *val = (char *)ctx;
1544 *val = '?'; /* Unknown by default */
1551 * Wait until no job is running
1553 int wait_cmd(UAContext *ua, const char *cmd)
1558 * Wait until no job is running
1560 if (ua->argc == 1) {
1561 bmicrosleep(0, 200000); /* let job actually start */
1562 for (bool running=true; running; ) {
1565 if (jcr->JobId != 0) {
1579 /* we have jobid, jobname or ujobid argument */
1581 uint32_t jobid = 0 ;
1583 if (!open_client_db(ua)) {
1584 ua->error_msg(_("ERR: Can't open db\n")) ;
1588 for (int i=1; i<ua->argc; i++) {
1589 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1593 jobid = str_to_int64(ua->argv[i]);
1595 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1596 strcasecmp(ua->argk[i], "job") == 0) {
1600 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1602 jobid = jcr->JobId ;
1606 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1610 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1612 jobid = jcr->JobId ;
1620 ua->error_msg(_("ERR: Job was not found\n"));
1625 * We wait the end of job
1628 bmicrosleep(0, 200000); /* let job actually start */
1629 for (bool running=true; running; ) {
1632 jcr=get_jcr_by_id(jobid) ;
1645 * We have to get JobStatus
1649 char jobstatus = '?'; /* Unknown by default */
1652 bsnprintf(buf, sizeof(buf),
1653 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1656 db_sql_query(ua->db, buf,
1657 status_handler, (void *)&jobstatus);
1659 switch (jobstatus) {
1661 status = 1 ; /* Warning */
1665 case JS_ErrorTerminated:
1667 status = 2 ; /* Critical */
1671 status = 0 ; /* Ok */
1675 status = 3 ; /* Unknown */
1679 ua->send_msg("JobId=%i\n", jobid) ;
1680 ua->send_msg("JobStatus=%s (%c)\n",
1681 job_status_to_str(jobstatus),
1684 if (ua->gui || ua->api) {
1685 ua->send_msg("ExitStatus=%i\n", status) ;
1692 static int help_cmd(UAContext *ua, const char *cmd)
1696 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1697 for (i=0; i<comsize; i++) {
1698 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1700 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1704 int qhelp_cmd(UAContext *ua, const char *cmd)
1708 for (i=0; i<comsize; i++) {
1709 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1715 static int version_cmd(UAContext *ua, const char *cmd)
1717 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1718 HOST_OS, DISTNAME, DISTVER);
1723 * Test code -- turned on only for debug testing
1725 static int version_cmd(UAContext *ua, const char *cmd)
1728 POOL_MEM query(PM_MESSAGE);
1730 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1731 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1732 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1733 for (int i=0; i < ids.num_ids; i++) {
1734 ua->send_msg("id=%d\n", ids.DBId[i]);
1742 * This call explicitly checks for a catalog=xxx and
1743 * if given, opens that catalog. It also checks for
1744 * client=xxx and if found, opens the catalog
1745 * corresponding to that client. If we still don't
1746 * have a catalog, look for a Job keyword and get the
1747 * catalog from its client record.
1749 bool open_client_db(UAContext *ua)
1756 /* Try for catalog keyword */
1757 i = find_arg_with_value(ua, NT_("catalog"));
1759 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1760 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1763 catalog = GetCatalogResWithName(ua->argv[i]);
1765 if (ua->catalog && ua->catalog != catalog) {
1768 ua->catalog = catalog;
1773 /* Try for client keyword */
1774 i = find_arg_with_value(ua, NT_("client"));
1776 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1777 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1780 client = GetClientResWithName(ua->argv[i]);
1782 catalog = client->catalog;
1783 if (ua->catalog && ua->catalog != catalog) {
1786 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1787 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1790 ua->catalog = catalog;
1795 /* Try for Job keyword */
1796 i = find_arg_with_value(ua, NT_("job"));
1798 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1799 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1802 job = GetJobResWithName(ua->argv[i]);
1804 catalog = job->client->catalog;
1805 if (ua->catalog && ua->catalog != catalog) {
1808 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1809 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1812 ua->catalog = catalog;
1822 * Open the catalog database.
1824 bool open_db(UAContext *ua)
1830 ua->catalog = get_catalog_resource(ua);
1832 ua->error_msg( _("Could not find a Catalog resource\n"));
1837 ua->jcr->catalog = ua->catalog;
1839 Dmsg0(100, "UA Open database\n");
1840 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1841 ua->catalog->db_password, ua->catalog->db_address,
1842 ua->catalog->db_port, ua->catalog->db_socket,
1843 ua->catalog->mult_db_connections);
1844 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1845 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1846 ua->catalog->db_name);
1848 ua->error_msg("%s", db_strerror(ua->db));
1853 ua->jcr->db = ua->db;
1855 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1857 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1861 void close_db(UAContext *ua)
1864 db_close_database(ua->jcr, ua->db);