2 Bacula(R) - The Network Backup Solution
4 Copyright (C) 2000-2017 Kern Sibbald
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
17 Bacula(R) is a registered trademark of Kern Sibbald.
20 * Bacula Director -- User Agent Commands
21 * These are "dot" commands, i.e. commands preceded
22 * by a period. These commands are meant to be used
23 * by a program, so there is no prompting, and the
24 * returned results are (supposed to be) predictable.
26 * Kern Sibbald, April MMII
31 #include "cats/bvfs.h"
32 #include "findlib/find.h"
34 /* Imported variables */
35 extern struct s_jl joblevels[];
36 extern struct s_jt jobtypes[];
38 /* Imported functions */
39 extern void do_messages(UAContext *ua, const char *cmd);
40 extern int quit_cmd(UAContext *ua, const char *cmd);
41 extern int qhelp_cmd(UAContext *ua, const char *cmd);
42 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
45 /* Forward referenced functions */
46 static bool admin_cmds(UAContext *ua, const char *cmd);
47 static bool jobscmd(UAContext *ua, const char *cmd);
48 static bool dotestimatecmd(UAContext *ua, const char *cmd);
49 static bool filesetscmd(UAContext *ua, const char *cmd);
50 static bool clientscmd(UAContext *ua, const char *cmd);
51 static bool msgscmd(UAContext *ua, const char *cmd);
52 static bool poolscmd(UAContext *ua, const char *cmd);
53 static bool schedulescmd(UAContext *ua, const char *cmd);
54 static bool storagecmd(UAContext *ua, const char *cmd);
55 static bool defaultscmd(UAContext *ua, const char *cmd);
56 static bool typescmd(UAContext *ua, const char *cmd);
57 static bool tagscmd(UAContext *ua, const char *cmd);
58 static bool backupscmd(UAContext *ua, const char *cmd);
59 static bool levelscmd(UAContext *ua, const char *cmd);
60 static bool getmsgscmd(UAContext *ua, const char *cmd);
61 static bool volstatuscmd(UAContext *ua, const char *cmd);
62 static bool mediatypescmd(UAContext *ua, const char *cmd);
63 static bool locationscmd(UAContext *ua, const char *cmd);
64 static bool mediacmd(UAContext *ua, const char *cmd);
65 static bool aopcmd(UAContext *ua, const char *cmd);
66 static bool catalogscmd(UAContext *ua, const char *cmd);
68 static bool dot_ls_cmd(UAContext *ua, const char *cmd);
69 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
70 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
71 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
72 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd);
73 static bool dot_bvfs_versions(UAContext *ua, const char *cmd);
74 static bool dot_bvfs_restore(UAContext *ua, const char *cmd);
75 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd);
76 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd);
77 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd);
78 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd);
79 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd);
80 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd);
81 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd);
82 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd);
83 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len);
85 static bool putfile_cmd(UAContext *ua, const char *cmd);
86 static bool api_cmd(UAContext *ua, const char *cmd);
87 static bool sql_cmd(UAContext *ua, const char *cmd);
88 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
89 static bool dot_help_cmd(UAContext *ua, const char *cmd);
90 static int one_handler(void *ctx, int num_field, char **row);
92 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
93 static struct cmdstruct commands[] = { /* help */ /* can be used in runscript */
94 { NT_(".api"), api_cmd, NULL, false},
95 { NT_(".backups"), backupscmd, NULL, false},
96 { NT_(".clients"), clientscmd, NULL, true},
97 { NT_(".catalogs"), catalogscmd, NULL, false},
98 { NT_(".defaults"), defaultscmd, NULL, false},
99 { NT_(".die"), admin_cmds, NULL, false},
100 { NT_(".dump"), admin_cmds, NULL, false},
101 { NT_(".exit"), admin_cmds, NULL, false},
102 { NT_(".filesets"), filesetscmd, NULL, false},
103 { NT_(".help"), dot_help_cmd, NULL, false},
104 { NT_(".jobs"), jobscmd, NULL, true},
105 { NT_(".estimate"), dotestimatecmd, NULL, false},
106 { NT_(".levels"), levelscmd, NULL, false},
107 { NT_(".messages"), getmsgscmd, NULL, false},
108 { NT_(".msgs"), msgscmd, NULL, false},
109 { NT_(".pools"), poolscmd, NULL, true},
110 { NT_(".quit"), dot_quit_cmd, NULL, false},
111 { NT_(".putfile"), putfile_cmd, NULL, false}, /* use @putfile */
112 { NT_(".schedule"), schedulescmd, NULL, false},
113 { NT_(".sql"), sql_cmd, NULL, false},
114 { NT_(".status"), dot_status_cmd, NULL, false},
115 { NT_(".storage"), storagecmd, NULL, true},
116 { NT_(".volstatus"), volstatuscmd, NULL, true},
117 { NT_(".media"), mediacmd, NULL, true},
118 { NT_(".mediatypes"), mediatypescmd, NULL, true},
119 { NT_(".locations"), locationscmd, NULL, true},
120 { NT_(".actiononpurge"),aopcmd, NULL, true},
121 { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL, true},
122 { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles, NULL, true},
123 { NT_(".bvfs_get_volumes"),dot_bvfs_get_volumes,NULL, true},
124 { NT_(".bvfs_update"), dot_bvfs_update, NULL, true},
125 { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL, true},
126 { NT_(".bvfs_get_jobs"), dot_bvfs_get_jobs, NULL, true},
127 { NT_(".bvfs_get_bootstrap"), dot_bvfs_get_bootstrap,NULL, true},
128 { NT_(".bvfs_versions"), dot_bvfs_versions, NULL, true},
129 { NT_(".bvfs_get_delta"), dot_bvfs_get_delta, NULL, true},
130 { NT_(".bvfs_restore"), dot_bvfs_restore, NULL, true},
131 { NT_(".bvfs_cleanup"), dot_bvfs_cleanup, NULL, true},
132 { NT_(".bvfs_decode_lstat"),dot_bvfs_decode_lstat,NULL, true},
133 { NT_(".bvfs_clear_cache"),dot_bvfs_clear_cache,NULL, false},
134 { NT_(".bvfs_update_fv"),dot_bvfs_update_fv, NULL, true},
135 { NT_(".ls"), dot_ls_cmd, NULL, false},
136 { NT_(".types"), typescmd, NULL, false},
137 { NT_(".tags"), tagscmd, NULL, false}
139 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
142 * Execute a command from the UA
144 bool do_a_dot_command(UAContext *ua)
151 Dmsg1(1400, "Dot command: %s\n", ua->UA_sock?ua->UA_sock->msg:"");
152 if (ua->argc == 0 || !ua->UA_sock) {
156 len = strlen(ua->argk[0]);
158 if (ua->api) ua->signal(BNET_CMD_BEGIN);
159 if (ua->api) ua->signal(BNET_CMD_OK);
160 return true; /* no op */
162 for (i=0; i<comsize; i++) { /* search for command */
163 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
164 /* Check if this command is authorized in RunScript */
165 if (ua->runscript && !commands[i].use_in_rs) {
166 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
170 /* Check if command permitted, but "quit" is always OK */
171 if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
172 strcmp(ua->argk[0], NT_(".api")) != 0 &&
173 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
174 Dmsg1(100, "not allowed %s\n", ua->cmd);
177 Dmsg1(100, "Cmd: %s\n", ua->cmd);
179 if (ua->api) ua->signal(BNET_CMD_BEGIN);
180 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
181 if (ua->api) ua->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
183 found = ua->UA_sock->is_stop() ? false : true;
188 ua->error_msg("%s%s", ua->argk[0], _(": is an invalid command.\n"));
197 static bool dot_ls_cmd(UAContext *ua, const char *cmd)
200 CLIENT *client = NULL;
205 jcr->setJobLevel(L_FULL);
206 i = find_arg_with_value(ua, NT_("client"));
208 client = GetClientResWithName(ua->argv[i]);
210 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
213 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
214 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
219 ua->error_msg(_("Client name missing.\n"));
223 i = find_arg_with_value(ua, NT_("path"));
228 ua->error_msg(_("path name missing.\n"));
232 jcr->client = client;
234 jcr->setJobType(JT_BACKUP);
235 jcr->start_time = time(NULL);
236 init_jcr_job_record(jcr); // need job
238 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
239 jcr->client->name(), jcr->client->address(buf.addr()), jcr->client->FDport);
241 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
242 ua->error_msg(_("Failed to connect to Client.\n"));
246 if (!send_ls_fileset(jcr, path)) {
247 ua->error_msg(_("Failed to send command to Client.\n"));
251 jcr->file_bsock->fsend("estimate listing=%d\n", 1);
252 while (jcr->file_bsock->recv() >= 0) {
253 ua->send_msg("%s", jcr->file_bsock->msg);
257 if (jcr->file_bsock) {
258 jcr->file_bsock->signal(BNET_TERMINATE);
259 free_bsock(ua->jcr->file_bsock);
264 static void bvfs_set_acl(UAContext *ua, Bvfs *bvfs)
270 /* If no console resource => default console and all is permitted */
274 bvfs->set_job_acl(ua->cons->ACL_lists[Job_ACL]);
275 bvfs->set_client_acl(ua->cons->ACL_lists[Client_ACL]);
276 bvfs->set_fileset_acl(ua->cons->ACL_lists[FileSet_ACL]);
277 bvfs->set_pool_acl(ua->cons->ACL_lists[Pool_ACL]);
280 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd)
286 int pos = find_arg_with_value(ua, "lstat");
289 for (char *p = ua->argv[pos] ; *p ; p++) {
290 if (! (B_ISALPHA(*p) || B_ISDIGIT(*p) || B_ISSPACE(*p) || *p == '/' || *p == '+' || *p == '-')) {
291 ua->error_msg("Can't accept %c in lstat\n", *p);
296 decode_stat(ua->argv[pos], &sp, sizeof(sp), &LinkFI);
297 encode_mode(sp.st_mode, buf);
298 Mmsg(q, "st_nlink=%lld\nst_mode=%lld\nperm=%s\nst_uid=%lld\nst_gid=%lld\n"
299 "st_size=%lld\nst_blocks=%lld\nst_ino=%lld\nst_ctime=%lld\n"
300 "st_mtime=%lld\nst_atime=%lld\nst_dev=%lld\nLinkFI=%lld\n",
301 (int64_t) sp.st_nlink,
302 (int64_t) sp.st_mode,
306 (int64_t) sp.st_size,
307 (int64_t) sp.st_blocks,
309 (int64_t) sp.st_ctime,
310 (int64_t) sp.st_mtime,
311 (int64_t) sp.st_atime,
316 ua->send_msg("%s", q.c_str());
321 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
323 if (!open_new_client_db(ua)) {
327 int pos = find_arg_with_value(ua, "jobid");
328 if (pos != -1 && is_a_number_list(ua->argv[pos])) {
329 if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) {
330 ua->error_msg("ERROR: BVFS reported a problem for %s\n",
334 /* update cache for all jobids */
335 bvfs_update_cache(ua->jcr, ua->db);
341 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd)
343 int pos = find_arg_with_value(ua, "jobid");
345 if (pos == -1 || !is_a_number_list(ua->argv[pos])) {
346 ua->error_msg("Expecting to find jobid=1,2,3 argument\n");
350 if (!open_new_client_db(ua)) {
354 bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
355 bvfs_update_fv_cache(ua->jcr, ua->db, ua->argv[pos]);
357 ua->info_msg("OK\n");
362 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd)
364 if (!open_client_db(ua)) {
368 int pos = find_arg(ua, "yes");
370 Bvfs fs(ua->jcr, ua->db);
372 ua->info_msg("OK\n");
374 ua->error_msg("Can't find 'yes' argument\n");
380 static int bvfs_result_handler(void *ctx, int fields, char **row)
382 UAContext *ua = (UAContext *)ctx;
385 char *fileid=row[BVFS_FileId];
386 char *lstat=row[BVFS_LStat];
387 char *jobid=row[BVFS_JobId];
389 char empty[] = "A A A A A A A A A A A A A A";
392 /* We need to deal with non existant path */
393 if (!fileid || !is_a_number(fileid)) {
399 memset(&statp, 0, sizeof(struct stat));
400 decode_stat(lstat, &statp, sizeof(statp), &LinkFI);
401 Dmsg1(100, "type=%s\n", row[0]);
402 if (bvfs_is_dir(row)) {
403 char *path = bvfs_basename_dir(row[BVFS_Name]);
404 ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
407 } else if (bvfs_is_version(row)) {
408 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
409 row[BVFS_FilenameId], fileid, jobid,
410 lstat, row[BVFS_Md5], row[BVFS_VolName],
411 row[BVFS_VolInchanger]);
413 } else if (bvfs_is_file(row)) {
414 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
415 row[BVFS_FilenameId], fileid, jobid,
416 lstat, row[BVFS_Name]);
418 } else if (bvfs_is_volume_list(row)) {
419 ua->send_msg("%s\t%s\n", row[BVFS_VolName],
420 row[BVFS_VolInchanger]);
422 } else if (bvfs_is_delta_list(row)) {
423 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
424 row[BVFS_FilenameId], fileid, jobid,
425 lstat, row[BVFS_DeltaSeq], row[BVFS_JobTDate]);
431 static bool bvfs_parse_arg_version(UAContext *ua,
442 for (int i=1; i<ua->argc; i++) {
443 if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
444 if (is_a_number(ua->argv[i])) {
445 *fnid = str_to_int64(ua->argv[i]);
449 if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
450 *client = ua->argv[i];
453 if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
457 if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
461 return (*client && *fnid > 0);
464 static bool bvfs_parse_arg(UAContext *ua,
465 DBId_t *pathid, char **path, char **jobid,
467 int *limit, int *offset)
478 for (int i=1; i<ua->argc; i++) {
482 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
483 if (is_a_number(ua->argv[i])) {
484 *pathid = str_to_int64(ua->argv[i]);
488 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
492 if (strcasecmp(ua->argk[i], NT_("username")) == 0) {
493 *username = ua->argv[i];
496 if (jobid && strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
497 if (is_a_number_list(ua->argv[i])) {
498 *jobid = ua->argv[i];
502 if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
504 memset(&jr, 0, sizeof(jr));
505 bstrncpy(jr.Job, ua->argv[i], sizeof(jr.Job));
506 if (!open_new_client_db(ua)) {
509 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
512 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
515 /* Store the jobid after the ua->cmd, a bit kluggy */
516 int len = strlen(ua->cmd);
517 ua->cmd = check_pool_memory_size(ua->cmd, len + 1 + 50);
518 *jobid = edit_uint64(jr.JobId, ua->cmd + len + 1);
521 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
522 if (is_a_number(ua->argv[i])) {
523 *limit = str_to_int64(ua->argv[i]);
527 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
528 if (is_a_number(ua->argv[i])) {
529 *offset = str_to_int64(ua->argv[i]);
534 if (jobid && *jobid == NULL) {
538 if (!(*pathid || *path)) {
545 /* .bvfs_cleanup path=b2XXXXX
547 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
550 if ((i = find_arg_with_value(ua, "path")) >= 0) {
551 if (!open_client_db(ua)) {
554 Bvfs fs(ua->jcr, ua->db);
555 fs.drop_restore_list(ua->argv[i]);
560 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
562 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
565 int limit=2000, offset=0, i;
566 char *path=NULL, *jobid=NULL, *username=NULL;
567 char *empty = (char *)"";
568 char *fileid, *dirid, *hardlink;
570 fileid = dirid = hardlink = empty;
572 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
573 &limit, &offset) || !path)
575 ua->error_msg("Can't find jobid, pathid or path argument\n");
576 return true; /* not enough param */
579 if (!open_new_client_db(ua)) {
583 Bvfs fs(ua->jcr, ua->db);
584 bvfs_set_acl(ua, &fs);
585 fs.set_username(username);
586 fs.set_jobids(jobid);
588 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
589 fileid = ua->argv[i];
591 if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
594 if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
595 hardlink = ua->argv[i];
597 if ((i = find_arg(ua, "nodelta")) >= 0) {
598 fs.set_compute_delta(false);
600 if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
601 ua->send_msg("OK\n");
603 ua->error_msg("Cannot create restore list.\n");
609 /* Get a bootstrap for a given bvfs restore session
610 * .bvfs_get_bootstrap path=b21xxxxxx
617 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd)
619 RESTORE_CTX rx; /* restore context */
620 POOLMEM *buf = get_pool_memory(PM_MESSAGE);
624 if (!open_new_client_db(ua)) {
625 ua->error_msg("ERROR: Unable to open database\n");
628 pos = find_arg_with_value(ua, "path");
630 ua->error_msg("ERROR: Unable to get path argument\n");
634 insert_table_into_findex_list(ua, &rx, ua->argv[pos]);
636 if (rx.bsr_list->size() > 0) {
637 if (!complete_bsr(ua, rx.bsr_list)) { /* find Vol, SessId, SessTime from JobIds */
638 ua->error_msg("ERROR: Unable to construct a valid BSR. Cannot continue.\n");
641 if (!(rx.selected_files = write_bsr_file(ua, rx))) {
642 ua->error_msg("ERROR: No files selected to be restored.\n");
645 FILE *fp = bfopen(ua->jcr->RestoreBootstrap, "r");
647 ua->error_msg("ERROR: Unable to open bootstrap file\n");
650 while (bfgets(buf, fp)) {
651 ua->send_msg("%s", buf);
655 ua->error_msg("ERROR: Unable to find files to restore\n");
660 if (ua->jcr->unlink_bsr) {
661 unlink(ua->jcr->RestoreBootstrap);
662 ua->jcr->unlink_bsr = false;
664 free_pool_memory(buf);
670 * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
675 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
679 char *path=NULL, *jobid=NULL, *username=NULL;
681 int limit=2000, offset=0;
684 bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
686 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
687 if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
688 ua->error_msg("Can't find jobid, pathid or path argument\n");
692 filename = ua->argv[i];
694 } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
695 if (!is_a_number(ua->argv[i])) {
696 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
699 fileid = str_to_int64(ua->argv[i]);
702 if (!open_new_client_db(ua)) {
706 Bvfs fs(ua->jcr, ua->db);
707 bvfs_set_acl(ua, &fs);
708 fs.set_username(username);
709 fs.set_handler(bvfs_result_handler, ua);
717 fs.get_volumes(fileid);
724 * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
725 * .bvfs_lsfiles jobid=1,2,3,4 path=/
727 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
730 int limit=2000, offset=0;
731 char *path=NULL, *jobid=NULL, *username=NULL;
732 char *pattern=NULL, *filename=NULL;
736 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
739 ua->error_msg("Can't find jobid, pathid or path argument\n");
740 return true; /* not enough param */
742 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
743 pattern = ua->argv[i];
745 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
746 filename = ua->argv[i];
749 if (!open_new_client_db(ua)) {
753 Bvfs fs(ua->jcr, ua->db);
754 bvfs_set_acl(ua, &fs);
755 fs.set_username(username);
756 fs.set_jobids(jobid);
757 fs.set_handler(bvfs_result_handler, ua);
759 fs.set_offset(offset);
762 fs.set_pattern(pattern);
765 fs.set_filename(filename);
768 ok = fs.ch_dir(pathid);
770 ok = fs.ch_dir(path);
784 * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
785 * .bvfs_lsdirs jobid=1,2,3,4 path=/
786 * .bvfs_lsdirs jobid=1,2,3,4 path=
788 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
791 int limit=2000, offset=0;
792 char *path=NULL, *jobid=NULL, *username=NULL;
798 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
801 ua->error_msg("Can't find jobid, pathid or path argument\n");
802 return true; /* not enough param */
805 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
806 pattern = ua->argv[i];
809 dironly = find_arg(ua, "dironly");
811 if (!open_new_client_db(ua)) {
815 Bvfs fs(ua->jcr, ua->db);
816 bvfs_set_acl(ua, &fs);
817 fs.set_username(username);
818 fs.set_jobids(jobid);
820 fs.set_handler(bvfs_result_handler, ua);
821 fs.set_offset(offset);
825 fs.set_pattern(pattern);
829 ok = fs.ch_dir(pathid);
831 ok = fs.ch_dir(path);
838 fs.ls_special_dirs();
849 * .bvfs_get_delta fileid=10
852 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd)
858 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
859 if (!is_a_number(ua->argv[i])) {
860 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
863 fileid = str_to_int64(ua->argv[i]);
866 ua->error_msg("Expecting FileId\n");
870 if (!open_new_client_db(ua)) {
873 Bvfs fs(ua->jcr, ua->db);
874 bvfs_set_acl(ua, &fs);
875 fs.set_handler(bvfs_result_handler, ua);
877 ret = fs.get_delta(fileid);
883 * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
886 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
890 int limit=2000, offset=0;
891 char *path=NULL, *client=NULL, *username=NULL;
892 bool copies=false, versions=false;
893 if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
896 ua->error_msg("Can't find pathid or path argument\n");
897 return true; /* not enough param */
900 if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
902 ua->error_msg("Can't find client or fnid argument\n");
903 return true; /* not enough param */
906 if (!open_new_client_db(ua)) {
910 Bvfs fs(ua->jcr, ua->db);
911 bvfs_set_acl(ua, &fs);
913 fs.set_see_all_versions(versions);
914 fs.set_see_copies(copies);
915 fs.set_handler(bvfs_result_handler, ua);
916 fs.set_offset(offset);
919 fs.get_all_file_versions(pathid, fnid, client);
925 /* .bvfs_get_jobids jobid=1
926 * -> returns needed jobids to restore
927 * .bvfs_get_jobids ujobid=xxx only
928 * -> returns the jobid of the job
929 * .bvfs_get_jobids jobid=1 jobname
930 * -> returns the jobname
931 * .bvfs_get_jobids client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
932 * -> returns all jobid for the client
933 * .bvfs_get_jobids client=xxx count
934 * -> returns the number of jobids for the client
935 * .bvfs_get_jobids jobid=1 all
936 * -> returns needed jobids to restore with all filesets a JobId=1 time
937 * .bvfs_get_jobids job=XXXXX
938 * -> returns needed jobids to restore with the jobname
939 * .bvfs_get_jobids ujobid=JobName
940 * -> returns needed jobids to restore
942 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
945 memset(&jr, 0, sizeof(JOB_DBR));
947 db_list_ctx jobids, tempids;
951 dbid_list ids; /* Store all FileSetIds for this client */
953 if (!open_new_client_db(ua)) {
957 Bvfs fs(ua->jcr, ua->db);
958 bvfs_set_acl(ua, &fs);
960 if ((pos = find_arg_with_value(ua, "username")) >= 0) {
961 fs.set_username(ua->argv[pos]);
964 if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
965 bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
968 if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
969 jr.JobId = str_to_int64(ua->argv[pos]);
971 /* Guess JobId from Job name, take the last successful jobid */
972 } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
977 bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
978 /* TODO: enhance this function to take client and/or fileset as argument*/
980 job = GetJobResWithName(jr.Name);
982 ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
988 "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
989 "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
990 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
991 "ORDER By JobTDate DESC LIMIT 1",
992 job->client->name(), job->fileset->name());
993 ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
997 ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
1002 /* Get JobId from ujobid */
1003 } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
1004 bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
1006 /* Return all backup jobid for a client */
1007 } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
1014 cli = GetClientResWithName(ua->argv[pos]);
1016 ua->error_msg(_("Unable to get Client record for Client=%s\n"),
1022 bvfs_get_filter(ua, where, limit, sizeof(limit));
1026 "FROM Job JOIN Client USING (ClientId) "
1027 "WHERE Client.Name = '%s' "
1028 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') %s "
1029 "ORDER By JobTDate ASC %s",
1030 cli->name(), where.c_str(), limit);
1031 ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
1035 ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
1039 nbjobs = fs.set_jobids(jobids.list);
1041 /* Apply the ACL filter on JobIds */
1042 if (find_arg(ua, "count") >= 0) {
1043 ua->send_msg("%d\n", nbjobs);
1046 ua->send_msg("%s\n", fs.get_jobids());
1051 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
1052 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
1053 ua->cmd, db_strerror(ua->db));
1057 /* Display only the requested jobid or
1058 * When in level base, we don't rely on any Full/Incr/Diff
1060 if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
1061 /* Apply the ACL filter on JobIds */
1062 fs.set_jobid(jr.JobId);
1063 ua->send_msg("%s\n", fs.get_jobids());
1067 /* Display only the requested job name
1069 if (find_arg(ua, "jobname") > 0) {
1070 /* Apply the ACL filter on JobIds */
1071 fs.set_jobid(jr.JobId);
1072 if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
1073 ua->send_msg("%s\n", jr.Job);
1078 /* If we have the "all" option, we do a search on all defined fileset
1081 if (find_arg(ua, "all") > 0) {
1082 edit_int64(jr.ClientId, ed1);
1083 Mmsg(query, uar_sel_filesetid, ed1);
1084 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1087 ids.DBId[0] = jr.FileSetId;
1090 jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
1092 /* Foreach different FileSet, we build a restore jobid list */
1093 for (int i=0; i < ids.num_ids; i++) {
1094 jr.FileSetId = ids.DBId[i];
1095 if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
1098 jobids.add(tempids);
1101 fs.set_jobids(jobids.list);
1102 ua->send_msg("%s\n", fs.get_jobids());
1106 static int jobs_handler(void *ctx, int num_field, char **row)
1108 UAContext *ua = (UAContext *)ctx;
1109 ua->send_msg("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
1113 static char *get_argument(UAContext *ua, const char *arg, char *esc, bool convert)
1116 if (((pos = find_arg_with_value(ua, arg)) < 0) ||
1117 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1121 db_escape_string(ua->jcr, ua->db, esc,
1122 ua->argv[pos], strlen(ua->argv[pos]));
1124 for (int i=0; esc[i] ; i++) {
1125 if (esc[i] == '*') {
1133 /* The DB should be locked */
1134 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len)
1137 char esc_name[MAX_ESCAPE_NAME_LENGTH];
1139 if (get_argument(ua, "jobname", esc_name, true) != NULL) {
1140 Mmsg(where, "AND Job.Job LIKE '%s' ", esc_name);
1143 if (get_argument(ua, "fileset", esc_name, true) != NULL) {
1144 Mmsg(tmp, "AND FileSet.FileSet LIKE '%s' ", esc_name);
1145 pm_strcat(where, tmp.c_str());
1148 if (get_argument(ua, "jobid", esc_name, false) != NULL) {
1149 Mmsg(tmp, "AND Job.JobId = '%s' ", esc_name);
1150 pm_strcat(where, tmp.c_str());
1153 if (get_argument(ua, "ujobid", esc_name, false) != NULL) {
1154 Mmsg(tmp, "AND Job.Job = '%s' ", esc_name);
1155 pm_strcat(where, tmp.c_str());
1158 if (get_argument(ua, "start", esc_name, false) != NULL) {
1159 Mmsg(tmp, "AND Job.StartTime >= '%s' ", esc_name);
1160 pm_strcat(where, tmp.c_str());
1163 if (get_argument(ua, "end", esc_name, false) != NULL) {
1164 Mmsg(tmp, "AND Job.EndTime <= '%s' ", esc_name);
1165 pm_strcat(where, tmp.c_str());
1169 if (get_argument(ua, "limit", esc_name, false) != NULL) {
1170 if (is_a_number(esc_name)) {
1171 bsnprintf(limit, len, "LIMIT %s ", esc_name);
1176 /* .bvfs_get_jobs client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
1177 * 1 yyyyy 1 Backup1_xxx_xxx_xxxx_xxx
1178 * 2 yyyyy 0 Backup1_xxx_xxx_xxxx_xxx
1180 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
1184 char esc_cli[MAX_ESCAPE_NAME_LENGTH];
1185 char limit[MAX_ESCAPE_NAME_LENGTH];
1186 if (!open_new_client_db(ua)) {
1190 if (((pos = find_arg_with_value(ua, "client")) < 0) ||
1191 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1196 /* TODO: Do checks on Jobs, FileSet, etc... */
1197 if (!acl_access_client_ok(ua, ua->argv[pos], JT_BACKUP_RESTORE)) {
1202 db_escape_string(ua->jcr, ua->db, esc_cli,
1203 ua->argv[pos], strlen(ua->argv[pos]));
1205 bvfs_get_filter(ua, where, limit, sizeof(limit));
1208 "SELECT JobId, JobTDate, HasCache, Job "
1209 "FROM Job JOIN Client USING (ClientId) JOIN FileSet USING (FileSetId) "
1210 "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
1212 "ORDER By JobTDate DESC %s",
1213 esc_cli, where.c_str(), limit);
1215 db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
1220 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
1226 static bool dot_help_cmd(UAContext *ua, const char *cmd)
1232 static bool getmsgscmd(UAContext *ua, const char *cmd)
1234 if (console_msg_pending) {
1235 do_messages(ua, cmd);
1241 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
1247 lstore.store = store;
1248 pm_strcpy(lstore.store_source, _("unknown source"));
1249 set_wstorage(jcr, &lstore);
1250 /* Try connecting for up to 15 seconds */
1251 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
1252 store->name(), store->address, store->SDport);
1253 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
1254 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1257 Dmsg0(120, _("Connected to storage daemon\n"));
1258 sd = jcr->store_bsock;
1259 sd->fsend("%s", cmd);
1260 if (sd->recv() >= 0) {
1261 ua->send_msg("%s", sd->msg);
1263 sd->signal(BNET_TERMINATE);
1264 free_bsock(ua->jcr->store_bsock);
1268 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
1272 /* Connect to File daemon */
1274 ua->jcr->client = client;
1275 /* Try to connect for 15 seconds */
1276 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1277 client->name(), client->address(buf.addr()), client->FDport);
1278 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1279 ua->error_msg(_("Failed to connect to Client.\n"));
1282 Dmsg0(120, "Connected to file daemon\n");
1283 fd = ua->jcr->file_bsock;
1284 fd->fsend("%s", cmd);
1285 if (fd->recv() >= 0) {
1286 ua->send_msg("%s", fd->msg);
1288 fd->signal(BNET_TERMINATE);
1289 free_bsock(ua->jcr->file_bsock);
1296 * .exit (no arg => .quit)
1298 static bool admin_cmds(UAContext *ua, const char *cmd)
1300 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1302 CLIENT *client=NULL;
1304 bool do_deadlock=false;
1305 const char *remote_cmd;
1309 if (strncmp(ua->argk[0], ".die", 4) == 0) {
1310 if (find_arg(ua, "deadlock") > 0) {
1312 remote_cmd = ".die deadlock";
1314 remote_cmd = ".die";
1316 } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1317 remote_cmd = "sm_dump";
1318 } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1319 remote_cmd = "exit";
1321 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1324 /* General debug? */
1325 for (i=1; i<ua->argc; i++) {
1326 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1327 strcasecmp(ua->argk[i], "director") == 0) {
1330 if (strcasecmp(ua->argk[i], "client") == 0 ||
1331 strcasecmp(ua->argk[i], "fd") == 0) {
1334 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1337 client = select_client_resource(ua, JT_SYSTEM);
1341 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1342 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1343 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1346 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1349 store = get_storage_resource(ua, false/*no default*/);
1354 if (!dir && !store && !client) {
1356 * We didn't find an appropriate keyword above, so
1359 start_prompt(ua, _("Available daemons are: \n"));
1360 add_prompt(ua, _("Director"));
1361 add_prompt(ua, _("Storage"));
1362 add_prompt(ua, _("Client"));
1363 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1364 case 0: /* Director */
1368 store = get_storage_resource(ua, false/*no default*/);
1371 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1379 do_storage_cmd(ua, store, remote_cmd);
1383 do_client_cmd(ua, client, remote_cmd);
1387 if (strncmp(remote_cmd, ".die", 4) == 0) {
1389 ua->send_msg(_("The Director will generate a deadlock.\n"));
1393 ua->send_msg(_("The Director will segment fault.\n"));
1394 a = jcr->JobId; /* ref NULL pointer */
1395 jcr->JobId = 1000; /* another ref NULL pointer */
1398 } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1399 sm_dump(false, true);
1400 } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1401 dot_quit_cmd(ua, cmd);
1411 * Dummy routine for non-development version
1413 static bool admin_cmds(UAContext *ua, const char *cmd)
1415 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1422 * Send a file to the director from bconsole @putfile command
1423 * The .putfile can not be used directly.
1425 static bool putfile_cmd(UAContext *ua, const char *cmd)
1427 int pos, i, pnl, fnl;
1429 POOLMEM *name = get_pool_memory(PM_FNAME);
1430 POOLMEM *path = get_pool_memory(PM_FNAME);
1431 POOLMEM *fname= get_pool_memory(PM_FNAME);
1432 const char *key = "putfile";
1435 if ((pos = find_arg_with_value(ua, "key")) > 0) {
1436 /* Check the string if the string is valid */
1437 for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1439 if (ua->argv[pos][i] == 0) {
1440 key = ua->argv[pos];
1443 ua->error_msg("Invalid key name for putfile command");
1449 /* the (intptr_t)ua will allow one file per console session */
1450 make_unique_filename(&name, (intptr_t)ua, (char *)key);
1452 fp = bfopen(name, "w");
1455 ua->error_msg("Unable to open destination file. ERR=%s\n",
1456 be.bstrerror(errno));
1461 while (ua->UA_sock->recv() > 0) {
1462 if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1464 ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1465 be.bstrerror(errno));
1467 /* TODO: Check if we need to quit here (data will still be in the
1472 split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1476 ua->send_msg("OK\n");
1479 ua->send_msg("ERROR\n");
1482 free_pool_memory(name);
1483 free_pool_memory(path);
1484 free_pool_memory(fname);
1491 /* .estimate command */
1492 static bool dotestimatecmd(UAContext *ua, const char *cmd)
1498 char *job = NULL, level = 0, *fileset = NULL, *client = NULL;
1499 memset(&jr, 0, sizeof(jr));
1501 for (int i = 1 ; i < ua->argc ; i++) {
1503 ua->error_msg(_("Invalid argument for %s\n"), ua->argk[i]);
1506 } else if (strcasecmp(ua->argk[i], "job") == 0) {
1509 } else if (strcasecmp(ua->argk[i], "level") == 0) {
1510 level = toupper(ua->argv[i][0]);
1512 } else if (strcasecmp(ua->argk[i], "fileset") == 0) {
1513 fileset = ua->argv[i];
1515 } else if (strcasecmp(ua->argk[i], "client") == 0) {
1516 client = ua->argv[i];
1520 ua->error_msg(_("Invalid argument for job\n"));
1523 if (!acl_access_ok(ua, Job_ACL, job) ||
1524 (fileset && !acl_access_ok(ua, FileSet_ACL, fileset)) ||
1525 (client && !acl_access_client_ok(ua, client, JT_BACKUP)))
1527 ua->error_msg(_("Access to specified Job, FileSet or Client not allowed.\n"));
1530 jres = (JOB *) GetResWithName(R_JOB, job);
1532 ua->error_msg(_("Invalid argument for job\n"));
1535 if (!open_client_db(ua)) {
1536 ua->error_msg(_("Unable to open the catalog.\n"));
1540 bstrncpy(jr.Name, jres->hdr.name, sizeof(jr.Name));
1541 jr.JobLevel = level ? level : jres->JobLevel;
1549 if (db_get_job_statistics(ua->jcr, ua->db, &jr)) {
1551 OutputWriter o(ua->api_opts);
1552 char *p = o.get_output(OT_START_OBJ,
1553 OT_JOBLEVEL, "level", jr.JobLevel,
1554 OT_INT, "nbjob", jr.CorrNbJob,
1555 OT_INT, "corrbytes", jr.CorrJobBytes,
1556 OT_SIZE, "jobbytes", jr.JobBytes,
1557 OT_INT, "corrfiles", jr.CorrJobFiles,
1558 OT_INT32, "jobfiles", jr.JobFiles,
1559 OT_INT, "duration", (int)0,
1560 OT_STRING, "job", jres->hdr.name,
1563 ua->send_msg("%s", p);
1565 /* We unlock the DB after the errmsg copy */
1566 pm_strcpy(ua->jcr->errmsg, ua->db->errmsg);
1568 ua->error_msg("Error with .estimate %s\n", ua->jcr->errmsg);
1575 * Can use an argument to filter on JobType
1576 * .jobs [type=B] or [type=!B]
1578 static bool jobscmd(UAContext *ua, const char *cmd)
1584 if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1585 if (ua->argv[pos][0] == '!') {
1587 type = ua->argv[pos][1];
1589 type = ua->argv[pos][0];
1593 foreach_res(job, R_JOB) {
1595 if ((exclude && type == job->JobType) || (!exclude && type != job->JobType)) {
1599 if (acl_access_ok(ua, Job_ACL, job->name())) {
1600 ua->send_msg("%s\n", job->name());
1607 static bool filesetscmd(UAContext *ua, const char *cmd)
1611 foreach_res(fs, R_FILESET) {
1612 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1613 ua->send_msg("%s\n", fs->name());
1620 static bool catalogscmd(UAContext *ua, const char *cmd)
1624 foreach_res(cat, R_CATALOG) {
1625 if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1626 ua->send_msg("%s\n", cat->name());
1633 /* This is not a good idea to lock the entire resource list to send information
1634 * on the network or query the DNS. So, we don't use the foreach_res() command
1635 * with a global lock and we do a copy of the client list in a specific list to
1636 * avoid any problem, I'm pretty sure we can use the res_head directly without
1637 * a global lock, but it needs testing to avoid race conditions.
1645 TmpClient(char *n, char *a):
1646 name(bstrdup(n)), address(bstrdup(a))
1655 static bool clientscmd(UAContext *ua, const char *cmd)
1659 const char *ip=NULL;
1661 alist *clientlist = NULL;
1665 if ((i = find_arg_with_value(ua, "address")) >= 0) {
1667 clientlist = New(alist(50, not_owned_by_alist));
1670 /* This is not a good idea to lock the entire resource list
1671 * to send information on the network or query the DNS. So,
1672 * we don't use the foreach_res() command with a global lock here.
1675 foreach_res(client, R_CLIENT) {
1676 if (acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
1678 elt = new TmpClient(client->name(), client->address(buf.addr()));
1679 clientlist->append(elt);
1682 /* do not check for a specific ip, display everything */
1683 ua->send_msg("%s\n", client->name());
1693 foreach_alist(elt, clientlist) {
1694 /* We look for a client that matches the specific ip address */
1695 dlist *addr_list=NULL;
1700 if (strcmp(elt->address, ip) == 0) {
1703 } else if ((addr_list = bnet_host2ipaddrs(elt->address, 0, &errstr)) == NULL) {
1704 Dmsg2(10, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n",
1705 elt->address, errstr);
1708 /* Try to find the ip address from the list, we might have
1709 * other ways to compare ip addresses
1711 foreach_dlist(ipaddr, addr_list) {
1712 if (strcmp(ip, ipaddr->get_address(buf, sizeof(buf))) == 0) {
1717 free_addresses(addr_list);
1721 ua->send_msg("%s\n", elt->name);
1725 /* Cleanup the temp list */
1726 foreach_alist(elt, clientlist) {
1733 static bool msgscmd(UAContext *ua, const char *cmd)
1737 foreach_res(msgs, R_MSGS) {
1738 ua->send_msg("%s\n", msgs->name());
1744 static bool poolscmd(UAContext *ua, const char *cmd)
1748 foreach_res(pool, R_POOL) {
1749 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1750 ua->send_msg("%s\n", pool->name());
1757 static bool schedulescmd(UAContext *ua, const char *cmd)
1761 foreach_res(sched, R_SCHEDULE) {
1762 if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1763 ua->send_msg("%s\n", sched->name());
1770 static bool storagecmd(UAContext *ua, const char *cmd)
1775 alist *already_in = NULL;
1777 /* .storage unique */
1778 if (find_arg(ua, "unique") > 0) {
1780 already_in = New(alist(10, owned_by_alist));
1784 foreach_res(store, R_STORAGE) {
1785 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1790 Mmsg(tmp, "%s:%d", store->address, store->SDport);
1791 foreach_alist(elt, already_in) { /* TODO: See if we need a hash or an ordered list here */
1792 if (strcmp(tmp.c_str(), elt) == 0) {
1798 already_in->append(bstrdup(tmp.c_str()));
1802 ua->send_msg("%s\n", store->name());
1813 static bool aopcmd(UAContext *ua, const char *cmd)
1815 ua->send_msg("None\n");
1816 ua->send_msg("Truncate\n");
1820 static bool typescmd(UAContext *ua, const char *cmd)
1822 ua->send_msg("Backup\n");
1823 ua->send_msg("Restore\n");
1824 ua->send_msg("Admin\n");
1825 ua->send_msg("Verify\n");
1826 ua->send_msg("Migrate\n");
1827 ua->send_msg("Copy\n");
1831 static bool tagscmd(UAContext *ua, const char *cmd)
1834 for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1835 ua->send_msg("%s\n", p);
1841 * If this command is called, it tells the director that we
1842 * are a program that wants a sort of API, and hence,
1843 * we will probably suppress certain output, include more
1844 * error codes, and most of all send back a good number
1845 * of new signals that indicate whether or not the command
1848 static bool api_cmd(UAContext *ua, const char *cmd)
1851 if (ua->argc >= 2) {
1852 ua->api = atoi(ua->argk[1]);
1854 /* Get output configuration options such as time format or separator */
1855 if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1856 bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1867 static int client_backups_handler(void *ctx, int num_field, char **row)
1869 UAContext *ua = (UAContext *)ctx;
1870 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1871 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1876 * Return the backups for this client
1878 * .backups client=xxx fileset=yyy
1881 static bool backupscmd(UAContext *ua, const char *cmd)
1883 if (!open_client_db(ua)) {
1886 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1887 strcmp(ua->argk[2], "fileset") != 0) {
1890 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE) ||
1891 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1892 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1895 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1896 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1897 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1903 static int sql_handler(void *ctx, int num_field, char **row)
1905 UAContext *ua = (UAContext *)ctx;
1906 POOL_MEM rows(PM_MESSAGE);
1908 /* Check for nonsense */
1909 if (num_field == 0 || row == NULL || row[0] == NULL) {
1910 return 0; /* nothing returned */
1912 for (int i=0; num_field--; i++) {
1914 pm_strcpy(rows, NPRT(row[0]));
1916 pm_strcat(rows, NPRT(row[i]));
1918 pm_strcat(rows, "\t");
1920 if (!rows.c_str() || !*rows.c_str()) {
1923 ua->send_msg("%s", rows.c_str());
1928 static bool sql_cmd(UAContext *ua, const char *cmd)
1931 if (!open_new_client_db(ua)) {
1934 index = find_arg_with_value(ua, "query");
1936 ua->error_msg(_("query keyword not found.\n"));
1939 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1940 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1941 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1947 static int one_handler(void *ctx, int num_field, char **row)
1949 UAContext *ua = (UAContext *)ctx;
1950 ua->send_msg("%s\n", row[0]);
1954 static bool mediatypescmd(UAContext *ua, const char *cmd)
1956 if (!open_client_db(ua)) {
1959 if (!db_sql_query(ua->db,
1960 "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1961 one_handler, (void *)ua))
1963 ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1968 static bool mediacmd(UAContext *ua, const char *cmd)
1970 if (!open_client_db(ua)) {
1973 if (!db_sql_query(ua->db,
1974 "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1975 one_handler, (void *)ua))
1977 ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1982 static bool locationscmd(UAContext *ua, const char *cmd)
1984 if (!open_client_db(ua)) {
1987 if (!db_sql_query(ua->db,
1988 "SELECT DISTINCT Location FROM Location ORDER BY Location",
1989 one_handler, (void *)ua))
1991 ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1996 static bool levelscmd(UAContext *ua, const char *cmd)
1999 /* Note some levels are blank, which means none is needed */
2000 if (ua->argc == 1) {
2001 for (i=0; joblevels[i].level_name; i++) {
2002 if (joblevels[i].level_name[0] != ' ') {
2003 ua->send_msg("%s\n", joblevels[i].level_name);
2006 } else if (ua->argc == 2) {
2008 /* Assume that first argument is the Job Type */
2009 for (i=0; jobtypes[i].type_name; i++) {
2010 if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
2011 jobtype = jobtypes[i].job_type;
2015 for (i=0; joblevels[i].level_name; i++) {
2016 if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
2017 ua->send_msg("%s\n", joblevels[i].level_name);
2025 static bool volstatuscmd(UAContext *ua, const char *cmd)
2027 ua->send_msg("Append\n");
2028 ua->send_msg("Full\n");
2029 ua->send_msg("Used\n");
2030 ua->send_msg("Recycle\n");
2031 ua->send_msg("Purged\n");
2032 ua->send_msg("Cleaning\n");
2033 ua->send_msg("Error\n");
2038 * Return default values for a job
2040 static bool defaultscmd(UAContext *ua, const char *cmd)
2043 if (ua->argc != 2 || !ua->argv[1]) {
2047 /* Send Job defaults */
2048 if (strcmp(ua->argk[1], "job") == 0) {
2049 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
2052 JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
2055 ua->send_msg("job=%s", job->name());
2056 ua->send_msg("pool=%s", job->pool->name());
2057 ua->send_msg("messages=%s", job->messages->name());
2058 ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
2059 get_job_storage(&store, job, NULL);
2060 ua->send_msg("storage=%s", store.store->name());
2061 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
2062 ua->send_msg("level=%s", level_to_str(job->JobLevel));
2063 ua->send_msg("type=%s", job_type_to_str(job->JobType));
2064 ua->send_msg("fileset=%s", job->fileset->name());
2065 ua->send_msg("enabled=%d", job->is_enabled());
2066 ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
2067 ua->send_msg("priority=%d", job->Priority);
2070 /* Send Pool defaults */
2071 else if (strcmp(ua->argk[1], "pool") == 0) {
2072 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
2075 POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
2077 ua->send_msg("pool=%s", pool->name());
2078 ua->send_msg("pool_type=%s", pool->pool_type);
2079 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
2080 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
2081 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
2082 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
2083 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
2084 ua->send_msg("max_volumes=%d", pool->max_volumes);
2085 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
2086 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
2087 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
2088 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
2089 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
2090 ua->send_msg("auto_prune=%d", pool->AutoPrune);
2091 ua->send_msg("recycle=%d", pool->Recycle);
2092 ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
2093 ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
2096 /* Send Storage defaults */
2097 else if (strcmp(ua->argk[1], "storage") == 0) {
2098 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
2101 STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
2104 ua->send_msg("storage=%s", storage->name());
2105 ua->send_msg("address=%s", storage->address);
2106 ua->send_msg("enabled=%d", storage->is_enabled());
2107 ua->send_msg("media_type=%s", storage->media_type);
2108 ua->send_msg("sdport=%d", storage->SDport);
2109 device = (DEVICE *)storage->device->first();
2110 ua->send_msg("device=%s", device->name());
2111 if (storage->device && storage->device->size() > 1) {
2112 while ((device = (DEVICE *)storage->device->next())) {
2113 ua->send_msg(",%s", device->name());
2118 /* Send Client defaults */
2119 else if (strcmp(ua->argk[1], "client") == 0) {
2120 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE)) {
2123 CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
2126 ua->send_msg("client=%s", client->name());
2127 ua->send_msg("address=%s", client->address(buf.addr()));
2128 ua->send_msg("fdport=%d", client->FDport);
2129 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
2130 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
2131 ua->send_msg("autoprune=%d", client->AutoPrune);
2132 ua->send_msg("catalog=%s", client->catalog->name());