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)
199 CLIENT *client = NULL;
204 jcr->setJobLevel(L_FULL);
205 i = find_arg_with_value(ua, NT_("client"));
207 client = GetClientResWithName(ua->argv[i]);
209 ua->error_msg(_("Client \"%s\" not found.\n"), ua->argv[i]);
212 if (!acl_access_client_ok(ua, client->name(), JT_BACKUP)) {
213 ua->error_msg(_("No authorization for Client \"%s\"\n"), client->name());
218 ua->error_msg(_("Client name missing.\n"));
222 i = find_arg_with_value(ua, NT_("path"));
227 ua->error_msg(_("path name missing.\n"));
231 jcr->client = client;
233 jcr->setJobType(JT_BACKUP);
234 jcr->start_time = time(NULL);
235 init_jcr_job_record(jcr); // need job
237 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
238 jcr->client->name(), jcr->client->address(), jcr->client->FDport);
240 if (!connect_to_file_daemon(jcr, 1, 15, 0)) {
241 ua->error_msg(_("Failed to connect to Client.\n"));
245 if (!send_ls_fileset(jcr, path)) {
246 ua->error_msg(_("Failed to send command to Client.\n"));
250 jcr->file_bsock->fsend("estimate listing=%d\n", 1);
251 while (jcr->file_bsock->recv() >= 0) {
252 ua->send_msg("%s", jcr->file_bsock->msg);
256 if (jcr->file_bsock) {
257 jcr->file_bsock->signal(BNET_TERMINATE);
258 free_bsock(ua->jcr->file_bsock);
263 static void bvfs_set_acl(UAContext *ua, Bvfs *bvfs)
269 /* If no console resource => default console and all is permitted */
273 bvfs->set_job_acl(ua->cons->ACL_lists[Job_ACL]);
274 bvfs->set_client_acl(ua->cons->ACL_lists[Client_ACL]);
275 bvfs->set_fileset_acl(ua->cons->ACL_lists[FileSet_ACL]);
276 bvfs->set_pool_acl(ua->cons->ACL_lists[Pool_ACL]);
279 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd)
285 int pos = find_arg_with_value(ua, "lstat");
288 for (char *p = ua->argv[pos] ; *p ; p++) {
289 if (! (B_ISALPHA(*p) || B_ISDIGIT(*p) || B_ISSPACE(*p) || *p == '/' || *p == '+' || *p == '-')) {
290 ua->error_msg("Can't accept %c in lstat\n", *p);
295 decode_stat(ua->argv[pos], &sp, sizeof(sp), &LinkFI);
296 encode_mode(sp.st_mode, buf);
297 Mmsg(q, "st_nlink=%lld\nst_mode=%lld\nperm=%s\nst_uid=%lld\nst_gid=%lld\n"
298 "st_size=%lld\nst_blocks=%lld\nst_ino=%lld\nst_ctime=%lld\n"
299 "st_mtime=%lld\nst_atime=%lld\nst_dev=%lld\nLinkFI=%lld\n",
300 (int64_t) sp.st_nlink,
301 (int64_t) sp.st_mode,
305 (int64_t) sp.st_size,
306 (int64_t) sp.st_blocks,
308 (int64_t) sp.st_ctime,
309 (int64_t) sp.st_mtime,
310 (int64_t) sp.st_atime,
315 ua->send_msg("%s", q.c_str());
320 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
322 if (!open_new_client_db(ua)) {
326 int pos = find_arg_with_value(ua, "jobid");
327 if (pos != -1 && is_a_number_list(ua->argv[pos])) {
328 if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) {
329 ua->error_msg("ERROR: BVFS reported a problem for %s\n",
333 /* update cache for all jobids */
334 bvfs_update_cache(ua->jcr, ua->db);
340 static bool dot_bvfs_update_fv(UAContext *ua, const char *cmd)
342 int pos = find_arg_with_value(ua, "jobid");
344 if (pos == -1 || !is_a_number_list(ua->argv[pos])) {
345 ua->error_msg("Expecting to find jobid=1,2,3 argument\n");
349 if (!open_new_client_db(ua)) {
353 bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos]);
354 bvfs_update_fv_cache(ua->jcr, ua->db, ua->argv[pos]);
356 ua->info_msg("OK\n");
361 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd)
363 if (!open_client_db(ua)) {
367 int pos = find_arg(ua, "yes");
369 Bvfs fs(ua->jcr, ua->db);
371 ua->info_msg("OK\n");
373 ua->error_msg("Can't find 'yes' argument\n");
379 static int bvfs_result_handler(void *ctx, int fields, char **row)
381 UAContext *ua = (UAContext *)ctx;
384 char *fileid=row[BVFS_FileId];
385 char *lstat=row[BVFS_LStat];
386 char *jobid=row[BVFS_JobId];
388 char empty[] = "A A A A A A A A A A A A A A";
391 /* We need to deal with non existant path */
392 if (!fileid || !is_a_number(fileid)) {
398 memset(&statp, 0, sizeof(struct stat));
399 decode_stat(lstat, &statp, sizeof(statp), &LinkFI);
400 Dmsg1(100, "type=%s\n", row[0]);
401 if (bvfs_is_dir(row)) {
402 char *path = bvfs_basename_dir(row[BVFS_Name]);
403 ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
406 } else if (bvfs_is_version(row)) {
407 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
408 row[BVFS_FilenameId], fileid, jobid,
409 lstat, row[BVFS_Md5], row[BVFS_VolName],
410 row[BVFS_VolInchanger]);
412 } else if (bvfs_is_file(row)) {
413 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
414 row[BVFS_FilenameId], fileid, jobid,
415 lstat, row[BVFS_Name]);
417 } else if (bvfs_is_volume_list(row)) {
418 ua->send_msg("%s\t%s\n", row[BVFS_VolName],
419 row[BVFS_VolInchanger]);
421 } else if (bvfs_is_delta_list(row)) {
422 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
423 row[BVFS_FilenameId], fileid, jobid,
424 lstat, row[BVFS_DeltaSeq], row[BVFS_JobTDate]);
430 static bool bvfs_parse_arg_version(UAContext *ua,
441 for (int i=1; i<ua->argc; i++) {
442 if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
443 if (is_a_number(ua->argv[i])) {
444 *fnid = str_to_int64(ua->argv[i]);
448 if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
449 *client = ua->argv[i];
452 if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
456 if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
460 return (*client && *fnid > 0);
463 static bool bvfs_parse_arg(UAContext *ua,
464 DBId_t *pathid, char **path, char **jobid,
466 int *limit, int *offset)
477 for (int i=1; i<ua->argc; i++) {
481 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
482 if (is_a_number(ua->argv[i])) {
483 *pathid = str_to_int64(ua->argv[i]);
487 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
491 if (strcasecmp(ua->argk[i], NT_("username")) == 0) {
492 *username = ua->argv[i];
495 if (jobid && strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
496 if (is_a_number_list(ua->argv[i])) {
497 *jobid = ua->argv[i];
501 if (strcasecmp(ua->argk[i], NT_("ujobid")) == 0) {
503 memset(&jr, 0, sizeof(jr));
504 bstrncpy(jr.Job, ua->argv[i], sizeof(jr.Job));
505 if (!open_new_client_db(ua)) {
508 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
511 if (!acl_access_ok(ua, Job_ACL, jr.Name)) {
514 /* Store the jobid after the ua->cmd, a bit kluggy */
515 int len = strlen(ua->cmd);
516 ua->cmd = check_pool_memory_size(ua->cmd, len + 1 + 50);
517 *jobid = edit_uint64(jr.JobId, ua->cmd + len + 1);
520 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
521 if (is_a_number(ua->argv[i])) {
522 *limit = str_to_int64(ua->argv[i]);
526 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
527 if (is_a_number(ua->argv[i])) {
528 *offset = str_to_int64(ua->argv[i]);
533 if (jobid && *jobid == NULL) {
537 if (!(*pathid || *path)) {
544 /* .bvfs_cleanup path=b2XXXXX
546 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
549 if ((i = find_arg_with_value(ua, "path")) >= 0) {
550 if (!open_client_db(ua)) {
553 Bvfs fs(ua->jcr, ua->db);
554 fs.drop_restore_list(ua->argv[i]);
559 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
561 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
564 int limit=2000, offset=0, i;
565 char *path=NULL, *jobid=NULL, *username=NULL;
566 char *empty = (char *)"";
567 char *fileid, *dirid, *hardlink;
569 fileid = dirid = hardlink = empty;
571 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
572 &limit, &offset) || !path)
574 ua->error_msg("Can't find jobid, pathid or path argument\n");
575 return true; /* not enough param */
578 if (!open_new_client_db(ua)) {
582 Bvfs fs(ua->jcr, ua->db);
583 bvfs_set_acl(ua, &fs);
584 fs.set_username(username);
585 fs.set_jobids(jobid);
587 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
588 fileid = ua->argv[i];
590 if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
593 if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
594 hardlink = ua->argv[i];
596 if ((i = find_arg(ua, "nodelta")) >= 0) {
597 fs.set_compute_delta(false);
599 if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
600 ua->send_msg("OK\n");
602 ua->error_msg("Cannot create restore list.\n");
608 /* Get a bootstrap for a given bvfs restore session
609 * .bvfs_get_bootstrap path=b21xxxxxx
616 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd)
618 RESTORE_CTX rx; /* restore context */
619 POOLMEM *buf = get_pool_memory(PM_MESSAGE);
623 if (!open_new_client_db(ua)) {
624 ua->error_msg("ERROR: Unable to open database\n");
627 pos = find_arg_with_value(ua, "path");
629 ua->error_msg("ERROR: Unable to get path argument\n");
633 insert_table_into_findex_list(ua, &rx, ua->argv[pos]);
635 if (rx.bsr_list->size() > 0) {
636 if (!complete_bsr(ua, rx.bsr_list)) { /* find Vol, SessId, SessTime from JobIds */
637 ua->error_msg("ERROR: Unable to construct a valid BSR. Cannot continue.\n");
640 if (!(rx.selected_files = write_bsr_file(ua, rx))) {
641 ua->error_msg("ERROR: No files selected to be restored.\n");
644 FILE *fp = bfopen(ua->jcr->RestoreBootstrap, "r");
646 ua->error_msg("ERROR: Unable to open bootstrap file\n");
649 while (bfgets(buf, fp)) {
650 ua->send_msg("%s", buf);
654 ua->error_msg("ERROR: Unable to find files to restore\n");
659 if (ua->jcr->unlink_bsr) {
660 unlink(ua->jcr->RestoreBootstrap);
661 ua->jcr->unlink_bsr = false;
663 free_pool_memory(buf);
669 * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
674 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
678 char *path=NULL, *jobid=NULL, *username=NULL;
680 int limit=2000, offset=0;
683 bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
685 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
686 if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
687 ua->error_msg("Can't find jobid, pathid or path argument\n");
691 filename = ua->argv[i];
693 } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
694 if (!is_a_number(ua->argv[i])) {
695 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
698 fileid = str_to_int64(ua->argv[i]);
701 if (!open_new_client_db(ua)) {
705 Bvfs fs(ua->jcr, ua->db);
706 bvfs_set_acl(ua, &fs);
707 fs.set_username(username);
708 fs.set_handler(bvfs_result_handler, ua);
716 fs.get_volumes(fileid);
723 * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
724 * .bvfs_lsfiles jobid=1,2,3,4 path=/
726 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
729 int limit=2000, offset=0;
730 char *path=NULL, *jobid=NULL, *username=NULL;
731 char *pattern=NULL, *filename=NULL;
735 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
738 ua->error_msg("Can't find jobid, pathid or path argument\n");
739 return true; /* not enough param */
741 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
742 pattern = ua->argv[i];
744 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
745 filename = ua->argv[i];
748 if (!open_new_client_db(ua)) {
752 Bvfs fs(ua->jcr, ua->db);
753 bvfs_set_acl(ua, &fs);
754 fs.set_username(username);
755 fs.set_jobids(jobid);
756 fs.set_handler(bvfs_result_handler, ua);
758 fs.set_offset(offset);
761 fs.set_pattern(pattern);
764 fs.set_filename(filename);
767 ok = fs.ch_dir(pathid);
769 ok = fs.ch_dir(path);
783 * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
784 * .bvfs_lsdirs jobid=1,2,3,4 path=/
785 * .bvfs_lsdirs jobid=1,2,3,4 path=
787 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
790 int limit=2000, offset=0;
791 char *path=NULL, *jobid=NULL, *username=NULL;
797 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
800 ua->error_msg("Can't find jobid, pathid or path argument\n");
801 return true; /* not enough param */
804 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
805 pattern = ua->argv[i];
808 dironly = find_arg(ua, "dironly");
810 if (!open_new_client_db(ua)) {
814 Bvfs fs(ua->jcr, ua->db);
815 bvfs_set_acl(ua, &fs);
816 fs.set_username(username);
817 fs.set_jobids(jobid);
819 fs.set_handler(bvfs_result_handler, ua);
820 fs.set_offset(offset);
824 fs.set_pattern(pattern);
828 ok = fs.ch_dir(pathid);
830 ok = fs.ch_dir(path);
837 fs.ls_special_dirs();
848 * .bvfs_get_delta fileid=10
851 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd)
857 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
858 if (!is_a_number(ua->argv[i])) {
859 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
862 fileid = str_to_int64(ua->argv[i]);
865 ua->error_msg("Expecting FileId\n");
869 if (!open_new_client_db(ua)) {
872 Bvfs fs(ua->jcr, ua->db);
873 bvfs_set_acl(ua, &fs);
874 fs.set_handler(bvfs_result_handler, ua);
876 ret = fs.get_delta(fileid);
882 * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
885 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
889 int limit=2000, offset=0;
890 char *path=NULL, *client=NULL, *username=NULL;
891 bool copies=false, versions=false;
892 if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
895 ua->error_msg("Can't find pathid or path argument\n");
896 return true; /* not enough param */
899 if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
901 ua->error_msg("Can't find client or fnid argument\n");
902 return true; /* not enough param */
905 if (!open_new_client_db(ua)) {
909 Bvfs fs(ua->jcr, ua->db);
910 bvfs_set_acl(ua, &fs);
912 fs.set_see_all_versions(versions);
913 fs.set_see_copies(copies);
914 fs.set_handler(bvfs_result_handler, ua);
915 fs.set_offset(offset);
918 fs.get_all_file_versions(pathid, fnid, client);
924 /* .bvfs_get_jobids jobid=1
925 * -> returns needed jobids to restore
926 * .bvfs_get_jobids ujobid=xxx only
927 * -> returns the jobid of the job
928 * .bvfs_get_jobids jobid=1 jobname
929 * -> returns the jobname
930 * .bvfs_get_jobids client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
931 * -> returns all jobid for the client
932 * .bvfs_get_jobids client=xxx count
933 * -> returns the number of jobids for the client
934 * .bvfs_get_jobids jobid=1 all
935 * -> returns needed jobids to restore with all filesets a JobId=1 time
936 * .bvfs_get_jobids job=XXXXX
937 * -> returns needed jobids to restore with the jobname
938 * .bvfs_get_jobids ujobid=JobName
939 * -> returns needed jobids to restore
941 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
944 memset(&jr, 0, sizeof(JOB_DBR));
946 db_list_ctx jobids, tempids;
950 dbid_list ids; /* Store all FileSetIds for this client */
952 if (!open_new_client_db(ua)) {
956 Bvfs fs(ua->jcr, ua->db);
957 bvfs_set_acl(ua, &fs);
959 if ((pos = find_arg_with_value(ua, "username")) >= 0) {
960 fs.set_username(ua->argv[pos]);
963 if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
964 bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
967 if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
968 jr.JobId = str_to_int64(ua->argv[pos]);
970 /* Guess JobId from Job name, take the last successful jobid */
971 } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
976 bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
977 /* TODO: enhance this function to take client and/or fileset as argument*/
979 job = GetJobResWithName(jr.Name);
981 ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
987 "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
988 "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
989 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
990 "ORDER By JobTDate DESC LIMIT 1",
991 job->client->name(), job->fileset->name());
992 ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
996 ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
1001 /* Get JobId from ujobid */
1002 } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
1003 bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
1005 /* Return all backup jobid for a client */
1006 } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
1013 cli = GetClientResWithName(ua->argv[pos]);
1015 ua->error_msg(_("Unable to get Client record for Client=%s\n"),
1021 bvfs_get_filter(ua, where, limit, sizeof(limit));
1025 "FROM Job JOIN Client USING (ClientId) "
1026 "WHERE Client.Name = '%s' "
1027 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') %s "
1028 "ORDER By JobTDate ASC %s",
1029 cli->name(), where.c_str(), limit);
1030 ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
1034 ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
1038 nbjobs = fs.set_jobids(jobids.list);
1040 /* Apply the ACL filter on JobIds */
1041 if (find_arg(ua, "count") >= 0) {
1042 ua->send_msg("%d\n", nbjobs);
1045 ua->send_msg("%s\n", fs.get_jobids());
1050 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
1051 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
1052 ua->cmd, db_strerror(ua->db));
1056 /* Display only the requested jobid or
1057 * When in level base, we don't rely on any Full/Incr/Diff
1059 if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
1060 /* Apply the ACL filter on JobIds */
1061 fs.set_jobid(jr.JobId);
1062 ua->send_msg("%s\n", fs.get_jobids());
1066 /* Display only the requested job name
1068 if (find_arg(ua, "jobname") > 0) {
1069 /* Apply the ACL filter on JobIds */
1070 fs.set_jobid(jr.JobId);
1071 if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
1072 ua->send_msg("%s\n", jr.Job);
1077 /* If we have the "all" option, we do a search on all defined fileset
1080 if (find_arg(ua, "all") > 0) {
1081 edit_int64(jr.ClientId, ed1);
1082 Mmsg(query, uar_sel_filesetid, ed1);
1083 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1086 ids.DBId[0] = jr.FileSetId;
1089 jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
1091 /* Foreach different FileSet, we build a restore jobid list */
1092 for (int i=0; i < ids.num_ids; i++) {
1093 jr.FileSetId = ids.DBId[i];
1094 if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
1097 jobids.add(tempids);
1100 fs.set_jobids(jobids.list);
1101 ua->send_msg("%s\n", fs.get_jobids());
1105 static int jobs_handler(void *ctx, int num_field, char **row)
1107 UAContext *ua = (UAContext *)ctx;
1108 ua->send_msg("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
1112 static char *get_argument(UAContext *ua, const char *arg, char *esc, bool convert)
1115 if (((pos = find_arg_with_value(ua, arg)) < 0) ||
1116 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1120 db_escape_string(ua->jcr, ua->db, esc,
1121 ua->argv[pos], strlen(ua->argv[pos]));
1123 for (int i=0; esc[i] ; i++) {
1124 if (esc[i] == '*') {
1132 /* The DB should be locked */
1133 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len)
1136 char esc_name[MAX_ESCAPE_NAME_LENGTH];
1138 if (get_argument(ua, "jobname", esc_name, true) != NULL) {
1139 Mmsg(where, "AND Job.Job LIKE '%s' ", esc_name);
1142 if (get_argument(ua, "fileset", esc_name, true) != NULL) {
1143 Mmsg(tmp, "AND FileSet.FileSet LIKE '%s' ", esc_name);
1144 pm_strcat(where, tmp.c_str());
1147 if (get_argument(ua, "jobid", esc_name, false) != NULL) {
1148 Mmsg(tmp, "AND Job.JobId = '%s' ", esc_name);
1149 pm_strcat(where, tmp.c_str());
1152 if (get_argument(ua, "ujobid", esc_name, false) != NULL) {
1153 Mmsg(tmp, "AND Job.Job = '%s' ", esc_name);
1154 pm_strcat(where, tmp.c_str());
1157 if (get_argument(ua, "start", esc_name, false) != NULL) {
1158 Mmsg(tmp, "AND Job.StartTime >= '%s' ", esc_name);
1159 pm_strcat(where, tmp.c_str());
1162 if (get_argument(ua, "end", esc_name, false) != NULL) {
1163 Mmsg(tmp, "AND Job.EndTime <= '%s' ", esc_name);
1164 pm_strcat(where, tmp.c_str());
1168 if (get_argument(ua, "limit", esc_name, false) != NULL) {
1169 if (is_a_number(esc_name)) {
1170 bsnprintf(limit, len, "LIMIT %s ", esc_name);
1175 /* .bvfs_get_jobs client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
1176 * 1 yyyyy 1 Backup1_xxx_xxx_xxxx_xxx
1177 * 2 yyyyy 0 Backup1_xxx_xxx_xxxx_xxx
1179 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
1183 char esc_cli[MAX_ESCAPE_NAME_LENGTH];
1184 char limit[MAX_ESCAPE_NAME_LENGTH];
1185 if (!open_new_client_db(ua)) {
1189 if (((pos = find_arg_with_value(ua, "client")) < 0) ||
1190 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1195 /* TODO: Do checks on Jobs, FileSet, etc... */
1196 if (!acl_access_client_ok(ua, ua->argv[pos], JT_BACKUP_RESTORE)) {
1201 db_escape_string(ua->jcr, ua->db, esc_cli,
1202 ua->argv[pos], strlen(ua->argv[pos]));
1204 bvfs_get_filter(ua, where, limit, sizeof(limit));
1207 "SELECT JobId, JobTDate, HasCache, Job "
1208 "FROM Job JOIN Client USING (ClientId) JOIN FileSet USING (FileSetId) "
1209 "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
1211 "ORDER By JobTDate DESC %s",
1212 esc_cli, where.c_str(), limit);
1214 db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
1219 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
1225 static bool dot_help_cmd(UAContext *ua, const char *cmd)
1231 static bool getmsgscmd(UAContext *ua, const char *cmd)
1233 if (console_msg_pending) {
1234 do_messages(ua, cmd);
1240 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
1246 lstore.store = store;
1247 pm_strcpy(lstore.store_source, _("unknown source"));
1248 set_wstorage(jcr, &lstore);
1249 /* Try connecting for up to 15 seconds */
1250 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
1251 store->name(), store->address, store->SDport);
1252 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
1253 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1256 Dmsg0(120, _("Connected to storage daemon\n"));
1257 sd = jcr->store_bsock;
1258 sd->fsend("%s", cmd);
1259 if (sd->recv() >= 0) {
1260 ua->send_msg("%s", sd->msg);
1262 sd->signal(BNET_TERMINATE);
1263 free_bsock(ua->jcr->store_bsock);
1267 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
1271 /* Connect to File daemon */
1273 ua->jcr->client = client;
1274 /* Try to connect for 15 seconds */
1275 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1276 client->name(), client->address(), client->FDport);
1277 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1278 ua->error_msg(_("Failed to connect to Client.\n"));
1281 Dmsg0(120, "Connected to file daemon\n");
1282 fd = ua->jcr->file_bsock;
1283 fd->fsend("%s", cmd);
1284 if (fd->recv() >= 0) {
1285 ua->send_msg("%s", fd->msg);
1287 fd->signal(BNET_TERMINATE);
1288 free_bsock(ua->jcr->file_bsock);
1295 * .exit (no arg => .quit)
1297 static bool admin_cmds(UAContext *ua, const char *cmd)
1299 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1301 CLIENT *client=NULL;
1303 bool do_deadlock=false;
1304 const char *remote_cmd;
1308 if (strncmp(ua->argk[0], ".die", 4) == 0) {
1309 if (find_arg(ua, "deadlock") > 0) {
1311 remote_cmd = ".die deadlock";
1313 remote_cmd = ".die";
1315 } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1316 remote_cmd = "sm_dump";
1317 } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1318 remote_cmd = "exit";
1320 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1323 /* General debug? */
1324 for (i=1; i<ua->argc; i++) {
1325 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1326 strcasecmp(ua->argk[i], "director") == 0) {
1329 if (strcasecmp(ua->argk[i], "client") == 0 ||
1330 strcasecmp(ua->argk[i], "fd") == 0) {
1333 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1336 client = select_client_resource(ua, JT_SYSTEM);
1340 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1341 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1342 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1345 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1348 store = get_storage_resource(ua, false/*no default*/);
1353 if (!dir && !store && !client) {
1355 * We didn't find an appropriate keyword above, so
1358 start_prompt(ua, _("Available daemons are: \n"));
1359 add_prompt(ua, _("Director"));
1360 add_prompt(ua, _("Storage"));
1361 add_prompt(ua, _("Client"));
1362 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1363 case 0: /* Director */
1367 store = get_storage_resource(ua, false/*no default*/);
1370 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1378 do_storage_cmd(ua, store, remote_cmd);
1382 do_client_cmd(ua, client, remote_cmd);
1386 if (strncmp(remote_cmd, ".die", 4) == 0) {
1388 ua->send_msg(_("The Director will generate a deadlock.\n"));
1392 ua->send_msg(_("The Director will segment fault.\n"));
1393 a = jcr->JobId; /* ref NULL pointer */
1394 jcr->JobId = 1000; /* another ref NULL pointer */
1397 } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1398 sm_dump(false, true);
1399 } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1400 dot_quit_cmd(ua, cmd);
1410 * Dummy routine for non-development version
1412 static bool admin_cmds(UAContext *ua, const char *cmd)
1414 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1421 * Send a file to the director from bconsole @putfile command
1422 * The .putfile can not be used directly.
1424 static bool putfile_cmd(UAContext *ua, const char *cmd)
1426 int pos, i, pnl, fnl;
1428 POOLMEM *name = get_pool_memory(PM_FNAME);
1429 POOLMEM *path = get_pool_memory(PM_FNAME);
1430 POOLMEM *fname= get_pool_memory(PM_FNAME);
1431 const char *key = "putfile";
1434 if ((pos = find_arg_with_value(ua, "key")) > 0) {
1435 /* Check the string if the string is valid */
1436 for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1438 if (ua->argv[pos][i] == 0) {
1439 key = ua->argv[pos];
1442 ua->error_msg("Invalid key name for putfile command");
1448 /* the (intptr_t)ua will allow one file per console session */
1449 make_unique_filename(&name, (intptr_t)ua, (char *)key);
1451 fp = bfopen(name, "w");
1454 ua->error_msg("Unable to open destination file. ERR=%s\n",
1455 be.bstrerror(errno));
1460 while (ua->UA_sock->recv() > 0) {
1461 if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1463 ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1464 be.bstrerror(errno));
1466 /* TODO: Check if we need to quit here (data will still be in the
1471 split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1475 ua->send_msg("OK\n");
1478 ua->send_msg("ERROR\n");
1481 free_pool_memory(name);
1482 free_pool_memory(path);
1483 free_pool_memory(fname);
1490 /* .estimate command */
1491 static bool dotestimatecmd(UAContext *ua, const char *cmd)
1497 char *job = NULL, level = 0, *fileset = NULL, *client = NULL;
1498 memset(&jr, 0, sizeof(jr));
1500 for (int i = 1 ; i < ua->argc ; i++) {
1502 ua->error_msg(_("Invalid argument for %s\n"), ua->argk[i]);
1505 } else if (strcasecmp(ua->argk[i], "job") == 0) {
1508 } else if (strcasecmp(ua->argk[i], "level") == 0) {
1509 level = toupper(ua->argv[i][0]);
1511 } else if (strcasecmp(ua->argk[i], "fileset") == 0) {
1512 fileset = ua->argv[i];
1514 } else if (strcasecmp(ua->argk[i], "client") == 0) {
1515 client = ua->argv[i];
1519 ua->error_msg(_("Invalid argument for job\n"));
1522 if (!acl_access_ok(ua, Job_ACL, job) ||
1523 (fileset && !acl_access_ok(ua, FileSet_ACL, fileset)) ||
1524 (client && !acl_access_client_ok(ua, client, JT_BACKUP)))
1526 ua->error_msg(_("Access to specified Job, FileSet or Client not allowed.\n"));
1529 jres = (JOB *) GetResWithName(R_JOB, job);
1531 ua->error_msg(_("Invalid argument for job\n"));
1534 if (!open_client_db(ua)) {
1535 ua->error_msg(_("Unable to open the catalog.\n"));
1539 bstrncpy(jr.Name, jres->hdr.name, sizeof(jr.Name));
1540 jr.JobLevel = level ? level : jres->JobLevel;
1548 if (db_get_job_statistics(ua->jcr, ua->db, &jr)) {
1550 OutputWriter o(ua->api_opts);
1551 char *p = o.get_output(OT_START_OBJ,
1552 OT_JOBLEVEL, "level", jr.JobLevel,
1553 OT_INT, "nbjob", jr.CorrNbJob,
1554 OT_INT, "corrbytes", jr.CorrJobBytes,
1555 OT_SIZE, "jobbytes", jr.JobBytes,
1556 OT_INT, "corrfiles", jr.CorrJobFiles,
1557 OT_INT32, "jobfiles", jr.JobFiles,
1558 OT_INT, "duration", (int)0,
1559 OT_STRING, "job", jres->hdr.name,
1562 ua->send_msg("%s", p);
1564 /* We unlock the DB after the errmsg copy */
1565 pm_strcpy(ua->jcr->errmsg, ua->db->errmsg);
1567 ua->error_msg("Error with .estimate %s\n", ua->jcr->errmsg);
1574 * Can use an argument to filter on JobType
1575 * .jobs [type=B] or [type=!B]
1577 static bool jobscmd(UAContext *ua, const char *cmd)
1583 if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1584 if (ua->argv[pos][0] == '!') {
1586 type = ua->argv[pos][1];
1588 type = ua->argv[pos][0];
1592 foreach_res(job, R_JOB) {
1594 if ((exclude && type == job->JobType) || (!exclude && type != job->JobType)) {
1598 if (acl_access_ok(ua, Job_ACL, job->name())) {
1599 ua->send_msg("%s\n", job->name());
1606 static bool filesetscmd(UAContext *ua, const char *cmd)
1610 foreach_res(fs, R_FILESET) {
1611 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1612 ua->send_msg("%s\n", fs->name());
1619 static bool catalogscmd(UAContext *ua, const char *cmd)
1623 foreach_res(cat, R_CATALOG) {
1624 if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1625 ua->send_msg("%s\n", cat->name());
1632 /* This is not a good idea to lock the entire resource list to send information
1633 * on the network or query the DNS. So, we don't use the foreach_res() command
1634 * with a global lock and we do a copy of the client list in a specific list to
1635 * avoid any problem, I'm pretty sure we can use the res_head directly without
1636 * a global lock, but it needs testing to avoid race conditions.
1644 TmpClient(char *n, char *a):
1645 name(bstrdup(n)), address(bstrdup(a))
1654 static bool clientscmd(UAContext *ua, const char *cmd)
1658 const char *ip=NULL;
1660 alist *clientlist = NULL;
1663 if ((i = find_arg_with_value(ua, "address")) >= 0) {
1665 clientlist = New(alist(50, not_owned_by_alist));
1668 /* This is not a good idea to lock the entire resource list
1669 * to send information on the network or query the DNS. So,
1670 * we don't use the foreach_res() command with a global lock here.
1673 foreach_res(client, R_CLIENT) {
1674 if (acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
1676 elt = new TmpClient(client->name(), client->address());
1677 clientlist->append(elt);
1680 /* do not check for a specific ip, display everything */
1681 ua->send_msg("%s\n", client->name());
1691 foreach_alist(elt, clientlist) {
1692 /* We look for a client that matches the specific ip address */
1693 dlist *addr_list=NULL;
1698 if (strcmp(elt->address, ip) == 0) {
1701 } else if ((addr_list = bnet_host2ipaddrs(elt->address, 0, &errstr)) == NULL) {
1702 Dmsg2(10, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n",
1703 elt->address, errstr);
1706 /* Try to find the ip address from the list, we might have
1707 * other ways to compare ip addresses
1709 foreach_dlist(ipaddr, addr_list) {
1710 if (strcmp(ip, ipaddr->get_address(buf, sizeof(buf))) == 0) {
1715 free_addresses(addr_list);
1719 ua->send_msg("%s\n", elt->name);
1723 /* Cleanup the temp list */
1724 foreach_alist(elt, clientlist) {
1731 static bool msgscmd(UAContext *ua, const char *cmd)
1735 foreach_res(msgs, R_MSGS) {
1736 ua->send_msg("%s\n", msgs->name());
1742 static bool poolscmd(UAContext *ua, const char *cmd)
1746 foreach_res(pool, R_POOL) {
1747 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1748 ua->send_msg("%s\n", pool->name());
1755 static bool schedulescmd(UAContext *ua, const char *cmd)
1759 foreach_res(sched, R_SCHEDULE) {
1760 if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1761 ua->send_msg("%s\n", sched->name());
1768 static bool storagecmd(UAContext *ua, const char *cmd)
1773 alist *already_in = NULL;
1775 /* .storage unique */
1776 if (find_arg(ua, "unique") > 0) {
1778 already_in = New(alist(10, owned_by_alist));
1782 foreach_res(store, R_STORAGE) {
1783 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1788 Mmsg(tmp, "%s:%d", store->address, store->SDport);
1789 foreach_alist(elt, already_in) { /* TODO: See if we need a hash or an ordered list here */
1790 if (strcmp(tmp.c_str(), elt) == 0) {
1796 already_in->append(bstrdup(tmp.c_str()));
1800 ua->send_msg("%s\n", store->name());
1811 static bool aopcmd(UAContext *ua, const char *cmd)
1813 ua->send_msg("None\n");
1814 ua->send_msg("Truncate\n");
1818 static bool typescmd(UAContext *ua, const char *cmd)
1820 ua->send_msg("Backup\n");
1821 ua->send_msg("Restore\n");
1822 ua->send_msg("Admin\n");
1823 ua->send_msg("Verify\n");
1824 ua->send_msg("Migrate\n");
1825 ua->send_msg("Copy\n");
1829 static bool tagscmd(UAContext *ua, const char *cmd)
1832 for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1833 ua->send_msg("%s\n", p);
1839 * If this command is called, it tells the director that we
1840 * are a program that wants a sort of API, and hence,
1841 * we will probably suppress certain output, include more
1842 * error codes, and most of all send back a good number
1843 * of new signals that indicate whether or not the command
1846 static bool api_cmd(UAContext *ua, const char *cmd)
1849 if (ua->argc >= 2) {
1850 ua->api = atoi(ua->argk[1]);
1852 /* Get output configuration options such as time format or separator */
1853 if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1854 bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1865 static int client_backups_handler(void *ctx, int num_field, char **row)
1867 UAContext *ua = (UAContext *)ctx;
1868 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1869 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1874 * Return the backups for this client
1876 * .backups client=xxx fileset=yyy
1879 static bool backupscmd(UAContext *ua, const char *cmd)
1881 if (!open_client_db(ua)) {
1884 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1885 strcmp(ua->argk[2], "fileset") != 0) {
1888 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE) ||
1889 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1890 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1893 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1894 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1895 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1901 static int sql_handler(void *ctx, int num_field, char **row)
1903 UAContext *ua = (UAContext *)ctx;
1904 POOL_MEM rows(PM_MESSAGE);
1906 /* Check for nonsense */
1907 if (num_field == 0 || row == NULL || row[0] == NULL) {
1908 return 0; /* nothing returned */
1910 for (int i=0; num_field--; i++) {
1912 pm_strcpy(rows, NPRT(row[0]));
1914 pm_strcat(rows, NPRT(row[i]));
1916 pm_strcat(rows, "\t");
1918 if (!rows.c_str() || !*rows.c_str()) {
1921 ua->send_msg("%s", rows.c_str());
1926 static bool sql_cmd(UAContext *ua, const char *cmd)
1929 if (!open_new_client_db(ua)) {
1932 index = find_arg_with_value(ua, "query");
1934 ua->error_msg(_("query keyword not found.\n"));
1937 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1938 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1939 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1945 static int one_handler(void *ctx, int num_field, char **row)
1947 UAContext *ua = (UAContext *)ctx;
1948 ua->send_msg("%s\n", row[0]);
1952 static bool mediatypescmd(UAContext *ua, const char *cmd)
1954 if (!open_client_db(ua)) {
1957 if (!db_sql_query(ua->db,
1958 "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1959 one_handler, (void *)ua))
1961 ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1966 static bool mediacmd(UAContext *ua, const char *cmd)
1968 if (!open_client_db(ua)) {
1971 if (!db_sql_query(ua->db,
1972 "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1973 one_handler, (void *)ua))
1975 ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1980 static bool locationscmd(UAContext *ua, const char *cmd)
1982 if (!open_client_db(ua)) {
1985 if (!db_sql_query(ua->db,
1986 "SELECT DISTINCT Location FROM Location ORDER BY Location",
1987 one_handler, (void *)ua))
1989 ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1994 static bool levelscmd(UAContext *ua, const char *cmd)
1997 /* Note some levels are blank, which means none is needed */
1998 if (ua->argc == 1) {
1999 for (i=0; joblevels[i].level_name; i++) {
2000 if (joblevels[i].level_name[0] != ' ') {
2001 ua->send_msg("%s\n", joblevels[i].level_name);
2004 } else if (ua->argc == 2) {
2006 /* Assume that first argument is the Job Type */
2007 for (i=0; jobtypes[i].type_name; i++) {
2008 if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
2009 jobtype = jobtypes[i].job_type;
2013 for (i=0; joblevels[i].level_name; i++) {
2014 if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
2015 ua->send_msg("%s\n", joblevels[i].level_name);
2023 static bool volstatuscmd(UAContext *ua, const char *cmd)
2025 ua->send_msg("Append\n");
2026 ua->send_msg("Full\n");
2027 ua->send_msg("Used\n");
2028 ua->send_msg("Recycle\n");
2029 ua->send_msg("Purged\n");
2030 ua->send_msg("Cleaning\n");
2031 ua->send_msg("Error\n");
2036 * Return default values for a job
2038 static bool defaultscmd(UAContext *ua, const char *cmd)
2041 if (ua->argc != 2 || !ua->argv[1]) {
2045 /* Send Job defaults */
2046 if (strcmp(ua->argk[1], "job") == 0) {
2047 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
2050 JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
2053 ua->send_msg("job=%s", job->name());
2054 ua->send_msg("pool=%s", job->pool->name());
2055 ua->send_msg("messages=%s", job->messages->name());
2056 ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
2057 get_job_storage(&store, job, NULL);
2058 ua->send_msg("storage=%s", store.store->name());
2059 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
2060 ua->send_msg("level=%s", level_to_str(job->JobLevel));
2061 ua->send_msg("type=%s", job_type_to_str(job->JobType));
2062 ua->send_msg("fileset=%s", job->fileset->name());
2063 ua->send_msg("enabled=%d", job->is_enabled());
2064 ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
2065 ua->send_msg("priority=%d", job->Priority);
2068 /* Send Pool defaults */
2069 else if (strcmp(ua->argk[1], "pool") == 0) {
2070 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
2073 POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
2075 ua->send_msg("pool=%s", pool->name());
2076 ua->send_msg("pool_type=%s", pool->pool_type);
2077 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
2078 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
2079 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
2080 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
2081 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
2082 ua->send_msg("max_volumes=%d", pool->max_volumes);
2083 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
2084 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
2085 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
2086 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
2087 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
2088 ua->send_msg("auto_prune=%d", pool->AutoPrune);
2089 ua->send_msg("recycle=%d", pool->Recycle);
2090 ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
2091 ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
2094 /* Send Storage defaults */
2095 else if (strcmp(ua->argk[1], "storage") == 0) {
2096 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
2099 STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
2102 ua->send_msg("storage=%s", storage->name());
2103 ua->send_msg("address=%s", storage->address);
2104 ua->send_msg("enabled=%d", storage->is_enabled());
2105 ua->send_msg("media_type=%s", storage->media_type);
2106 ua->send_msg("sdport=%d", storage->SDport);
2107 device = (DEVICE *)storage->device->first();
2108 ua->send_msg("device=%s", device->name());
2109 if (storage->device && storage->device->size() > 1) {
2110 while ((device = (DEVICE *)storage->device->next())) {
2111 ua->send_msg(",%s", device->name());
2116 /* Send Client defaults */
2117 else if (strcmp(ua->argk[1], "client") == 0) {
2118 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE)) {
2121 CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
2123 ua->send_msg("client=%s", client->name());
2124 ua->send_msg("address=%s", client->address());
2125 ua->send_msg("fdport=%d", client->FDport);
2126 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
2127 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
2128 ua->send_msg("autoprune=%d", client->AutoPrune);
2129 ua->send_msg("catalog=%s", client->catalog->name());