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 plus additions
11 that are listed in the file LICENSE.
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 status_cmd(UAContext *ua, const char *cmd);
51 extern int list_cmd(UAContext *ua, const char *cmd);
52 extern int llist_cmd(UAContext *ua, const char *cmd);
53 extern int show_cmd(UAContext *ua, const char *cmd);
54 extern int messagescmd(UAContext *ua, const char *cmd);
55 extern int autodisplay_cmd(UAContext *ua, const char *cmd);
56 extern int gui_cmd(UAContext *ua, const char *cmd);
57 extern int sqlquerycmd(UAContext *ua, const char *cmd);
58 extern int querycmd(UAContext *ua, const char *cmd);
59 extern int retentioncmd(UAContext *ua, const char *cmd);
60 extern int prunecmd(UAContext *ua, const char *cmd);
61 extern int purgecmd(UAContext *ua, const char *cmd);
62 extern int restore_cmd(UAContext *ua, const char *cmd);
63 extern int label_cmd(UAContext *ua, const char *cmd);
64 extern int relabel_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 create_cmd(UAContext *ua, const char *cmd);
70 static int cancel_cmd(UAContext *ua, const char *cmd);
71 static int enable_cmd(UAContext *ua, const char *cmd);
72 static int disable_cmd(UAContext *ua, const char *cmd);
73 static int setdebug_cmd(UAContext *ua, const char *cmd);
74 static int trace_cmd(UAContext *ua, const char *cmd);
75 static int var_cmd(UAContext *ua, const char *cmd);
76 static int estimate_cmd(UAContext *ua, const char *cmd);
77 static int help_cmd(UAContext *ua, const char *cmd);
78 static int delete_cmd(UAContext *ua, const char *cmd);
79 static int use_cmd(UAContext *ua, const char *cmd);
80 static int unmount_cmd(UAContext *ua, const char *cmd);
81 static int version_cmd(UAContext *ua, const char *cmd);
82 static int automount_cmd(UAContext *ua, const char *cmd);
83 static int time_cmd(UAContext *ua, const char *cmd);
84 static int reload_cmd(UAContext *ua, const char *cmd);
85 static int delete_volume(UAContext *ua);
86 static int delete_pool(UAContext *ua);
87 static void delete_job(UAContext *ua);
88 static int mount_cmd(UAContext *ua, const char *cmd);
89 static int release_cmd(UAContext *ua, const char *cmd);
90 static int wait_cmd(UAContext *ua, const char *cmd);
91 static int setip_cmd(UAContext *ua, const char *cmd);
92 static int python_cmd(UAContext *ua, const char *cmd);
93 static void do_job_delete(UAContext *ua, JobId_t JobId);
94 static void delete_job_id_range(UAContext *ua, char *tok);
96 int qhelp_cmd(UAContext *ua, const char *cmd);
97 int quit_cmd(UAContext *ua, const char *cmd);
100 struct cmdstruct { const char *key; int (*func)(UAContext *ua, const char *cmd); const char *help; };
101 static struct cmdstruct commands[] = {
102 { NT_("add"), add_cmd, _("add media to a pool")},
103 { NT_("autodisplay"), autodisplay_cmd, _("autodisplay [on|off] -- console messages")},
104 { NT_("automount"), automount_cmd, _("automount [on|off] -- after label")},
105 { NT_("cancel"), cancel_cmd, _("cancel [<jobid=nnn> | <job=name>] -- cancel a job")},
106 { NT_("create"), create_cmd, _("create DB Pool from resource")},
107 { NT_("delete"), delete_cmd, _("delete [pool=<pool-name> | media volume=<volume-name>]")},
108 { NT_("disable"), disable_cmd, _("disable <job=name> -- disable a job")},
109 { NT_("enable"), enable_cmd, _("enable <job=name> -- enable a job")},
110 { NT_("estimate"), estimate_cmd, _("performs FileSet estimate, listing gives full listing")},
111 { NT_("exit"), quit_cmd, _("exit = quit")},
112 { NT_("gui"), gui_cmd, _("gui [on|off] -- non-interactive gui mode")},
113 { NT_("help"), help_cmd, _("print this command")},
114 { NT_("list"), list_cmd, _("list [pools | jobs | jobtotals | media <pool=pool-name> | files <jobid=nn>]; from catalog")},
115 { NT_("label"), label_cmd, _("label a tape")},
116 { NT_("llist"), llist_cmd, _("full or long list like list command")},
117 { NT_("messages"), messagescmd, _("messages")},
118 { NT_("mount"), mount_cmd, _("mount <storage-name>")},
119 { NT_("prune"), prunecmd, _("prune expired records from catalog")},
120 { NT_("purge"), purgecmd, _("purge records from catalog")},
121 { NT_("python"), python_cmd, _("python control commands")},
122 { NT_("quit"), quit_cmd, _("quit")},
123 { NT_("query"), querycmd, _("query catalog")},
124 { NT_("restore"), restore_cmd, _("restore files")},
125 { NT_("relabel"), relabel_cmd, _("relabel a tape")},
126 { NT_("release"), release_cmd, _("release <storage-name>")},
127 { NT_("reload"), reload_cmd, _("reload conf file")},
128 { NT_("run"), run_cmd, _("run <job-name>")},
129 { NT_("status"), status_cmd, _("status [storage | client]=<name>")},
130 { NT_("setdebug"), setdebug_cmd, _("sets debug level")},
131 { NT_("setip"), setip_cmd, _("sets new client address -- if authorized")},
132 { NT_("show"), show_cmd, _("show (resource records) [jobs | pools | ... | all]")},
133 { NT_("sqlquery"), sqlquerycmd, _("use SQL to query catalog")},
134 { NT_("time"), time_cmd, _("print current time")},
135 { NT_("trace"), trace_cmd, _("turn on/off trace to file")},
136 { NT_("unmount"), unmount_cmd, _("unmount <storage-name>")},
137 { NT_("umount"), unmount_cmd, _("umount <storage-name> for old-time Unix guys")},
138 { NT_("update"), update_cmd, _("update Volume, Pool or slots")},
139 { NT_("use"), use_cmd, _("use catalog xxx")},
140 { NT_("var"), var_cmd, _("does variable expansion")},
141 { NT_("version"), version_cmd, _("print Director version")},
142 { NT_("wait"), wait_cmd, _("wait until no jobs are running [<jobname=name> | <jobid=nnn> | <ujobid=complete_name>]")},
144 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
147 * Execute a command from the UA
149 int do_a_command(UAContext *ua, const char *cmd)
157 Dmsg1(900, "Command: %s\n", ua->UA_sock->msg);
162 while (ua->jcr->wstorage->size()) {
163 ua->jcr->wstorage->remove(0);
166 len = strlen(ua->argk[0]);
167 for (i=0; i<comsize; i++) { /* search for command */
168 if (strncasecmp(ua->argk[0], commands[i].key, len) == 0) {
169 /* Check if command permitted, but "quit" is always OK */
170 if (strcmp(ua->argk[0], NT_("quit")) != 0 &&
171 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
174 stat = (*commands[i].func)(ua, cmd); /* go execute command */
180 bnet_fsend(ua->UA_sock, _("%s: is an invalid command.\n"), ua->argk[0]);
186 * This is a common routine used to stuff the Pool DB record defaults
187 * into the Media DB record just before creating a media (Volume)
190 void set_pool_dbr_defaults_in_media_dbr(MEDIA_DBR *mr, POOL_DBR *pr)
192 mr->PoolId = pr->PoolId;
193 bstrncpy(mr->VolStatus, NT_("Append"), sizeof(mr->VolStatus));
194 mr->Recycle = pr->Recycle;
195 mr->VolRetention = pr->VolRetention;
196 mr->VolUseDuration = pr->VolUseDuration;
197 mr->RecyclePoolId = pr->RecyclePoolId;
198 mr->MaxVolJobs = pr->MaxVolJobs;
199 mr->MaxVolFiles = pr->MaxVolFiles;
200 mr->MaxVolBytes = pr->MaxVolBytes;
201 mr->LabelType = pr->LabelType;
207 * Add Volumes to an existing Pool
209 static int add_cmd(UAContext *ua, const char *cmd)
213 int num, i, max, startnum;
215 char name[MAX_NAME_LENGTH];
217 int Slot = 0, InChanger = 0;
220 "You probably don't want to be using this command since it\n"
221 "creates database records without labeling the Volumes.\n"
222 "You probably want to use the \"label\" command.\n\n"));
224 if (!open_client_db(ua)) {
228 memset(&pr, 0, sizeof(pr));
229 memset(&mr, 0, sizeof(mr));
231 if (!get_pool_dbr(ua, &pr)) {
235 Dmsg4(120, "id=%d Num=%d Max=%d type=%s\n", pr.PoolId, pr.NumVols,
236 pr.MaxVols, pr.PoolType);
238 while (pr.MaxVols > 0 && pr.NumVols >= pr.MaxVols) {
239 bsendmsg(ua, _("Pool already has maximum volumes=%d\n"), pr.MaxVols);
241 if (!get_pint(ua, _("Enter new maximum (zero for unlimited): "))) {
244 pr.MaxVols = ua->pint32_val;
249 if ((store = get_storage_resource(ua, false/*no default*/)) != NULL) {
250 bstrncpy(mr.MediaType, store->media_type, sizeof(mr.MediaType));
251 } else if (!get_media_type(ua, mr.MediaType, sizeof(mr.MediaType))) {
255 if (pr.MaxVols == 0) {
258 max = pr.MaxVols - pr.NumVols;
262 bsnprintf(buf, sizeof(buf), _("Enter number of Volumes to create. 0=>fixed name. Max=%d: "), max);
263 if (!get_pint(ua, buf)) {
266 num = ua->pint32_val;
267 if (num < 0 || num > max) {
268 bsendmsg(ua, _("The number must be between 0 and %d\n"), max);
275 if (!get_cmd(ua, _("Enter Volume name: "))) {
279 if (!get_cmd(ua, _("Enter base volume name: "))) {
283 /* Don't allow | in Volume name because it is the volume separator character */
284 if (!is_volume_name_legal(ua, ua->cmd)) {
287 if (strlen(ua->cmd) >= MAX_NAME_LENGTH-10) {
288 bsendmsg(ua, _("Volume name too long.\n"));
291 if (strlen(ua->cmd) == 0) {
292 bsendmsg(ua, _("Volume name must be at least one character long.\n"));
296 bstrncpy(name, ua->cmd, sizeof(name));
298 bstrncat(name, "%04d", sizeof(name));
301 if (!get_pint(ua, _("Enter the starting number: "))) {
304 startnum = ua->pint32_val;
306 bsendmsg(ua, _("Start number must be greater than zero.\n"));
316 if (store && store->autochanger) {
317 if (!get_pint(ua, _("Enter slot (0 for none): "))) {
320 Slot = ua->pint32_val;
321 if (!get_yesno(ua, _("InChanger? yes/no: "))) {
324 InChanger = ua->pint32_val;
327 set_pool_dbr_defaults_in_media_dbr(&mr, &pr);
328 for (i=startnum; i < num+startnum; i++) {
329 bsnprintf(mr.VolumeName, sizeof(mr.VolumeName), name, i);
331 mr.InChanger = InChanger;
332 mr.StorageId = store->StorageId;
334 Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
335 if (!db_create_media_record(ua->jcr, ua->db, &mr)) {
336 bsendmsg(ua, "%s", db_strerror(ua->db));
340 first_id = mr.PoolId;
344 Dmsg0(200, "Update pool record.\n");
345 if (db_update_pool_record(ua->jcr, ua->db, &pr) != 1) {
346 bsendmsg(ua, "%s", db_strerror(ua->db));
349 bsendmsg(ua, _("%d Volumes created in pool %s\n"), num, pr.Name);
355 * Turn auto mount on/off
360 int automount_cmd(UAContext *ua, const char *cmd)
365 if (!get_cmd(ua, _("Turn on or off? "))) {
373 ua->automount = (strcasecmp(onoff, NT_("off")) == 0) ? 0 : 1;
381 static int cancel_cmd(UAContext *ua, const char *cmd)
386 char JobName[MAX_NAME_LENGTH];
388 for (i=1; i<ua->argc; i++) {
389 if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
394 JobId = str_to_int64(ua->argv[i]);
395 if (!(jcr=get_jcr_by_id(JobId))) {
396 bsendmsg(ua, _("JobId %s is not running. Use Job name to cancel inactive jobs.\n"), ua->argv[i]);
400 } else if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
404 if (!(jcr=get_jcr_by_partial_name(ua->argv[i]))) {
405 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
406 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
407 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
410 } else if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
414 if (!(jcr=get_jcr_by_full_name(ua->argv[i]))) {
415 bsendmsg(ua, _("Warning Job %s is not running. Continuing anyway ...\n"), ua->argv[i]);
416 jcr = new_jcr(sizeof(JCR), dird_free_jcr);
417 bstrncpy(jcr->Job, ua->argv[i], sizeof(jcr->Job));
424 if (jcr->job && !acl_access_ok(ua, Job_ACL, jcr->job->name())) {
425 bsendmsg(ua, _("Unauthorized command from this console.\n"));
430 * If we still do not have a jcr,
431 * throw up a list and ask the user to select one.
434 int tjobs = 0; /* total # number jobs */
435 /* Count Jobs running */
437 if (jcr->JobId == 0) { /* this is us */
440 tjobs++; /* count of all jobs */
441 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
442 continue; /* skip not authorized */
444 njobs++; /* count of authorized jobs */
448 if (njobs == 0) { /* no authorized */
450 bsendmsg(ua, _("No Jobs running.\n"));
452 bsendmsg(ua, _("None of your jobs are running.\n"));
457 start_prompt(ua, _("Select Job:\n"));
460 if (jcr->JobId == 0) { /* this is us */
463 if (!acl_access_ok(ua, Job_ACL, jcr->job->name())) {
464 continue; /* skip not authorized */
466 bsnprintf(buf, sizeof(buf), _("JobId=%s Job=%s"), edit_int64(jcr->JobId, ed1), jcr->Job);
471 if (do_prompt(ua, _("Job"), _("Choose Job to cancel"), buf, sizeof(buf)) < 0) {
475 if (!get_yesno(ua, _("Confirm cancel (yes/no): ")) || ua->pint32_val == 0) {
479 sscanf(buf, "JobId=%d Job=%127s", &njobs, JobName);
480 jcr = get_jcr_by_full_name(JobName);
482 bsendmsg(ua, _("Job \"%s\" not found.\n"), JobName);
487 ret = cancel_job(ua, jcr);
493 * This is a common routine to create or update a
494 * Pool DB base record from a Pool Resource. We handle
495 * the setting of MaxVols and NumVols slightly differently
496 * depending on if we are creating the Pool or we are
497 * simply bringing it into agreement with the resource (updage).
499 * Caution : RecyclePoolId isn't setup in this function.
500 * You can use set_pooldbr_recyclepoolid();
503 void set_pooldbr_from_poolres(POOL_DBR *pr, POOL *pool, e_pool_op op)
505 bstrncpy(pr->PoolType, pool->pool_type, sizeof(pr->PoolType));
506 if (op == POOL_OP_CREATE) {
507 pr->MaxVols = pool->max_volumes;
509 } else { /* update pool */
510 if (pr->MaxVols != pool->max_volumes) {
511 pr->MaxVols = pool->max_volumes;
513 if (pr->MaxVols != 0 && pr->MaxVols < pr->NumVols) {
514 pr->MaxVols = pr->NumVols;
517 pr->LabelType = pool->LabelType;
518 pr->UseOnce = pool->use_volume_once;
519 pr->UseCatalog = pool->use_catalog;
520 pr->Recycle = pool->Recycle;
521 pr->VolRetention = pool->VolRetention;
522 pr->VolUseDuration = pool->VolUseDuration;
523 pr->MaxVolJobs = pool->MaxVolJobs;
524 pr->MaxVolFiles = pool->MaxVolFiles;
525 pr->MaxVolBytes = pool->MaxVolBytes;
526 pr->AutoPrune = pool->AutoPrune;
527 pr->Recycle = pool->Recycle;
528 if (pool->label_format) {
529 bstrncpy(pr->LabelFormat, pool->label_format, sizeof(pr->LabelFormat));
531 bstrncpy(pr->LabelFormat, "*", sizeof(pr->LabelFormat)); /* none */
535 /* set/update Pool.RecyclePoolId in Catalog */
536 int update_pool_recyclepool(JCR *jcr, B_DB *db, POOL *pool)
540 if (!pool->RecyclePool) {
544 memset(&pr, 0, sizeof(POOL_DBR));
545 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
547 if (!db_get_pool_record(jcr, db, &pr)) {
548 return -1; /* not exists in database */
551 set_pooldbr_from_poolres(&pr, pool, POOL_OP_UPDATE);
553 if (!set_pooldbr_recyclepoolid(jcr, db, &pr, pool)) {
554 return -1; /* error */
557 if (!db_update_pool_record(jcr, db, &pr)) {
558 return -1; /* error */
563 /* set POOL_DBR.RecyclePoolId from Pool resource
564 * works with set_pooldbr_from_poolres
566 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
571 if (pool->RecyclePool) {
572 memset(&rpool, 0, sizeof(POOL_DBR));
574 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
575 if (db_get_pool_record(jcr, db, &rpool)) {
576 pr->RecyclePoolId = rpool.PoolId;
578 Jmsg(jcr, M_WARNING, 0,
579 _("Can't set %s RecyclePool to %s, %s is not in database.\n" \
580 "Try to update it with 'update pool=%s'\n"),
581 pool->name(), rpool.Name, rpool.Name,pool->name());
585 } else { /* no RecyclePool used, set it to 0 */
586 pr->RecyclePoolId = 0;
593 * Create a pool record from a given Pool resource
594 * Also called from backup.c
595 * Returns: -1 on error
596 * 0 record already exists
600 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
604 memset(&pr, 0, sizeof(POOL_DBR));
606 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
608 if (db_get_pool_record(jcr, db, &pr)) {
610 if (op == POOL_OP_UPDATE) { /* update request */
611 set_pooldbr_from_poolres(&pr, pool, op);
612 db_update_pool_record(jcr, db, &pr);
614 return 0; /* exists */
617 set_pooldbr_from_poolres(&pr, pool, op);
619 if (!db_create_pool_record(jcr, db, &pr)) {
620 return -1; /* error */
628 * Create a Pool Record in the database.
629 * It is always created from the Resource record.
631 static int create_cmd(UAContext *ua, const char *cmd)
635 if (!open_client_db(ua)) {
639 pool = get_pool_resource(ua);
644 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
646 bsendmsg(ua, _("Error: Pool %s already exists.\n"
647 "Use update to change it.\n"), pool->name());
651 bsendmsg(ua, "%s", db_strerror(ua->db));
657 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
662 extern DIRRES *director;
665 * Python control command
666 * python restart (restarts interpreter)
668 static int python_cmd(UAContext *ua, const char *cmd)
670 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
671 term_python_interpreter();
672 init_python_interpreter(director->name(),
673 director->scripts_directory, "DirStartUp");
674 bsendmsg(ua, _("Python interpreter restarted.\n"));
676 bsendmsg(ua, _("Nothing done.\n"));
683 * Set a new address in a Client resource. We do this only
684 * if the Console name is the same as the Client name
685 * and the Console can access the client.
687 static int setip_cmd(UAContext *ua, const char *cmd)
691 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
692 bsendmsg(ua, _("Unauthorized command from this console.\n"));
696 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
699 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
702 if (client->address) {
703 free(client->address);
705 /* MA Bug 6 remove ifdef */
706 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
707 client->address = bstrdup(buf);
708 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
709 client->name(), client->address);
716 static void do_en_disable_cmd(UAContext *ua, bool setting)
721 i = find_arg_with_value(ua, NT_("job"));
723 job = select_job_resource(ua);
729 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
733 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
737 if (!acl_access_ok(ua, Job_ACL, job->name())) {
738 bsendmsg(ua, _("Unauthorized command from this console.\n"));
741 job->enabled = setting;
742 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
746 static int enable_cmd(UAContext *ua, const char *cmd)
748 do_en_disable_cmd(ua, true);
752 static int disable_cmd(UAContext *ua, const char *cmd)
754 do_en_disable_cmd(ua, false);
759 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
765 lstore.store = store;
766 pm_strcpy(lstore.store_source, _("unknown source"));
767 set_wstorage(jcr, &lstore);
768 /* Try connecting for up to 15 seconds */
769 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
770 store->name(), store->address, store->SDport);
771 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
772 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
775 Dmsg0(120, _("Connected to storage daemon\n"));
776 sd = jcr->store_bsock;
777 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
778 if (bnet_recv(sd) >= 0) {
779 bsendmsg(ua, "%s", sd->msg);
781 bnet_sig(sd, BNET_TERMINATE);
783 jcr->store_bsock = NULL;
787 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
791 /* Connect to File daemon */
793 ua->jcr->client = client;
794 /* Try to connect for 15 seconds */
795 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
796 client->name(), client->address, client->FDport);
797 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
798 bsendmsg(ua, _("Failed to connect to Client.\n"));
801 Dmsg0(120, "Connected to file daemon\n");
802 fd = ua->jcr->file_bsock;
803 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
804 if (bnet_recv(fd) >= 0) {
805 bsendmsg(ua, "%s", fd->msg);
807 bnet_sig(fd, BNET_TERMINATE);
809 ua->jcr->file_bsock = NULL;
814 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
816 STORE *store, **unique_store;
817 CLIENT *client, **unique_client;
823 /* Count Storage items */
827 foreach_res(store, R_STORAGE) {
830 unique_store = (STORE **) malloc(i * sizeof(STORE));
831 /* Find Unique Storage address/port */
832 store = (STORE *)GetNextRes(R_STORAGE, NULL);
834 unique_store[i++] = store;
835 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
837 for (j=0; j<i; j++) {
838 if (strcmp(unique_store[j]->address, store->address) == 0 &&
839 unique_store[j]->SDport == store->SDport) {
845 unique_store[i++] = store;
846 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
851 /* Call each unique Storage daemon */
852 for (j=0; j<i; j++) {
853 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
857 /* Count Client items */
861 foreach_res(client, R_CLIENT) {
864 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
865 /* Find Unique Client address/port */
866 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
868 unique_client[i++] = client;
869 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
871 for (j=0; j<i; j++) {
872 if (strcmp(unique_client[j]->address, client->address) == 0 &&
873 unique_client[j]->FDport == client->FDport) {
879 unique_client[i++] = client;
880 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
885 /* Call each unique File daemon */
886 for (j=0; j<i; j++) {
887 do_client_setdebug(ua, unique_client[j], level, trace_flag);
893 * setdebug level=nn all trace=1/0
895 static int setdebug_cmd(UAContext *ua, const char *cmd)
903 if (!open_client_db(ua)) {
906 Dmsg1(120, "setdebug:%s:\n", cmd);
909 i = find_arg_with_value(ua, "level");
911 level = atoi(ua->argv[i]);
914 if (!get_pint(ua, _("Enter new debug level: "))) {
917 level = ua->pint32_val;
920 /* Look for trace flag. -1 => not change */
921 i = find_arg_with_value(ua, "trace");
923 trace_flag = atoi(ua->argv[i]);
924 if (trace_flag > 0) {
930 for (i=1; i<ua->argc; i++) {
931 if (strcasecmp(ua->argk[i], "all") == 0) {
932 do_all_setdebug(ua, level, trace_flag);
935 if (strcasecmp(ua->argk[i], "dir") == 0 ||
936 strcasecmp(ua->argk[i], "director") == 0) {
938 set_trace(trace_flag);
941 if (strcasecmp(ua->argk[i], "client") == 0 ||
942 strcasecmp(ua->argk[i], "fd") == 0) {
945 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
947 do_client_setdebug(ua, client, level, trace_flag);
951 client = select_client_resource(ua);
953 do_client_setdebug(ua, client, level, trace_flag);
958 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
959 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
960 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
963 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
965 do_storage_setdebug(ua, store, level, trace_flag);
969 store = get_storage_resource(ua, false/*no default*/);
971 do_storage_setdebug(ua, store, level, trace_flag);
977 * We didn't find an appropriate keyword above, so
980 start_prompt(ua, _("Available daemons are: \n"));
981 add_prompt(ua, _("Director"));
982 add_prompt(ua, _("Storage"));
983 add_prompt(ua, _("Client"));
984 add_prompt(ua, _("All"));
985 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
986 case 0: /* Director */
988 set_trace(trace_flag);
991 store = get_storage_resource(ua, false/*no default*/);
993 do_storage_setdebug(ua, store, level, trace_flag);
997 client = select_client_resource(ua);
999 do_client_setdebug(ua, client, level, trace_flag);
1003 do_all_setdebug(ua, level, trace_flag);
1012 * Turn debug tracing to file on/off
1014 static int trace_cmd(UAContext *ua, const char *cmd)
1018 if (ua->argc != 2) {
1019 if (!get_cmd(ua, _("Turn on or off? "))) {
1024 onoff = ua->argk[1];
1027 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1032 static int var_cmd(UAContext *ua, const char *cmd)
1034 POOLMEM *val = get_pool_memory(PM_FNAME);
1037 if (!open_client_db(ua)) {
1040 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1043 while (*var == ' ') { /* skip spaces */
1046 Dmsg1(100, "Var=%s:\n", var);
1047 variable_expansion(ua->jcr, var, &val);
1048 bsendmsg(ua, "%s\n", val);
1049 free_pool_memory(val);
1053 static int estimate_cmd(UAContext *ua, const char *cmd)
1056 CLIENT *client = NULL;
1057 FILESET *fileset = NULL;
1059 char since[MAXSTRING];
1062 jcr->JobLevel = L_FULL;
1063 for (int i=1; i<ua->argc; i++) {
1064 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1065 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1067 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1071 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1073 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1074 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1075 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1081 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1083 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1084 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1085 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1091 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1095 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1096 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1097 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1102 if (!job && !(client && fileset)) {
1103 if (!(job = select_job_resource(ua))) {
1108 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1110 bsendmsg(ua, _("No job specified.\n"));
1113 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1114 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1119 client = job->client;
1122 fileset = job->fileset;
1124 jcr->client = client;
1125 jcr->fileset = fileset;
1127 ua->catalog = client->catalog;
1134 jcr->JobType = JT_BACKUP;
1135 init_jcr_job_record(jcr);
1137 if (!get_or_create_client_record(jcr)) {
1140 if (!get_or_create_fileset_record(jcr)) {
1144 get_level_since_time(ua->jcr, since, sizeof(since));
1146 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1147 job->client->name(), job->client->address, job->client->FDport);
1148 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1149 bsendmsg(ua, _("Failed to connect to Client.\n"));
1153 if (!send_include_list(jcr)) {
1154 bsendmsg(ua, _("Error sending include list.\n"));
1158 if (!send_exclude_list(jcr)) {
1159 bsendmsg(ua, _("Error sending exclude list.\n"));
1163 if (!send_level_command(jcr)) {
1167 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1168 while (bnet_recv(jcr->file_bsock) >= 0) {
1169 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1173 if (jcr->file_bsock) {
1174 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1175 bnet_close(jcr->file_bsock);
1176 jcr->file_bsock = NULL;
1185 static int time_cmd(UAContext *ua, const char *cmd)
1188 time_t ttime = time(NULL);
1190 (void)localtime_r(&ttime, &tm);
1191 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1192 bsendmsg(ua, "%s\n", sdt);
1197 * reload the conf file
1199 extern "C" void reload_config(int sig);
1201 static int reload_cmd(UAContext *ua, const char *cmd)
1208 * Delete Pool records (should purge Media with it).
1210 * delete pool=<pool-name>
1211 * delete volume pool=<pool-name> volume=<name>
1214 static int delete_cmd(UAContext *ua, const char *cmd)
1216 static const char *keywords[] = {
1222 if (!open_client_db(ua)) {
1226 switch (find_arg_keyword(ua, keywords)) {
1235 while ((i=find_arg(ua, "jobid")) > 0) {
1237 *ua->argk[i] = 0; /* zap keyword already visited */
1245 "In general it is not a good idea to delete either a\n"
1246 "Pool or a Volume since they may contain data.\n\n"));
1248 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1259 bsendmsg(ua, _("Nothing done.\n"));
1267 * delete_job has been modified to parse JobID lists like the
1269 * delete JobID=3,4,6,7-11,14
1271 * Thanks to Phil Stracchino for the above addition.
1274 static void delete_job(UAContext *ua)
1279 int i = find_arg_with_value(ua, NT_("jobid"));
1281 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1282 s = bstrdup(ua->argv[i]);
1285 * We could use strtok() here. But we're not going to, because:
1286 * (a) strtok() is deprecated, having been replaced by strsep();
1287 * (b) strtok() is broken in significant ways.
1288 * we could use strsep() instead, but it's not universally available.
1289 * so we grow our own using strchr().
1291 sep = strchr(tok, ',');
1292 while (sep != NULL) {
1294 if (strchr(tok, '-')) {
1295 delete_job_id_range(ua, tok);
1297 JobId = str_to_int64(tok);
1298 do_job_delete(ua, JobId);
1301 sep = strchr(tok, ',');
1303 /* pick up the last token */
1304 if (strchr(tok, '-')) {
1305 delete_job_id_range(ua, tok);
1307 JobId = str_to_int64(tok);
1308 do_job_delete(ua, JobId);
1313 JobId = str_to_int64(ua->argv[i]);
1314 do_job_delete(ua, JobId);
1316 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1319 JobId = ua->int64_val;
1320 do_job_delete(ua, JobId);
1325 * we call delete_job_id_range to parse range tokens and iterate over ranges
1327 static void delete_job_id_range(UAContext *ua, char *tok)
1332 tok2 = strchr(tok, '-');
1335 j1 = str_to_int64(tok);
1336 j2 = str_to_int64(tok2);
1337 for (j=j1; j<=j2; j++) {
1338 do_job_delete(ua, j);
1343 * do_job_delete now performs the actual delete operation atomically
1346 static void do_job_delete(UAContext *ua, JobId_t JobId)
1348 POOL_MEM query(PM_MESSAGE);
1351 purge_files_from_job(ua, JobId);
1352 purge_job_from_catalog(ua, JobId);
1353 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1357 * Delete media records from database -- dangerous
1359 static int delete_volume(UAContext *ua)
1363 if (!select_media_dbr(ua, &mr)) {
1366 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1367 "and all Jobs saved on that volume from the Catalog\n"),
1370 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1373 if (ua->pint32_val) {
1374 db_delete_media_record(ua->jcr, ua->db, &mr);
1380 * Delete a pool record from the database -- dangerous
1382 static int delete_pool(UAContext *ua)
1386 memset(&pr, 0, sizeof(pr));
1388 if (!get_pool_dbr(ua, &pr)) {
1391 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1394 if (ua->pint32_val) {
1395 db_delete_pool_record(ua->jcr, ua->db, &pr);
1401 static void do_mount_cmd(UAContext *ua, const char *command)
1406 char dev_name[MAX_NAME_LENGTH];
1410 if (!open_client_db(ua)) {
1413 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1415 store.store = get_storage_resource(ua, true/*arg is storage*/);
1416 pm_strcpy(store.store_source, _("unknown source"));
1420 set_wstorage(jcr, &store);
1421 drive = get_storage_drive(ua, store.store);
1422 if (strcmp(command, "mount") == 0) {
1423 slot = get_storage_slot(ua, store.store);
1426 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1427 store.store->media_type, store.store->dev_name(), drive);
1429 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1430 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1433 sd = jcr->store_bsock;
1434 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1435 bash_spaces(dev_name);
1437 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1439 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1441 while (bnet_recv(sd) >= 0) {
1442 bsendmsg(ua, "%s", sd->msg);
1444 bnet_sig(sd, BNET_TERMINATE);
1446 jcr->store_bsock = NULL;
1450 * mount [storage=<name>] [drive=nn] [slot=mm]
1452 static int mount_cmd(UAContext *ua, const char *cmd)
1454 do_mount_cmd(ua, "mount"); /* mount */
1460 * unmount [storage=<name>] [drive=nn]
1462 static int unmount_cmd(UAContext *ua, const char *cmd)
1464 do_mount_cmd(ua, "unmount"); /* unmount */
1470 * release [storage=<name>] [drive=nn]
1472 static int release_cmd(UAContext *ua, const char *cmd)
1474 do_mount_cmd(ua, "release"); /* release */
1481 * use catalog=<name>
1483 static int use_cmd(UAContext *ua, const char *cmd)
1485 CAT *oldcatalog, *catalog;
1488 close_db(ua); /* close any previously open db */
1489 oldcatalog = ua->catalog;
1491 if (!(catalog = get_catalog_resource(ua))) {
1492 ua->catalog = oldcatalog;
1494 ua->catalog = catalog;
1497 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1498 ua->catalog->name(), ua->catalog->db_name);
1503 int quit_cmd(UAContext *ua, const char *cmd)
1509 /* Handler to get job status */
1510 static int status_handler(void *ctx, int num_fields, char **row)
1512 char *val = (char *)ctx;
1517 *val = '?'; /* Unknown by default */
1524 * Wait until no job is running
1526 int wait_cmd(UAContext *ua, const char *cmd)
1531 * Wait until no job is running
1533 if (ua->argc == 1) {
1534 bmicrosleep(0, 200000); /* let job actually start */
1535 for (bool running=true; running; ) {
1538 if (jcr->JobId != 0) {
1552 /* we have jobid, jobname or ujobid argument */
1554 uint32_t jobid = 0 ;
1556 if (!open_client_db(ua)) {
1557 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1561 for (int i=1; i<ua->argc; i++) {
1562 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1566 jobid = str_to_int64(ua->argv[i]);
1568 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1569 strcasecmp(ua->argk[i], "job") == 0) {
1573 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1575 jobid = jcr->JobId ;
1579 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1583 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1585 jobid = jcr->JobId ;
1593 bsendmsg(ua, _("ERR: Job was not found\n"));
1598 * We wait the end of job
1601 bmicrosleep(0, 200000); /* let job actually start */
1602 for (bool running=true; running; ) {
1605 jcr=get_jcr_by_id(jobid) ;
1618 * We have to get JobStatus
1622 char jobstatus = '?'; /* Unknown by default */
1625 bsnprintf(buf, sizeof(buf),
1626 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1629 db_sql_query(ua->db, buf,
1630 status_handler, (void *)&jobstatus);
1632 switch (jobstatus) {
1634 status = 1 ; /* Warning */
1638 case JS_ErrorTerminated:
1640 status = 2 ; /* Critical */
1644 status = 0 ; /* Ok */
1648 status = 3 ; /* Unknown */
1652 bsendmsg(ua, "JobId=%i\n", jobid) ;
1653 bsendmsg(ua, "JobStatus=%s (%c)\n",
1654 job_status_to_str(jobstatus),
1658 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1665 static int help_cmd(UAContext *ua, const char *cmd)
1669 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1670 for (i=0; i<comsize; i++) {
1671 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1673 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1677 int qhelp_cmd(UAContext *ua, const char *cmd)
1681 for (i=0; i<comsize; i++) {
1682 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1687 static int version_cmd(UAContext *ua, const char *cmd)
1689 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1694 * This call explicitly checks for a catalog=xxx and
1695 * if given, opens that catalog. It also checks for
1696 * client=xxx and if found, opens the catalog
1697 * corresponding to that client. If we still don't
1698 * have a catalog, look for a Job keyword and get the
1699 * catalog from its client record.
1701 bool open_client_db(UAContext *ua)
1708 /* Try for catalog keyword */
1709 i = find_arg_with_value(ua, NT_("catalog"));
1711 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1712 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1715 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1717 if (ua->catalog && ua->catalog != catalog) {
1720 ua->catalog = catalog;
1725 /* Try for client keyword */
1726 i = find_arg_with_value(ua, NT_("client"));
1728 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1729 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1732 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1734 catalog = client->catalog;
1735 if (ua->catalog && ua->catalog != catalog) {
1738 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1739 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1742 ua->catalog = catalog;
1747 /* Try for Job keyword */
1748 i = find_arg_with_value(ua, NT_("job"));
1750 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1751 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1754 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1756 catalog = job->client->catalog;
1757 if (ua->catalog && ua->catalog != catalog) {
1760 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1761 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1764 ua->catalog = catalog;
1774 * Open the catalog database.
1776 bool open_db(UAContext *ua)
1782 ua->catalog = get_catalog_resource(ua);
1784 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1789 ua->jcr->catalog = ua->catalog;
1791 Dmsg0(150, "Open database\n");
1792 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1793 ua->catalog->db_password, ua->catalog->db_address,
1794 ua->catalog->db_port, ua->catalog->db_socket,
1795 ua->catalog->mult_db_connections);
1796 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1797 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1798 ua->catalog->db_name);
1800 bsendmsg(ua, "%s", db_strerror(ua->db));
1805 ua->jcr->db = ua->db;
1806 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1807 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1811 void close_db(UAContext *ua)
1814 db_close_database(ua->jcr, ua->db);