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]);
1086 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
1092 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1094 job = GetJobResWithName(ua->argv[i]);
1096 ua->error_msg(_("Job \"%s\" not found.\n"), ua->argv[i]);
1099 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1100 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1106 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1108 fileset = GetFileSetResWithName(ua->argv[i]);
1110 ua->error_msg(_("Fileset \"%s\" not found.\n"), ua->argv[i]);
1113 if (!acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1114 ua->error_msg(_("No authorization for FileSet \"%s\"\n"), fileset->name());
1120 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1124 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1125 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1126 ua->error_msg(_("Level %s not valid.\n"), ua->argv[i]);
1131 if (!job && !(client && fileset)) {
1132 if (!(job = select_job_resource(ua))) {
1137 job = GetJobResWithName(ua->argk[1]);
1139 ua->error_msg(_("No job specified.\n"));
1142 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1143 ua->error_msg(_("No authorization for Job \"%s\"\n"), job->name());
1148 client = job->client;
1151 fileset = job->fileset;
1153 jcr->client = client;
1154 jcr->fileset = fileset;
1156 if (job->pool->catalog) {
1157 ua->catalog = job->pool->catalog;
1159 ua->catalog = client->catalog;
1167 jcr->JobType = JT_BACKUP;
1168 init_jcr_job_record(jcr);
1170 if (!get_or_create_client_record(jcr)) {
1173 if (!get_or_create_fileset_record(jcr)) {
1177 get_level_since_time(ua->jcr, since, sizeof(since));
1179 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1180 jcr->client->name(), jcr->client->address, jcr->client->FDport);
1181 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1182 ua->error_msg(_("Failed to connect to Client.\n"));
1186 if (!send_include_list(jcr)) {
1187 ua->error_msg(_("Error sending include list.\n"));
1191 if (!send_exclude_list(jcr)) {
1192 ua->error_msg(_("Error sending exclude list.\n"));
1196 if (!send_level_command(jcr)) {
1200 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1201 while (bnet_recv(jcr->file_bsock) >= 0) {
1202 ua->send_msg("%s", jcr->file_bsock->msg);
1206 if (jcr->file_bsock) {
1207 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1208 bnet_close(jcr->file_bsock);
1209 jcr->file_bsock = NULL;
1218 static int time_cmd(UAContext *ua, const char *cmd)
1221 time_t ttime = time(NULL);
1223 (void)localtime_r(&ttime, &tm);
1224 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1225 ua->send_msg("%s\n", sdt);
1230 * reload the conf file
1232 extern "C" void reload_config(int sig);
1234 static int reload_cmd(UAContext *ua, const char *cmd)
1241 * Delete Pool records (should purge Media with it).
1243 * delete pool=<pool-name>
1244 * delete volume pool=<pool-name> volume=<name>
1247 static int delete_cmd(UAContext *ua, const char *cmd)
1249 static const char *keywords[] = {
1255 if (!open_client_db(ua)) {
1259 switch (find_arg_keyword(ua, keywords)) {
1268 while ((i=find_arg(ua, "jobid")) > 0) {
1270 *ua->argk[i] = 0; /* zap keyword already visited */
1278 "In general it is not a good idea to delete either a\n"
1279 "Pool or a Volume since they may contain data.\n\n"));
1281 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1292 ua->warning_msg(_("Nothing done.\n"));
1300 * delete_job has been modified to parse JobID lists like the
1302 * delete JobID=3,4,6,7-11,14
1304 * Thanks to Phil Stracchino for the above addition.
1307 static void delete_job(UAContext *ua)
1312 int i = find_arg_with_value(ua, NT_("jobid"));
1314 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1315 s = bstrdup(ua->argv[i]);
1318 * We could use strtok() here. But we're not going to, because:
1319 * (a) strtok() is deprecated, having been replaced by strsep();
1320 * (b) strtok() is broken in significant ways.
1321 * we could use strsep() instead, but it's not universally available.
1322 * so we grow our own using strchr().
1324 sep = strchr(tok, ',');
1325 while (sep != NULL) {
1327 if (strchr(tok, '-')) {
1328 delete_job_id_range(ua, tok);
1330 JobId = str_to_int64(tok);
1331 do_job_delete(ua, JobId);
1334 sep = strchr(tok, ',');
1336 /* pick up the last token */
1337 if (strchr(tok, '-')) {
1338 delete_job_id_range(ua, tok);
1340 JobId = str_to_int64(tok);
1341 do_job_delete(ua, JobId);
1346 JobId = str_to_int64(ua->argv[i]);
1347 do_job_delete(ua, JobId);
1349 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1352 JobId = ua->int64_val;
1353 do_job_delete(ua, JobId);
1358 * we call delete_job_id_range to parse range tokens and iterate over ranges
1360 static void delete_job_id_range(UAContext *ua, char *tok)
1365 tok2 = strchr(tok, '-');
1368 j1 = str_to_int64(tok);
1369 j2 = str_to_int64(tok2);
1370 for (j=j1; j<=j2; j++) {
1371 do_job_delete(ua, j);
1376 * do_job_delete now performs the actual delete operation atomically
1378 static void do_job_delete(UAContext *ua, JobId_t JobId)
1382 edit_int64(JobId, ed1);
1383 purge_jobs_from_catalog(ua, ed1);
1384 ua->send_msg(_("Job %s and associated records deleted from the catalog.\n"), ed1);
1388 * Delete media records from database -- dangerous
1390 static int delete_volume(UAContext *ua)
1395 if (!select_media_dbr(ua, &mr)) {
1398 ua->warning_msg(_("\nThis command will delete volume %s\n"
1399 "and all Jobs saved on that volume from the Catalog\n"),
1402 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Volume \"%s\"? (yes/no): "),
1404 if (!get_yesno(ua, buf)) {
1407 if (ua->pint32_val) {
1408 db_delete_media_record(ua->jcr, ua->db, &mr);
1414 * Delete a pool record from the database -- dangerous
1416 static int delete_pool(UAContext *ua)
1421 memset(&pr, 0, sizeof(pr));
1423 if (!get_pool_dbr(ua, &pr)) {
1426 bsnprintf(buf, sizeof(buf), _("Are you sure you want to delete Pool \"%s\"? (yes/no): "),
1428 if (!get_yesno(ua, buf)) {
1431 if (ua->pint32_val) {
1432 db_delete_pool_record(ua->jcr, ua->db, &pr);
1437 int memory_cmd(UAContext *ua, const char *cmd)
1439 list_dir_status_header(ua);
1440 sm_dump(false, true);
1444 static void do_mount_cmd(UAContext *ua, const char *command)
1449 char dev_name[MAX_NAME_LENGTH];
1453 if (!open_client_db(ua)) {
1456 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1458 store.store = get_storage_resource(ua, true/*arg is storage*/);
1462 pm_strcpy(store.store_source, _("unknown source"));
1463 set_wstorage(jcr, &store);
1464 drive = get_storage_drive(ua, store.store);
1465 if (strcmp(command, "mount") == 0) {
1466 slot = get_storage_slot(ua, store.store);
1469 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1470 store.store->media_type, store.store->dev_name(), drive);
1472 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1473 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1476 sd = jcr->store_bsock;
1477 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1478 bash_spaces(dev_name);
1480 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1482 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1484 while (bnet_recv(sd) >= 0) {
1485 ua->send_msg("%s", sd->msg);
1487 bnet_sig(sd, BNET_TERMINATE);
1489 jcr->store_bsock = NULL;
1493 * mount [storage=<name>] [drive=nn] [slot=mm]
1495 static int mount_cmd(UAContext *ua, const char *cmd)
1497 do_mount_cmd(ua, "mount"); /* mount */
1503 * unmount [storage=<name>] [drive=nn]
1505 static int unmount_cmd(UAContext *ua, const char *cmd)
1507 do_mount_cmd(ua, "unmount"); /* unmount */
1513 * release [storage=<name>] [drive=nn]
1515 static int release_cmd(UAContext *ua, const char *cmd)
1517 do_mount_cmd(ua, "release"); /* release */
1524 * use catalog=<name>
1526 static int use_cmd(UAContext *ua, const char *cmd)
1528 CAT *oldcatalog, *catalog;
1531 close_db(ua); /* close any previously open db */
1532 oldcatalog = ua->catalog;
1534 if (!(catalog = get_catalog_resource(ua))) {
1535 ua->catalog = oldcatalog;
1537 ua->catalog = catalog;
1540 ua->send_msg(_("Using Catalog name=%s DB=%s\n"),
1541 ua->catalog->name(), ua->catalog->db_name);
1546 int quit_cmd(UAContext *ua, const char *cmd)
1552 /* Handler to get job status */
1553 static int status_handler(void *ctx, int num_fields, char **row)
1555 char *val = (char *)ctx;
1560 *val = '?'; /* Unknown by default */
1567 * Wait until no job is running
1569 int wait_cmd(UAContext *ua, const char *cmd)
1575 * Wait until no job is running
1577 if (ua->argc == 1) {
1578 bmicrosleep(0, 200000); /* let job actually start */
1579 for (bool running=true; running; ) {
1582 if (jcr->JobId != 0) {
1596 /* we have jobid, jobname or ujobid argument */
1598 uint32_t jobid = 0 ;
1600 if (!open_client_db(ua)) {
1601 ua->error_msg(_("ERR: Can't open db\n")) ;
1605 for (int i=1; i<ua->argc; i++) {
1606 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1610 jobid = str_to_int64(ua->argv[i]);
1612 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1613 strcasecmp(ua->argk[i], "job") == 0) {
1617 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1619 jobid = jcr->JobId ;
1623 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1627 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1629 jobid = jcr->JobId ;
1637 ua->error_msg(_("ERR: Job was not found\n"));
1642 * We wait the end of a specific job
1645 bmicrosleep(0, 200000); /* let job actually start */
1646 for (bool running=true; running; ) {
1649 jcr=get_jcr_by_id(jobid) ;
1662 * We have to get JobStatus
1666 char jobstatus = '?'; /* Unknown by default */
1669 bsnprintf(buf, sizeof(buf),
1670 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1673 db_sql_query(ua->db, buf,
1674 status_handler, (void *)&jobstatus);
1676 switch (jobstatus) {
1678 status = 1 ; /* Warning */
1682 case JS_ErrorTerminated:
1684 status = 2 ; /* Critical */
1688 status = 0 ; /* Ok */
1692 status = 3 ; /* Unknown */
1696 ua->send_msg("JobId=%i\n", jobid) ;
1697 ua->send_msg("JobStatus=%s (%c)\n",
1698 job_status_to_str(jobstatus),
1701 if (ua->gui || ua->api) {
1702 ua->send_msg("ExitStatus=%i\n", status) ;
1709 static int help_cmd(UAContext *ua, const char *cmd)
1713 ua->send_msg(_(" Command Description\n ======= ===========\n"));
1714 for (i=0; i<comsize; i++) {
1715 ua->send_msg(_(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1717 ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1721 int qhelp_cmd(UAContext *ua, const char *cmd)
1725 for (i=0; i<comsize; i++) {
1726 ua->send_msg("%s %s\n", commands[i].key, _(commands[i].help));
1732 static int version_cmd(UAContext *ua, const char *cmd)
1734 ua->send_msg(_("%s Version: %s (%s) %s %s %s\n"), my_name, VERSION, BDATE,
1735 HOST_OS, DISTNAME, DISTVER);
1740 * Test code -- turned on only for debug testing
1742 static int version_cmd(UAContext *ua, const char *cmd)
1745 POOL_MEM query(PM_MESSAGE);
1747 Mmsg(query, "select MediaId from Media,Pool where Pool.PoolId=Media.PoolId and Pool.Name='Full'");
1748 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1749 ua->send_msg("num_ids=%d max_ids=%d tot_ids=%d\n", ids.num_ids, ids.max_ids, ids.tot_ids);
1750 for (int i=0; i < ids.num_ids; i++) {
1751 ua->send_msg("id=%d\n", ids.DBId[i]);
1759 * This call explicitly checks for a catalog=xxx and
1760 * if given, opens that catalog. It also checks for
1761 * client=xxx and if found, opens the catalog
1762 * corresponding to that client. If we still don't
1763 * have a catalog, look for a Job keyword and get the
1764 * catalog from its client record.
1766 bool open_client_db(UAContext *ua)
1773 /* Try for catalog keyword */
1774 i = find_arg_with_value(ua, NT_("catalog"));
1776 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1777 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1780 catalog = GetCatalogResWithName(ua->argv[i]);
1782 if (ua->catalog && ua->catalog != catalog) {
1785 ua->catalog = catalog;
1790 /* Try for client keyword */
1791 i = find_arg_with_value(ua, NT_("client"));
1793 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1794 ua->error_msg(_("No authorization for Client \"%s\"\n"), ua->argv[i]);
1797 client = GetClientResWithName(ua->argv[i]);
1799 catalog = client->catalog;
1800 if (ua->catalog && ua->catalog != catalog) {
1803 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1804 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1807 ua->catalog = catalog;
1812 /* Try for Job keyword */
1813 i = find_arg_with_value(ua, NT_("job"));
1815 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1816 ua->error_msg(_("No authorization for Job \"%s\"\n"), ua->argv[i]);
1819 job = GetJobResWithName(ua->argv[i]);
1821 catalog = job->client->catalog;
1822 if (ua->catalog && ua->catalog != catalog) {
1825 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1826 ua->error_msg(_("No authorization for Catalog \"%s\"\n"), catalog->name());
1829 ua->catalog = catalog;
1839 * Open the catalog database.
1841 bool open_db(UAContext *ua)
1847 ua->catalog = get_catalog_resource(ua);
1849 ua->error_msg( _("Could not find a Catalog resource\n"));
1854 ua->jcr->catalog = ua->catalog;
1856 Dmsg0(100, "UA Open database\n");
1857 ua->db = db_init(ua->jcr, ua->catalog->db_driver, ua->catalog->db_name,
1858 ua->catalog->db_user,
1859 ua->catalog->db_password, ua->catalog->db_address,
1860 ua->catalog->db_port, ua->catalog->db_socket,
1861 ua->catalog->mult_db_connections);
1862 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1863 ua->error_msg(_("Could not open catalog database \"%s\".\n"),
1864 ua->catalog->db_name);
1866 ua->error_msg("%s", db_strerror(ua->db));
1871 ua->jcr->db = ua->db;
1873 ua->send_msg(_("Using Catalog \"%s\"\n"), ua->catalog->name());
1875 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1879 void close_db(UAContext *ua)
1882 db_close_database(ua->jcr, ua->db);