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.\n" \
549 "Try to update it with 'update pool=%s'\n"),
550 pool->name(), rpool.Name, rpool.Name,pool->name());
554 } else { /* no RecyclePool used, set it to 0 */
555 pr->RecyclePoolId = 0;
562 * Create a pool record from a given Pool resource
563 * Also called from backup.c
564 * Returns: -1 on error
565 * 0 record already exists
569 int create_pool(JCR *jcr, B_DB *db, POOL *pool, e_pool_op op)
573 memset(&pr, 0, sizeof(POOL_DBR));
575 bstrncpy(pr.Name, pool->name(), sizeof(pr.Name));
577 if (db_get_pool_record(jcr, db, &pr)) {
579 if (op == POOL_OP_UPDATE) { /* update request */
580 set_pooldbr_from_poolres(&pr, pool, op);
581 db_update_pool_record(jcr, db, &pr);
583 return 0; /* exists */
586 set_pooldbr_from_poolres(&pr, pool, op);
587 set_pooldbr_recyclepoolid(jcr, db, &pr, pool);
589 if (!db_create_pool_record(jcr, db, &pr)) {
590 return -1; /* error */
598 * Create a Pool Record in the database.
599 * It is always created from the Resource record.
601 static int create_cmd(UAContext *ua, const char *cmd)
605 if (!open_client_db(ua)) {
609 pool = get_pool_resource(ua);
614 switch (create_pool(ua->jcr, ua->db, pool, POOL_OP_CREATE)) {
616 bsendmsg(ua, _("Error: Pool %s already exists.\n"
617 "Use update to change it.\n"), pool->name());
621 bsendmsg(ua, "%s", db_strerror(ua->db));
627 bsendmsg(ua, _("Pool %s created.\n"), pool->name());
632 extern DIRRES *director;
635 * Python control command
636 * python restart (restarts interpreter)
638 static int python_cmd(UAContext *ua, const char *cmd)
640 if (ua->argc >= 2 && strcasecmp(ua->argk[1], NT_("restart")) == 0) {
641 term_python_interpreter();
642 init_python_interpreter(director->name(),
643 director->scripts_directory, "DirStartUp");
644 bsendmsg(ua, _("Python interpreter restarted.\n"));
646 bsendmsg(ua, _("Nothing done.\n"));
653 * Set a new address in a Client resource. We do this only
654 * if the Console name is the same as the Client name
655 * and the Console can access the client.
657 static int setip_cmd(UAContext *ua, const char *cmd)
661 if (!ua->cons || !acl_access_ok(ua, Client_ACL, ua->cons->name())) {
662 bsendmsg(ua, _("Unauthorized command from this console.\n"));
666 client = (CLIENT *)GetResWithName(R_CLIENT, ua->cons->name());
669 bsendmsg(ua, _("Client \"%s\" not found.\n"), ua->cons->name());
672 if (client->address) {
673 free(client->address);
675 /* MA Bug 6 remove ifdef */
676 sockaddr_to_ascii(&(ua->UA_sock->client_addr), buf, sizeof(buf));
677 client->address = bstrdup(buf);
678 bsendmsg(ua, _("Client \"%s\" address set to %s\n"),
679 client->name(), client->address);
686 static void do_en_disable_cmd(UAContext *ua, bool setting)
691 i = find_arg_with_value(ua, NT_("job"));
693 job = select_job_resource(ua);
699 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
703 bsendmsg(ua, _("Job \"%s\" not found.\n"), ua->argv[i]);
707 if (!acl_access_ok(ua, Job_ACL, job->name())) {
708 bsendmsg(ua, _("Unauthorized command from this console.\n"));
711 job->enabled = setting;
712 bsendmsg(ua, _("Job \"%s\" %sabled\n"), job->name(), setting?"en":"dis");
716 static int enable_cmd(UAContext *ua, const char *cmd)
718 do_en_disable_cmd(ua, true);
722 static int disable_cmd(UAContext *ua, const char *cmd)
724 do_en_disable_cmd(ua, false);
729 static void do_storage_setdebug(UAContext *ua, STORE *store, int level, int trace_flag)
735 lstore.store = store;
736 pm_strcpy(lstore.store_source, _("unknown source"));
737 set_wstorage(jcr, &lstore);
738 /* Try connecting for up to 15 seconds */
739 bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d\n"),
740 store->name(), store->address, store->SDport);
741 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
742 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
745 Dmsg0(120, _("Connected to storage daemon\n"));
746 sd = jcr->store_bsock;
747 bnet_fsend(sd, "setdebug=%d trace=%d\n", level, trace_flag);
748 if (bnet_recv(sd) >= 0) {
749 bsendmsg(ua, "%s", sd->msg);
751 bnet_sig(sd, BNET_TERMINATE);
753 jcr->store_bsock = NULL;
757 static void do_client_setdebug(UAContext *ua, CLIENT *client, int level, int trace_flag)
761 /* Connect to File daemon */
763 ua->jcr->client = client;
764 /* Try to connect for 15 seconds */
765 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
766 client->name(), client->address, client->FDport);
767 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
768 bsendmsg(ua, _("Failed to connect to Client.\n"));
771 Dmsg0(120, "Connected to file daemon\n");
772 fd = ua->jcr->file_bsock;
773 bnet_fsend(fd, "setdebug=%d trace=%d\n", level, trace_flag);
774 if (bnet_recv(fd) >= 0) {
775 bsendmsg(ua, "%s", fd->msg);
777 bnet_sig(fd, BNET_TERMINATE);
779 ua->jcr->file_bsock = NULL;
784 static void do_all_setdebug(UAContext *ua, int level, int trace_flag)
786 STORE *store, **unique_store;
787 CLIENT *client, **unique_client;
793 /* Count Storage items */
797 foreach_res(store, R_STORAGE) {
800 unique_store = (STORE **) malloc(i * sizeof(STORE));
801 /* Find Unique Storage address/port */
802 store = (STORE *)GetNextRes(R_STORAGE, NULL);
804 unique_store[i++] = store;
805 while ((store = (STORE *)GetNextRes(R_STORAGE, (RES *)store))) {
807 for (j=0; j<i; j++) {
808 if (strcmp(unique_store[j]->address, store->address) == 0 &&
809 unique_store[j]->SDport == store->SDport) {
815 unique_store[i++] = store;
816 Dmsg2(140, "Stuffing: %s:%d\n", store->address, store->SDport);
821 /* Call each unique Storage daemon */
822 for (j=0; j<i; j++) {
823 do_storage_setdebug(ua, unique_store[j], level, trace_flag);
827 /* Count Client items */
831 foreach_res(client, R_CLIENT) {
834 unique_client = (CLIENT **) malloc(i * sizeof(CLIENT));
835 /* Find Unique Client address/port */
836 client = (CLIENT *)GetNextRes(R_CLIENT, NULL);
838 unique_client[i++] = client;
839 while ((client = (CLIENT *)GetNextRes(R_CLIENT, (RES *)client))) {
841 for (j=0; j<i; j++) {
842 if (strcmp(unique_client[j]->address, client->address) == 0 &&
843 unique_client[j]->FDport == client->FDport) {
849 unique_client[i++] = client;
850 Dmsg2(140, "Stuffing: %s:%d\n", client->address, client->FDport);
855 /* Call each unique File daemon */
856 for (j=0; j<i; j++) {
857 do_client_setdebug(ua, unique_client[j], level, trace_flag);
863 * setdebug level=nn all trace=1/0
865 static int setdebug_cmd(UAContext *ua, const char *cmd)
873 if (!open_client_db(ua)) {
876 Dmsg1(120, "setdebug:%s:\n", cmd);
879 i = find_arg_with_value(ua, "level");
881 level = atoi(ua->argv[i]);
884 if (!get_pint(ua, _("Enter new debug level: "))) {
887 level = ua->pint32_val;
890 /* Look for trace flag. -1 => not change */
891 i = find_arg_with_value(ua, "trace");
893 trace_flag = atoi(ua->argv[i]);
894 if (trace_flag > 0) {
900 for (i=1; i<ua->argc; i++) {
901 if (strcasecmp(ua->argk[i], "all") == 0) {
902 do_all_setdebug(ua, level, trace_flag);
905 if (strcasecmp(ua->argk[i], "dir") == 0 ||
906 strcasecmp(ua->argk[i], "director") == 0) {
908 set_trace(trace_flag);
911 if (strcasecmp(ua->argk[i], "client") == 0 ||
912 strcasecmp(ua->argk[i], "fd") == 0) {
915 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
917 do_client_setdebug(ua, client, level, trace_flag);
921 client = select_client_resource(ua);
923 do_client_setdebug(ua, client, level, trace_flag);
928 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
929 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
930 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
933 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
935 do_storage_setdebug(ua, store, level, trace_flag);
939 store = get_storage_resource(ua, false/*no default*/);
941 do_storage_setdebug(ua, store, level, trace_flag);
947 * We didn't find an appropriate keyword above, so
950 start_prompt(ua, _("Available daemons are: \n"));
951 add_prompt(ua, _("Director"));
952 add_prompt(ua, _("Storage"));
953 add_prompt(ua, _("Client"));
954 add_prompt(ua, _("All"));
955 switch(do_prompt(ua, "", _("Select daemon type to set debug level"), NULL, 0)) {
956 case 0: /* Director */
958 set_trace(trace_flag);
961 store = get_storage_resource(ua, false/*no default*/);
963 do_storage_setdebug(ua, store, level, trace_flag);
967 client = select_client_resource(ua);
969 do_client_setdebug(ua, client, level, trace_flag);
973 do_all_setdebug(ua, level, trace_flag);
982 * Turn debug tracing to file on/off
984 static int trace_cmd(UAContext *ua, const char *cmd)
989 if (!get_cmd(ua, _("Turn on or off? "))) {
997 set_trace((strcasecmp(onoff, NT_("off")) == 0) ? false : true);
1002 static int var_cmd(UAContext *ua, const char *cmd)
1004 POOLMEM *val = get_pool_memory(PM_FNAME);
1007 if (!open_client_db(ua)) {
1010 for (var=ua->cmd; *var != ' '; ) { /* skip command */
1013 while (*var == ' ') { /* skip spaces */
1016 Dmsg1(100, "Var=%s:\n", var);
1017 variable_expansion(ua->jcr, var, &val);
1018 bsendmsg(ua, "%s\n", val);
1019 free_pool_memory(val);
1023 static int estimate_cmd(UAContext *ua, const char *cmd)
1026 CLIENT *client = NULL;
1027 FILESET *fileset = NULL;
1029 char since[MAXSTRING];
1032 jcr->JobLevel = L_FULL;
1033 for (int i=1; i<ua->argc; i++) {
1034 if (strcasecmp(ua->argk[i], NT_("client")) == 0 ||
1035 strcasecmp(ua->argk[i], NT_("fd")) == 0) {
1037 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1041 if (strcasecmp(ua->argk[i], NT_("job")) == 0) {
1043 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1044 if (job && !acl_access_ok(ua, Job_ACL, job->name())) {
1045 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1051 if (strcasecmp(ua->argk[i], NT_("fileset")) == 0) {
1053 fileset = (FILESET *)GetResWithName(R_FILESET, ua->argv[i]);
1054 if (fileset && !acl_access_ok(ua, FileSet_ACL, fileset->name())) {
1055 bsendmsg(ua, _("No authorization for FileSet \"%s\"\n"), fileset->name());
1061 if (strcasecmp(ua->argk[i], NT_("listing")) == 0) {
1065 if (strcasecmp(ua->argk[i], NT_("level")) == 0) {
1066 if (!get_level_from_name(ua->jcr, ua->argv[i])) {
1067 bsendmsg(ua, _("Level %s not valid.\n"), ua->argv[i]);
1072 if (!job && !(client && fileset)) {
1073 if (!(job = select_job_resource(ua))) {
1078 job = (JOB *)GetResWithName(R_JOB, ua->argk[1]);
1080 bsendmsg(ua, _("No job specified.\n"));
1083 if (!acl_access_ok(ua, Job_ACL, job->name())) {
1084 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), job->name());
1089 client = job->client;
1092 fileset = job->fileset;
1094 jcr->client = client;
1095 jcr->fileset = fileset;
1097 ua->catalog = client->catalog;
1104 jcr->JobType = JT_BACKUP;
1105 init_jcr_job_record(jcr);
1107 if (!get_or_create_client_record(jcr)) {
1110 if (!get_or_create_fileset_record(jcr)) {
1114 get_level_since_time(ua->jcr, since, sizeof(since));
1116 bsendmsg(ua, _("Connecting to Client %s at %s:%d\n"),
1117 job->client->name(), job->client->address, job->client->FDport);
1118 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
1119 bsendmsg(ua, _("Failed to connect to Client.\n"));
1123 if (!send_include_list(jcr)) {
1124 bsendmsg(ua, _("Error sending include list.\n"));
1128 if (!send_exclude_list(jcr)) {
1129 bsendmsg(ua, _("Error sending exclude list.\n"));
1133 if (!send_level_command(jcr)) {
1137 bnet_fsend(jcr->file_bsock, "estimate listing=%d\n", listing);
1138 while (bnet_recv(jcr->file_bsock) >= 0) {
1139 bsendmsg(ua, "%s", jcr->file_bsock->msg);
1143 if (jcr->file_bsock) {
1144 bnet_sig(jcr->file_bsock, BNET_TERMINATE);
1145 bnet_close(jcr->file_bsock);
1146 jcr->file_bsock = NULL;
1155 static int time_cmd(UAContext *ua, const char *cmd)
1158 time_t ttime = time(NULL);
1160 (void)localtime_r(&ttime, &tm);
1161 strftime(sdt, sizeof(sdt), "%d-%b-%Y %H:%M:%S", &tm);
1162 bsendmsg(ua, "%s\n", sdt);
1167 * reload the conf file
1169 extern "C" void reload_config(int sig);
1171 static int reload_cmd(UAContext *ua, const char *cmd)
1178 * Delete Pool records (should purge Media with it).
1180 * delete pool=<pool-name>
1181 * delete volume pool=<pool-name> volume=<name>
1184 static int delete_cmd(UAContext *ua, const char *cmd)
1186 static const char *keywords[] = {
1192 if (!open_client_db(ua)) {
1196 switch (find_arg_keyword(ua, keywords)) {
1205 while ((i=find_arg(ua, "jobid")) > 0) {
1207 *ua->argk[i] = 0; /* zap keyword already visited */
1215 "In general it is not a good idea to delete either a\n"
1216 "Pool or a Volume since they may contain data.\n\n"));
1218 switch (do_keyword_prompt(ua, _("Choose catalog item to delete"), keywords)) {
1229 bsendmsg(ua, _("Nothing done.\n"));
1237 * delete_job has been modified to parse JobID lists like the
1239 * delete JobID=3,4,6,7-11,14
1241 * Thanks to Phil Stracchino for the above addition.
1244 static void delete_job(UAContext *ua)
1249 int i = find_arg_with_value(ua, NT_("jobid"));
1251 if (strchr(ua->argv[i], ',') != NULL || strchr(ua->argv[i], '-') != NULL) {
1252 s = bstrdup(ua->argv[i]);
1255 * We could use strtok() here. But we're not going to, because:
1256 * (a) strtok() is deprecated, having been replaced by strsep();
1257 * (b) strtok() is broken in significant ways.
1258 * we could use strsep() instead, but it's not universally available.
1259 * so we grow our own using strchr().
1261 sep = strchr(tok, ',');
1262 while (sep != NULL) {
1264 if (strchr(tok, '-')) {
1265 delete_job_id_range(ua, tok);
1267 JobId = str_to_int64(tok);
1268 do_job_delete(ua, JobId);
1271 sep = strchr(tok, ',');
1273 /* pick up the last token */
1274 if (strchr(tok, '-')) {
1275 delete_job_id_range(ua, tok);
1277 JobId = str_to_int64(tok);
1278 do_job_delete(ua, JobId);
1283 JobId = str_to_int64(ua->argv[i]);
1284 do_job_delete(ua, JobId);
1286 } else if (!get_pint(ua, _("Enter JobId to delete: "))) {
1289 JobId = ua->int64_val;
1290 do_job_delete(ua, JobId);
1295 * we call delete_job_id_range to parse range tokens and iterate over ranges
1297 static void delete_job_id_range(UAContext *ua, char *tok)
1302 tok2 = strchr(tok, '-');
1305 j1 = str_to_int64(tok);
1306 j2 = str_to_int64(tok2);
1307 for (j=j1; j<=j2; j++) {
1308 do_job_delete(ua, j);
1313 * do_job_delete now performs the actual delete operation atomically
1316 static void do_job_delete(UAContext *ua, JobId_t JobId)
1318 POOL_MEM query(PM_MESSAGE);
1321 purge_files_from_job(ua, JobId);
1322 purge_job_from_catalog(ua, JobId);
1323 bsendmsg(ua, _("Job %s and associated records deleted from the catalog.\n"), edit_int64(JobId, ed1));
1327 * Delete media records from database -- dangerous
1329 static int delete_volume(UAContext *ua)
1333 if (!select_media_dbr(ua, &mr)) {
1336 bsendmsg(ua, _("\nThis command will delete volume %s\n"
1337 "and all Jobs saved on that volume from the Catalog\n"),
1340 if (!get_yesno(ua, _("Are you sure you want to delete this Volume? (yes/no): "))) {
1343 if (ua->pint32_val) {
1344 db_delete_media_record(ua->jcr, ua->db, &mr);
1350 * Delete a pool record from the database -- dangerous
1352 static int delete_pool(UAContext *ua)
1356 memset(&pr, 0, sizeof(pr));
1358 if (!get_pool_dbr(ua, &pr)) {
1361 if (!get_yesno(ua, _("Are you sure you want to delete this Pool? (yes/no): "))) {
1364 if (ua->pint32_val) {
1365 db_delete_pool_record(ua->jcr, ua->db, &pr);
1371 static void do_mount_cmd(UAContext *ua, const char *command)
1376 char dev_name[MAX_NAME_LENGTH];
1380 if (!open_client_db(ua)) {
1383 Dmsg2(120, "%s: %s\n", command, ua->UA_sock->msg);
1385 store.store = get_storage_resource(ua, true/*arg is storage*/);
1386 pm_strcpy(store.store_source, _("unknown source"));
1390 set_wstorage(jcr, &store);
1391 drive = get_storage_drive(ua, store.store);
1392 if (strcmp(command, "mount") == 0) {
1393 slot = get_storage_slot(ua, store.store);
1396 Dmsg3(120, "Found storage, MediaType=%s DevName=%s drive=%d\n",
1397 store.store->media_type, store.store->dev_name(), drive);
1399 if (!connect_to_storage_daemon(jcr, 10, SDConnectTimeout, 1)) {
1400 bsendmsg(ua, _("Failed to connect to Storage daemon.\n"));
1403 sd = jcr->store_bsock;
1404 bstrncpy(dev_name, store.store->dev_name(), sizeof(dev_name));
1405 bash_spaces(dev_name);
1407 bnet_fsend(sd, "%s %s drive=%d slot=%d", command, dev_name, drive, slot);
1409 bnet_fsend(sd, "%s %s drive=%d", command, dev_name, drive);
1411 while (bnet_recv(sd) >= 0) {
1412 bsendmsg(ua, "%s", sd->msg);
1414 bnet_sig(sd, BNET_TERMINATE);
1416 jcr->store_bsock = NULL;
1420 * mount [storage=<name>] [drive=nn] [slot=mm]
1422 static int mount_cmd(UAContext *ua, const char *cmd)
1424 do_mount_cmd(ua, "mount"); /* mount */
1430 * unmount [storage=<name>] [drive=nn]
1432 static int unmount_cmd(UAContext *ua, const char *cmd)
1434 do_mount_cmd(ua, "unmount"); /* unmount */
1440 * release [storage=<name>] [drive=nn]
1442 static int release_cmd(UAContext *ua, const char *cmd)
1444 do_mount_cmd(ua, "release"); /* release */
1451 * use catalog=<name>
1453 static int use_cmd(UAContext *ua, const char *cmd)
1455 CAT *oldcatalog, *catalog;
1458 close_db(ua); /* close any previously open db */
1459 oldcatalog = ua->catalog;
1461 if (!(catalog = get_catalog_resource(ua))) {
1462 ua->catalog = oldcatalog;
1464 ua->catalog = catalog;
1467 bsendmsg(ua, _("Using Catalog name=%s DB=%s\n"),
1468 ua->catalog->name(), ua->catalog->db_name);
1473 int quit_cmd(UAContext *ua, const char *cmd)
1479 /* Handler to get job status */
1480 static int status_handler(void *ctx, int num_fields, char **row)
1482 char *val = (char *)ctx;
1487 *val = '?'; /* Unknown by default */
1494 * Wait until no job is running
1496 int wait_cmd(UAContext *ua, const char *cmd)
1501 * Wait until no job is running
1503 if (ua->argc == 1) {
1504 bmicrosleep(0, 200000); /* let job actually start */
1505 for (bool running=true; running; ) {
1508 if (jcr->JobId != 0) {
1522 /* we have jobid, jobname or ujobid argument */
1524 uint32_t jobid = 0 ;
1526 if (!open_client_db(ua)) {
1527 bsendmsg(ua, _("ERR: Can't open db\n")) ;
1531 for (int i=1; i<ua->argc; i++) {
1532 if (strcasecmp(ua->argk[i], "jobid") == 0) {
1536 jobid = str_to_int64(ua->argv[i]);
1538 } else if (strcasecmp(ua->argk[i], "jobname") == 0 ||
1539 strcasecmp(ua->argk[i], "job") == 0) {
1543 jcr=get_jcr_by_partial_name(ua->argv[i]) ;
1545 jobid = jcr->JobId ;
1549 } else if (strcasecmp(ua->argk[i], "ujobid") == 0) {
1553 jcr=get_jcr_by_full_name(ua->argv[i]) ;
1555 jobid = jcr->JobId ;
1563 bsendmsg(ua, _("ERR: Job was not found\n"));
1568 * We wait the end of job
1571 bmicrosleep(0, 200000); /* let job actually start */
1572 for (bool running=true; running; ) {
1575 jcr=get_jcr_by_id(jobid) ;
1588 * We have to get JobStatus
1592 char jobstatus = '?'; /* Unknown by default */
1595 bsnprintf(buf, sizeof(buf),
1596 "SELECT JobStatus FROM Job WHERE JobId='%i'", jobid);
1599 db_sql_query(ua->db, buf,
1600 status_handler, (void *)&jobstatus);
1602 switch (jobstatus) {
1604 status = 1 ; /* Warning */
1608 case JS_ErrorTerminated:
1610 status = 2 ; /* Critical */
1614 status = 0 ; /* Ok */
1618 status = 3 ; /* Unknown */
1622 bsendmsg(ua, "JobId=%i\n", jobid) ;
1623 bsendmsg(ua, "JobStatus=%s (%c)\n",
1624 job_status_to_str(jobstatus),
1628 bsendmsg(ua, "ExitStatus=%i\n", status) ;
1635 static int help_cmd(UAContext *ua, const char *cmd)
1639 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
1640 for (i=0; i<comsize; i++) {
1641 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
1643 bsendmsg(ua, _("\nWhen at a prompt, entering a period cancels the command.\n\n"));
1647 int qhelp_cmd(UAContext *ua, const char *cmd)
1651 for (i=0; i<comsize; i++) {
1652 bsendmsg(ua, "%s %s\n", commands[i].key, _(commands[i].help));
1657 static int version_cmd(UAContext *ua, const char *cmd)
1659 bsendmsg(ua, _("%s Version: %s (%s)\n"), my_name, VERSION, BDATE);
1664 * This call explicitly checks for a catalog=xxx and
1665 * if given, opens that catalog. It also checks for
1666 * client=xxx and if found, opens the catalog
1667 * corresponding to that client. If we still don't
1668 * have a catalog, look for a Job keyword and get the
1669 * catalog from its client record.
1671 bool open_client_db(UAContext *ua)
1678 /* Try for catalog keyword */
1679 i = find_arg_with_value(ua, NT_("catalog"));
1681 if (!acl_access_ok(ua, Catalog_ACL, ua->argv[i])) {
1682 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), ua->argv[i]);
1685 catalog = (CAT *)GetResWithName(R_CATALOG, ua->argv[i]);
1687 if (ua->catalog && ua->catalog != catalog) {
1690 ua->catalog = catalog;
1695 /* Try for client keyword */
1696 i = find_arg_with_value(ua, NT_("client"));
1698 if (!acl_access_ok(ua, Client_ACL, ua->argv[i])) {
1699 bsendmsg(ua, _("No authorization for Client \"%s\"\n"), ua->argv[i]);
1702 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1704 catalog = client->catalog;
1705 if (ua->catalog && ua->catalog != catalog) {
1708 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1709 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1712 ua->catalog = catalog;
1717 /* Try for Job keyword */
1718 i = find_arg_with_value(ua, NT_("job"));
1720 if (!acl_access_ok(ua, Job_ACL, ua->argv[i])) {
1721 bsendmsg(ua, _("No authorization for Job \"%s\"\n"), ua->argv[i]);
1724 job = (JOB *)GetResWithName(R_JOB, ua->argv[i]);
1726 catalog = job->client->catalog;
1727 if (ua->catalog && ua->catalog != catalog) {
1730 if (!acl_access_ok(ua, Catalog_ACL, catalog->name())) {
1731 bsendmsg(ua, _("No authorization for Catalog \"%s\"\n"), catalog->name());
1734 ua->catalog = catalog;
1744 * Open the catalog database.
1746 bool open_db(UAContext *ua)
1752 ua->catalog = get_catalog_resource(ua);
1754 bsendmsg(ua, _("Could not find a Catalog resource\n"));
1759 ua->jcr->catalog = ua->catalog;
1761 Dmsg0(150, "Open database\n");
1762 ua->db = db_init_database(ua->jcr, ua->catalog->db_name, ua->catalog->db_user,
1763 ua->catalog->db_password, ua->catalog->db_address,
1764 ua->catalog->db_port, ua->catalog->db_socket,
1765 ua->catalog->mult_db_connections);
1766 if (!ua->db || !db_open_database(ua->jcr, ua->db)) {
1767 bsendmsg(ua, _("Could not open catalog database \"%s\".\n"),
1768 ua->catalog->db_name);
1770 bsendmsg(ua, "%s", db_strerror(ua->db));
1775 ua->jcr->db = ua->db;
1776 bsendmsg(ua, _("Using Catalog \"%s\"\n"), ua->catalog->name());
1777 Dmsg1(150, "DB %s opened\n", ua->catalog->db_name);
1781 void close_db(UAContext *ua)
1784 db_close_database(ua->jcr, ua->db);