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 bool set_pooldbr_recyclepoolid(JCR *jcr, B_DB *db, POOL_DBR *pr, POOL *pool)
540 if (pool->RecyclePool) {
541 memset(&rpool, 0, sizeof(POOL_DBR));
543 bstrncpy(rpool.Name, pool->RecyclePool->name(), sizeof(rpool.Name));
544 if (db_get_pool_record(jcr, db, &rpool)) {
545 pr->RecyclePoolId = rpool.PoolId;
547 Jmsg(jcr, M_WARNING, 0,
548 _("Can't set %s RecyclePool to %s, %s is not in database, try to update it with 'update pool=%s'\n"),pool->name(),rpool.Name, rpool.Name,pool->name());
552 } else { /* no RecyclePool used, set it to 0 */
553 pr->RecyclePoolId = 0;
560 * Create a pool record from a given Pool resource
561 * Also called from backup.c
562 * Returns: -1 on error
563 * 0 record already exists
567 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
571 memset(&pr, 0, sizeof(POOL_DBR));
573 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
575 if (db_get_pool_record(jcr, db, &pr)) {
577 if (op == POOL_OP_UPDATE) { /* update request */
578 set_pooldbr_from_poolres(&pr, pool, op);
579 db_update_pool_record(jcr, db, &pr);
581 return 0; /* exists */
584 set_pooldbr_from_poolres(&pr, pool, op);
585 set_pooldbr_recyclepoolid(jcr, db, &pr, pool);
587 if (!db_create_pool_record(jcr, db, &pr)) {
588 return -1; /* error */
596 * Create a Pool Record in the database.
597 * It is always created from the Resource record.
599 static int create_cmd(UAContext *ua, const char *cmd)
603 if (!open_client_db(ua)) {
607 pool = get_pool_resource(ua);
612 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
614 bsendmsg(ua, _("Error: Pool %s already exists.\n"
615 "Use update to change it.\n"), pool->name());
619 bsendmsg(ua, "%s", db_strerror(ua->db));
625 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
630 extern DIRRES *director;
633 * Python control command
634 * python restart (restarts interpreter)
636 static int python_cmd(UAContext *ua, const char *cmd)
638 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
639 term_python_interpreter();
640 init_python_interpreter(director->name(),
641 director->scripts_directory, "DirStartUp");
642 bsendmsg(ua, _("Python interpreter restarted.\n"));
644 bsendmsg(ua, _("Nothing done.\n"));
651 * Set a new address in a Client resource. We do this only
652 * if the Console name is the same as the Client name
653 * and the Console can access the client.
655 static int setip_cmd(UAContext *ua, const char *cmd)
659 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
660 bsendmsg(ua, _("Unauthorized command from this console.\n"));
664 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
667 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
670 if (client->address) {
671 free(client->address);
673 /* MA Bug 6 remove ifdef */
674 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
675 client->address = bstrdup(buf);
676 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
677 client->name(), client->address);
684 static void do_en_disable_cmd(UAContext *ua, bool setting)
689 i = find_arg_with_value(ua, NT_("job"));
691 job = select_job_resource(ua);
697 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
701 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
705 if (!acl_access_ok(ua, Job_ACL, job->name())) {
706 bsendmsg(ua, _("Unauthorized command from this console.\n"));
709 job->enabled = setting;
710 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
714 static int enable_cmd(UAContext *ua, const char *cmd)
716 do_en_disable_cmd(ua, true);
720 static int disable_cmd(UAContext *ua, const char *cmd)
722 do_en_disable_cmd(ua, false);
727 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
733 lstore.store = store;
734 pm_strcpy(lstore.store_source, _("unknown source"));
735 set_wstorage(jcr, &lstore);
736 /* Try connecting for up to 15 seconds */
737 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
738 store->name(), store->address, store->SDport);
739 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
740 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
743 Dmsg0(120, _("Connected to storage daemon\n"));
744 sd = jcr->store_bsock;
745 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
746 if (bnet_recv(sd) >= 0) {
747 bsendmsg(ua, "%s", sd->msg);
749 bnet_sig(sd, BNET_TERMINATE);
751 jcr->store_bsock = NULL;
755 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
759 /* Connect to File daemon */
761 ua->jcr->client = client;
762 /* Try to connect for 15 seconds */
763 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
764 client->name(), client->address, client->FDport);
765 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
766 bsendmsg(ua, _("Failed to connect to Client.\n"));
769 Dmsg0(120, "Connected to file daemon\n");
770 fd = ua->jcr->file_bsock;
771 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
772 if (bnet_recv(fd) >= 0) {
773 bsendmsg(ua, "%s", fd->msg);
775 bnet_sig(fd, BNET_TERMINATE);
777 ua->jcr->file_bsock = NULL;
782 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
784 STORE *store, **unique_store;
785 CLIENT *client, **unique_client;
791 /* Count Storage items */
795 foreach_res(store, R_STORAGE) {
798 unique_store = (STORE **) malloc(i * sizeof(STORE));
799 /* Find Unique Storage address/port */
800 store = (STORE *)GetNextRes(R_STORAGE, NULL);
802 unique_store[i++] = store;
803 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
805 for (j=0; j<i; j++) {
806 if (strcmp(unique_store[j]->address, store->address) == 0 &&
807 unique_store[j]->SDport == store->SDport) {
813 unique_store[i++] = store;
814 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
819 /* Call each unique Storage daemon */
820 for (j=0; j<i; j++) {
821 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
825 /* Count Client items */
829 foreach_res(client, R_CLIENT) {
832 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
833 /* Find Unique Client address/port */
834 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
836 unique_client[i++] = client;
837 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
839 for (j=0; j<i; j++) {
840 if (strcmp(unique_client[j]->address, client->address) == 0 &&
841 unique_client[j]->FDport == client->FDport) {
847 unique_client[i++] = client;
848 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
853 /* Call each unique File daemon */
854 for (j=0; j<i; j++) {
855 do_client_setdebug(ua, unique_client[j], level, trace_flag);
861 * setdebug level=nn all trace=1/0
863 static int setdebug_cmd(UAContext *ua, const char *cmd)
871 if (!open_client_db(ua)) {
874 Dmsg1(120, "setdebug:%s:\n", cmd);
877 i = find_arg_with_value(ua, "level");
879 level = atoi(ua->argv[i]);
882 if (!get_pint(ua, _("Enter new debug level: "))) {
885 level = ua->pint32_val;
888 /* Look for trace flag. -1 => not change */
889 i = find_arg_with_value(ua, "trace");
891 trace_flag = atoi(ua->argv[i]);
892 if (trace_flag > 0) {
898 for (i=1; i<ua->argc; i++) {
899 if (strcasecmp(ua->argk[i], "all") == 0) {
900 do_all_setdebug(ua, level, trace_flag);
903 if (strcasecmp(ua->argk[i], "dir") == 0 ||
904 strcasecmp(ua->argk[i], "director") == 0) {
906 set_trace(trace_flag);
909 if (strcasecmp(ua->argk[i], "client") == 0 ||
910 strcasecmp(ua->argk[i], "fd") == 0) {
913 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
915 do_client_setdebug(ua, client, level, trace_flag);
919 client = select_client_resource(ua);
921 do_client_setdebug(ua, client, level, trace_flag);
926 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
927 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
928 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
931 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
933 do_storage_setdebug(ua, store, level, trace_flag);
937 store = get_storage_resource(ua, false/*no default*/);
939 do_storage_setdebug(ua, store, level, trace_flag);
945 * We didn't find an appropriate keyword above, so
948 start_prompt(ua, _("Available daemons are: \n"));
949 add_prompt(ua, _("Director"));
950 add_prompt(ua, _("Storage"));
951 add_prompt(ua, _("Client"));
952 add_prompt(ua, _("All"));
953 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
954 case 0: /* Director */
956 set_trace(trace_flag);
959 store = get_storage_resource(ua, false/*no default*/);
961 do_storage_setdebug(ua, store, level, trace_flag);
965 client = select_client_resource(ua);
967 do_client_setdebug(ua, client, level, trace_flag);
971 do_all_setdebug(ua, level, trace_flag);
980 * Turn debug tracing to file on/off
982 static int trace_cmd(UAContext *ua, const char *cmd)
987 if (!get_cmd(ua, _("Turn on or off? "))) {
995 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1000 static int var_cmd(UAContext *ua, const char *cmd)
1002 POOLMEM *val = get_pool_memory(PM_FNAME);
1005 if (!open_client_db(ua)) {
1008 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1011 while (*var == ' ') { /* skip spaces */
1014 Dmsg1(100, "Var=%s:\n", var);
1015 variable_expansion(ua->jcr, var, &val);
1016 bsendmsg(ua, "%s\n", val);
1017 free_pool_memory(val);
1021 static int estimate_cmd(UAContext *ua, const char *cmd)
1024 CLIENT *client = NULL;
1025 FILESET *fileset = NULL;
1027 char since[MAXSTRING];
1030 jcr->JobLevel = L_FULL;
1031 for (int i=1; i<ua->argc; i++) {
1032 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1033 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1035 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1039 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1041 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1042 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1043 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1049 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1051 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1052 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1053 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1059 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1063 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1064 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1065 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1070 if (!job && !(client && fileset)) {
1071 if (!(job = select_job_resource(ua))) {
1076 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1078 bsendmsg(ua, _("No job specified.\n"));
1081 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1082 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1087 client = job->client;
1090 fileset = job->fileset;
1092 jcr->client = client;
1093 jcr->fileset = fileset;
1095 ua->catalog = client->catalog;
1102 jcr->JobType = JT_BACKUP;
1103 init_jcr_job_record(jcr);
1105 if (!get_or_create_client_record(jcr)) {
1108 if (!get_or_create_fileset_record(jcr)) {
1112 get_level_since_time(ua->jcr, since, sizeof(since));
1114 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1115 job->client->name(), job->client->address, job->client->FDport);
1116 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1117 bsendmsg(ua, _("Failed to connect to Client.\n"));
1121 if (!send_include_list(jcr)) {
1122 bsendmsg(ua, _("Error sending include list.\n"));
1126 if (!send_exclude_list(jcr)) {
1127 bsendmsg(ua, _("Error sending exclude list.\n"));
1131 if (!send_level_command(jcr)) {
1135 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1136 while (bnet_recv(jcr->file_bsock) >= 0) {
1137 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1141 if (jcr->file_bsock) {
1142 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1143 bnet_close(jcr->file_bsock);
1144 jcr->file_bsock = NULL;
1153 static int time_cmd(UAContext *ua, const char *cmd)
1156 time_t ttime = time(NULL);
1158 (void)localtime_r(&ttime, &tm);
1159 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1160 bsendmsg(ua, "%s\n", sdt);
1165 * reload the conf file
1167 extern "C" void reload_config(int sig);
1169 static int reload_cmd(UAContext *ua, const char *cmd)
1176 * Delete Pool records (should purge Media with it).
1178 * delete pool=<pool-name>
1179 * delete volume pool=<pool-name> volume=<name>
1182 static int delete_cmd(UAContext *ua, const char *cmd)
1184 static const char *keywords[] = {
1190 if (!open_client_db(ua)) {
1194 switch (find_arg_keyword(ua, keywords)) {
1203 while ((i=find_arg(ua, "jobid")) > 0) {
1205 *ua->argk[i] = 0; /* zap keyword already visited */
1213 "In general it is not a good idea to delete either a\n"
1214 "Pool or a Volume since they may contain data.\n\n"));
1216 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1227 bsendmsg(ua, _("Nothing done.\n"));
1235 * delete_job has been modified to parse JobID lists like the
1237 * delete JobID=3,4,6,7-11,14
1239 * Thanks to Phil Stracchino for the above addition.
1242 static void delete_job(UAContext *ua)
1247 int i = find_arg_with_value(ua, NT_("jobid"));
1249 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1250 s = bstrdup(ua->argv[i]);
1253 * We could use strtok() here. But we're not going to, because:
1254 * (a) strtok() is deprecated, having been replaced by strsep();
1255 * (b) strtok() is broken in significant ways.
1256 * we could use strsep() instead, but it's not universally available.
1257 * so we grow our own using strchr().
1259 sep = strchr(tok, ',');
1260 while (sep != NULL) {
1262 if (strchr(tok, '-')) {
1263 delete_job_id_range(ua, tok);
1265 JobId = str_to_int64(tok);
1266 do_job_delete(ua, JobId);
1269 sep = strchr(tok, ',');
1271 /* pick up the last token */
1272 if (strchr(tok, '-')) {
1273 delete_job_id_range(ua, tok);
1275 JobId = str_to_int64(tok);
1276 do_job_delete(ua, JobId);
1281 JobId = str_to_int64(ua->argv[i]);
1282 do_job_delete(ua, JobId);
1284 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1287 JobId = ua->int64_val;
1288 do_job_delete(ua, JobId);
1293 * we call delete_job_id_range to parse range tokens and iterate over ranges
1295 static void delete_job_id_range(UAContext *ua, char *tok)
1300 tok2 = strchr(tok, '-');
1303 j1 = str_to_int64(tok);
1304 j2 = str_to_int64(tok2);
1305 for (j=j1; j<=j2; j++) {
1306 do_job_delete(ua, j);
1311 * do_job_delete now performs the actual delete operation atomically
1314 static void do_job_delete(UAContext *ua, JobId_t JobId)
1316 POOL_MEM query(PM_MESSAGE);
1319 purge_files_from_job(ua, JobId);
1320 purge_job_from_catalog(ua, JobId);
1321 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1325 * Delete media records from database -- dangerous
1327 static int delete_volume(UAContext *ua)
1331 if (!select_media_dbr(ua, &mr)) {
1334 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1335 "and all Jobs saved on that volume from the Catalog\n"),
1338 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1341 if (ua->pint32_val) {
1342 db_delete_media_record(ua->jcr, ua->db, &mr);
1348 * Delete a pool record from the database -- dangerous
1350 static int delete_pool(UAContext *ua)
1354 memset(&pr, 0, sizeof(pr));
1356 if (!get_pool_dbr(ua, &pr)) {
1359 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1362 if (ua->pint32_val) {
1363 db_delete_pool_record(ua->jcr, ua->db, &pr);
1369 static void do_mount_cmd(UAContext *ua, const char *command)
1374 char dev_name[MAX_NAME_LENGTH];
1378 if (!open_client_db(ua)) {
1381 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1383 store.store = get_storage_resource(ua, true/*arg is storage*/);
1384 pm_strcpy(store.store_source, _("unknown source"));
1388 set_wstorage(jcr, &store);
1389 drive = get_storage_drive(ua, store.store);
1390 if (strcmp(command, "mount") == 0) {
1391 slot = get_storage_slot(ua, store.store);
1394 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1395 store.store->media_type, store.store->dev_name(), drive);
1397 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1398 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1401 sd = jcr->store_bsock;
1402 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1403 bash_spaces(dev_name);
1405 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1407 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1409 while (bnet_recv(sd) >= 0) {
1410 bsendmsg(ua, "%s", sd->msg);
1412 bnet_sig(sd, BNET_TERMINATE);
1414 jcr->store_bsock = NULL;
1418 * mount [storage=<name>] [drive=nn] [slot=mm]
1420 static int mount_cmd(UAContext *ua, const char *cmd)
1422 do_mount_cmd(ua, "mount"); /* mount */
1428 * unmount [storage=<name>] [drive=nn]
1430 static int unmount_cmd(UAContext *ua, const char *cmd)
1432 do_mount_cmd(ua, "unmount"); /* unmount */
1438 * release [storage=<name>] [drive=nn]
1440 static int release_cmd(UAContext *ua, const char *cmd)
1442 do_mount_cmd(ua, "release"); /* release */
1449 * use catalog=<name>
1451 static int use_cmd(UAContext *ua, const char *cmd)
1453 CAT *oldcatalog, *catalog;
1456 close_db(ua); /* close any previously open db */
1457 oldcatalog = ua->catalog;
1459 if (!(catalog = get_catalog_resource(ua))) {
1460 ua->catalog = oldcatalog;
1462 ua->catalog = catalog;
1465 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1466 ua->catalog->name(), ua->catalog->db_name);
1471 int quit_cmd(UAContext *ua, const char *cmd)
1477 /* Handler to get job status */
1478 static int status_handler(void *ctx, int num_fields, char **row)
1480 char *val = (char *)ctx;
1485 *val = '?'; /* Unknown by default */
1492 * Wait until no job is running
1494 int wait_cmd(UAContext *ua, const char *cmd)
1499 * Wait until no job is running
1501 if (ua->argc == 1) {
1502 bmicrosleep(0, 200000); /* let job actually start */
1503 for (bool running=true; running; ) {
1506 if (jcr->JobId != 0) {
1520 /* we have jobid, jobname or ujobid argument */
1522 uint32_t jobid = 0 ;
1524 if (!open_client_db(ua)) {
1525 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1529 for (int i=1; i<ua->argc; i++) {
1530 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1534 jobid = str_to_int64(ua->argv[i]);
1536 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1537 strcasecmp(ua->argk[i], "job") == 0) {
1541 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1543 jobid = jcr->JobId ;
1547 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1551 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1553 jobid = jcr->JobId ;
1561 bsendmsg(ua, _("ERR: Job was not found\n"));
1566 * We wait the end of job
1569 bmicrosleep(0, 200000); /* let job actually start */
1570 for (bool running=true; running; ) {
1573 jcr=get_jcr_by_id(jobid) ;
1586 * We have to get JobStatus
1590 char jobstatus = '?'; /* Unknown by default */
1593 bsnprintf(buf, sizeof(buf),
1594 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1597 db_sql_query(ua->db, buf,
1598 status_handler, (void *)&jobstatus);
1600 switch (jobstatus) {
1602 status = 1 ; /* Warning */
1606 case JS_ErrorTerminated:
1608 status = 2 ; /* Critical */
1612 status = 0 ; /* Ok */
1616 status = 3 ; /* Unknown */
1620 bsendmsg(ua, "JobId=%i\n", jobid) ;
1621 bsendmsg(ua, "JobStatus=%s (%c)\n",
1622 job_status_to_str(jobstatus),
1626 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1633 static int help_cmd(UAContext *ua, const char *cmd)
1637 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1638 for (i=0; i<comsize; i++) {
1639 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1641 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1645 int qhelp_cmd(UAContext *ua, const char *cmd)
1649 for (i=0; i<comsize; i++) {
1650 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1655 static int version_cmd(UAContext *ua, const char *cmd)
1657 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1662 * This call explicitly checks for a catalog=xxx and
1663 * if given, opens that catalog. It also checks for
1664 * client=xxx and if found, opens the catalog
1665 * corresponding to that client. If we still don't
1666 * have a catalog, look for a Job keyword and get the
1667 * catalog from its client record.
1669 bool open_client_db(UAContext *ua)
1676 /* Try for catalog keyword */
1677 i = find_arg_with_value(ua, NT_("catalog"));
1679 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1680 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1683 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1685 if (ua->catalog && ua->catalog != catalog) {
1688 ua->catalog = catalog;
1693 /* Try for client keyword */
1694 i = find_arg_with_value(ua, NT_("client"));
1696 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1697 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1700 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1702 catalog = client->catalog;
1703 if (ua->catalog && ua->catalog != catalog) {
1706 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1707 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1710 ua->catalog = catalog;
1715 /* Try for Job keyword */
1716 i = find_arg_with_value(ua, NT_("job"));
1718 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1719 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1722 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1724 catalog = job->client->catalog;
1725 if (ua->catalog && ua->catalog != catalog) {
1728 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1729 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1732 ua->catalog = catalog;
1742 * Open the catalog database.
1744 bool open_db(UAContext *ua)
1750 ua->catalog = get_catalog_resource(ua);
1752 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1757 ua->jcr->catalog = ua->catalog;
1759 Dmsg0(150, "Open database\n");
1760 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1761 ua->catalog->db_password, ua->catalog->db_address,
1762 ua->catalog->db_port, ua->catalog->db_socket,
1763 ua->catalog->mult_db_connections);
1764 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1765 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1766 ua->catalog->db_name);
1768 bsendmsg(ua, "%s", db_strerror(ua->db));
1773 ua->jcr->db = ua->db;
1774 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1775 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1779 void close_db(UAContext *ua)
1782 db_close_database(ua->jcr, ua->db);