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;
568 fileid = dirid = hardlink = empty;
570 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
571 &limit, &offset) || !path)
573 ua->error_msg("Can't find jobid, pathid or path argument\n");
574 return true; /* not enough param */
577 if (!open_new_client_db(ua)) {
581 Bvfs fs(ua->jcr, ua->db);
582 bvfs_set_acl(ua, &fs);
583 fs.set_username(username);
584 fs.set_jobids(jobid);
586 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
587 fileid = ua->argv[i];
589 if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
592 if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
593 hardlink = ua->argv[i];
595 if ((i = find_arg(ua, "nodelta")) >= 0) {
596 fs.set_compute_delta(false);
598 if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
599 ua->send_msg("OK\n");
601 ua->error_msg("Can't create restore list\n");
607 /* Get a bootstrap for a given bvfs restore session
608 * .bvfs_get_bootstrap path=b21xxxxxx
615 static bool dot_bvfs_get_bootstrap(UAContext *ua, const char *cmd)
617 RESTORE_CTX rx; /* restore context */
618 POOLMEM *buf = get_pool_memory(PM_MESSAGE);
622 if (!open_new_client_db(ua)) {
623 ua->error_msg("ERROR: Unable to open database\n");
626 pos = find_arg_with_value(ua, "path");
628 ua->error_msg("ERROR: Unable to get path argument\n");
632 insert_table_into_findex_list(ua, &rx, ua->argv[pos]);
634 if (rx.bsr_list->size() > 0) {
635 if (!complete_bsr(ua, rx.bsr_list)) { /* find Vol, SessId, SessTime from JobIds */
636 ua->error_msg("ERROR: Unable to construct a valid BSR. Cannot continue.\n");
639 if (!(rx.selected_files = write_bsr_file(ua, rx))) {
640 ua->error_msg("ERROR: No files selected to be restored.\n");
643 FILE *fp = bfopen(ua->jcr->RestoreBootstrap, "r");
645 ua->error_msg("ERROR: Unable to open bootstrap file\n");
648 while (bfgets(buf, fp)) {
649 ua->send_msg("%s", buf);
653 ua->error_msg("ERROR: Unable to find files to restore\n");
658 if (ua->jcr->unlink_bsr) {
659 unlink(ua->jcr->RestoreBootstrap);
660 ua->jcr->unlink_bsr = false;
662 free_pool_memory(buf);
668 * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
673 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
677 char *path=NULL, *jobid=NULL, *username=NULL;
679 int limit=2000, offset=0;
682 bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
684 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
685 if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
686 ua->error_msg("Can't find jobid, pathid or path argument\n");
690 filename = ua->argv[i];
692 } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
693 if (!is_a_number(ua->argv[i])) {
694 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
697 fileid = str_to_int64(ua->argv[i]);
700 if (!open_new_client_db(ua)) {
704 Bvfs fs(ua->jcr, ua->db);
705 bvfs_set_acl(ua, &fs);
706 fs.set_username(username);
707 fs.set_handler(bvfs_result_handler, ua);
715 fs.get_volumes(fileid);
722 * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
723 * .bvfs_lsfiles jobid=1,2,3,4 path=/
725 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
728 int limit=2000, offset=0;
729 char *path=NULL, *jobid=NULL, *username=NULL;
730 char *pattern=NULL, *filename=NULL;
734 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
737 ua->error_msg("Can't find jobid, pathid or path argument\n");
738 return true; /* not enough param */
740 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
741 pattern = ua->argv[i];
743 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
744 filename = ua->argv[i];
747 if (!open_new_client_db(ua)) {
751 Bvfs fs(ua->jcr, ua->db);
752 bvfs_set_acl(ua, &fs);
753 fs.set_username(username);
754 fs.set_jobids(jobid);
755 fs.set_handler(bvfs_result_handler, ua);
757 fs.set_offset(offset);
760 fs.set_pattern(pattern);
763 fs.set_filename(filename);
766 ok = fs.ch_dir(pathid);
768 ok = fs.ch_dir(path);
782 * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
783 * .bvfs_lsdirs jobid=1,2,3,4 path=/
784 * .bvfs_lsdirs jobid=1,2,3,4 path=
786 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
789 int limit=2000, offset=0;
790 char *path=NULL, *jobid=NULL, *username=NULL;
796 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
799 ua->error_msg("Can't find jobid, pathid or path argument\n");
800 return true; /* not enough param */
803 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
804 pattern = ua->argv[i];
807 dironly = find_arg(ua, "dironly");
809 if (!open_new_client_db(ua)) {
813 Bvfs fs(ua->jcr, ua->db);
814 bvfs_set_acl(ua, &fs);
815 fs.set_username(username);
816 fs.set_jobids(jobid);
818 fs.set_handler(bvfs_result_handler, ua);
819 fs.set_offset(offset);
823 fs.set_pattern(pattern);
827 ok = fs.ch_dir(pathid);
829 ok = fs.ch_dir(path);
836 fs.ls_special_dirs();
847 * .bvfs_get_delta fileid=10
850 static bool dot_bvfs_get_delta(UAContext *ua, const char *cmd)
856 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
857 if (!is_a_number(ua->argv[i])) {
858 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
861 fileid = str_to_int64(ua->argv[i]);
864 ua->error_msg("Expecting FileId\n");
868 if (!open_new_client_db(ua)) {
871 Bvfs fs(ua->jcr, ua->db);
872 bvfs_set_acl(ua, &fs);
873 fs.set_handler(bvfs_result_handler, ua);
875 ret = fs.get_delta(fileid);
881 * .bvfs_versions fnid=10 pathid=10 client=xxx copies versions
884 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
888 int limit=2000, offset=0;
889 char *path=NULL, *client=NULL, *username=NULL;
890 bool copies=false, versions=false;
891 if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
894 ua->error_msg("Can't find pathid or path argument\n");
895 return true; /* not enough param */
898 if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
900 ua->error_msg("Can't find client or fnid argument\n");
901 return true; /* not enough param */
904 if (!open_new_client_db(ua)) {
908 Bvfs fs(ua->jcr, ua->db);
909 bvfs_set_acl(ua, &fs);
911 fs.set_see_all_versions(versions);
912 fs.set_see_copies(copies);
913 fs.set_handler(bvfs_result_handler, ua);
914 fs.set_offset(offset);
917 fs.get_all_file_versions(pathid, fnid, client);
923 /* .bvfs_get_jobids jobid=1
924 * -> returns needed jobids to restore
925 * .bvfs_get_jobids ujobid=xxx only
926 * -> returns the jobid of the job
927 * .bvfs_get_jobids jobid=1 jobname
928 * -> returns the jobname
929 * .bvfs_get_jobids client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
930 * -> returns all jobid for the client
931 * .bvfs_get_jobids client=xxx count
932 * -> returns the number of jobids for the client
933 * .bvfs_get_jobids jobid=1 all
934 * -> returns needed jobids to restore with all filesets a JobId=1 time
935 * .bvfs_get_jobids job=XXXXX
936 * -> returns needed jobids to restore with the jobname
937 * .bvfs_get_jobids ujobid=JobName
938 * -> returns needed jobids to restore
940 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
943 memset(&jr, 0, sizeof(JOB_DBR));
945 db_list_ctx jobids, tempids;
949 dbid_list ids; /* Store all FileSetIds for this client */
951 if (!open_new_client_db(ua)) {
955 Bvfs fs(ua->jcr, ua->db);
956 bvfs_set_acl(ua, &fs);
958 if ((pos = find_arg_with_value(ua, "username")) >= 0) {
959 fs.set_username(ua->argv[pos]);
962 if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
963 bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
966 if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
967 jr.JobId = str_to_int64(ua->argv[pos]);
969 /* Guess JobId from Job name, take the last successful jobid */
970 } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
975 bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
976 /* TODO: enhance this function to take client and/or fileset as argument*/
978 job = GetJobResWithName(jr.Name);
980 ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
986 "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
987 "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
988 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
989 "ORDER By JobTDate DESC LIMIT 1",
990 job->client->name(), job->fileset->name());
991 ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
995 ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
1000 /* Get JobId from ujobid */
1001 } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
1002 bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
1004 /* Return all backup jobid for a client */
1005 } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
1012 cli = GetClientResWithName(ua->argv[pos]);
1014 ua->error_msg(_("Unable to get Client record for Client=%s\n"),
1020 bvfs_get_filter(ua, where, limit, sizeof(limit));
1024 "FROM Job JOIN Client USING (ClientId) "
1025 "WHERE Client.Name = '%s' "
1026 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') %s "
1027 "ORDER By JobTDate ASC %s",
1028 cli->name(), where.c_str(), limit);
1029 ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
1033 ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
1037 nbjobs = fs.set_jobids(jobids.list);
1039 /* Apply the ACL filter on JobIds */
1040 if (find_arg(ua, "count") >= 0) {
1041 ua->send_msg("%d\n", nbjobs);
1044 ua->send_msg("%s\n", fs.get_jobids());
1049 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
1050 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
1051 ua->cmd, db_strerror(ua->db));
1055 /* Display only the requested jobid or
1056 * When in level base, we don't rely on any Full/Incr/Diff
1058 if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
1059 /* Apply the ACL filter on JobIds */
1060 fs.set_jobid(jr.JobId);
1061 ua->send_msg("%s\n", fs.get_jobids());
1065 /* Display only the requested job name
1067 if (find_arg(ua, "jobname") > 0) {
1068 /* Apply the ACL filter on JobIds */
1069 fs.set_jobid(jr.JobId);
1070 if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
1071 ua->send_msg("%s\n", jr.Job);
1076 /* If we have the "all" option, we do a search on all defined fileset
1079 if (find_arg(ua, "all") > 0) {
1080 edit_int64(jr.ClientId, ed1);
1081 Mmsg(query, uar_sel_filesetid, ed1);
1082 db_get_query_dbids(ua->jcr, ua->db, query, ids);
1085 ids.DBId[0] = jr.FileSetId;
1088 jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
1090 /* Foreach different FileSet, we build a restore jobid list */
1091 for (int i=0; i < ids.num_ids; i++) {
1092 jr.FileSetId = ids.DBId[i];
1093 if (!db_get_accurate_jobids(ua->jcr, ua->db, &jr, &tempids)) {
1096 jobids.add(tempids);
1099 fs.set_jobids(jobids.list);
1100 ua->send_msg("%s\n", fs.get_jobids());
1104 static int jobs_handler(void *ctx, int num_field, char **row)
1106 UAContext *ua = (UAContext *)ctx;
1107 ua->send_msg("%s %s %s %s\n", row[0], row[1], row[2], row[3]);
1111 static char *get_argument(UAContext *ua, const char *arg, char *esc, bool convert)
1114 if (((pos = find_arg_with_value(ua, arg)) < 0) ||
1115 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1119 db_escape_string(ua->jcr, ua->db, esc,
1120 ua->argv[pos], strlen(ua->argv[pos]));
1122 for (int i=0; esc[i] ; i++) {
1123 if (esc[i] == '*') {
1131 /* The DB should be locked */
1132 static void bvfs_get_filter(UAContext *ua, POOL_MEM &where, char *limit, int len)
1135 char esc_name[MAX_ESCAPE_NAME_LENGTH];
1137 if (get_argument(ua, "jobname", esc_name, true) != NULL) {
1138 Mmsg(where, "AND Job.Job LIKE '%s' ", esc_name);
1141 if (get_argument(ua, "fileset", esc_name, true) != NULL) {
1142 Mmsg(tmp, "AND FileSet.FileSet LIKE '%s' ", esc_name);
1143 pm_strcat(where, tmp.c_str());
1146 if (get_argument(ua, "jobid", esc_name, false) != NULL) {
1147 Mmsg(tmp, "AND Job.JobId = '%s' ", esc_name);
1148 pm_strcat(where, tmp.c_str());
1151 if (get_argument(ua, "ujobid", esc_name, false) != NULL) {
1152 Mmsg(tmp, "AND Job.Job = '%s' ", esc_name);
1153 pm_strcat(where, tmp.c_str());
1156 if (get_argument(ua, "start", esc_name, false) != NULL) {
1157 Mmsg(tmp, "AND Job.StartTime >= '%s' ", esc_name);
1158 pm_strcat(where, tmp.c_str());
1161 if (get_argument(ua, "end", esc_name, false) != NULL) {
1162 Mmsg(tmp, "AND Job.EndTime <= '%s' ", esc_name);
1163 pm_strcat(where, tmp.c_str());
1167 if (get_argument(ua, "limit", esc_name, false) != NULL) {
1168 if (is_a_number(esc_name)) {
1169 bsnprintf(limit, len, "LIMIT %s ", esc_name);
1174 /* .bvfs_get_jobs client=xxx [ujobid=yyyy] [jobname=<glob>] [fileset=<glob>] [start=<ts>] [end=<ts>]
1175 * 1 yyyyy 1 Backup1_xxx_xxx_xxxx_xxx
1176 * 2 yyyyy 0 Backup1_xxx_xxx_xxxx_xxx
1178 static bool dot_bvfs_get_jobs(UAContext *ua, const char *cmd)
1182 char esc_cli[MAX_ESCAPE_NAME_LENGTH];
1183 char limit[MAX_ESCAPE_NAME_LENGTH];
1184 if (!open_new_client_db(ua)) {
1188 if (((pos = find_arg_with_value(ua, "client")) < 0) ||
1189 (strlen(ua->argv[pos]) > MAX_NAME_LENGTH))
1194 /* TODO: Do checks on Jobs, FileSet, etc... */
1195 if (!acl_access_client_ok(ua, ua->argv[pos], JT_BACKUP_RESTORE)) {
1200 db_escape_string(ua->jcr, ua->db, esc_cli,
1201 ua->argv[pos], strlen(ua->argv[pos]));
1203 bvfs_get_filter(ua, where, limit, sizeof(limit));
1206 "SELECT JobId, JobTDate, HasCache, Job "
1207 "FROM Job JOIN Client USING (ClientId) JOIN FileSet USING (FileSetId) "
1208 "WHERE Client.Name = '%s' AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
1210 "ORDER By JobTDate DESC %s",
1211 esc_cli, where.c_str(), limit);
1213 db_sql_query(ua->db, ua->db->cmd, jobs_handler, ua);
1218 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
1224 static bool dot_help_cmd(UAContext *ua, const char *cmd)
1230 static bool getmsgscmd(UAContext *ua, const char *cmd)
1232 if (console_msg_pending) {
1233 do_messages(ua, cmd);
1239 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
1245 lstore.store = store;
1246 pm_strcpy(lstore.store_source, _("unknown source"));
1247 set_wstorage(jcr, &lstore);
1248 /* Try connecting for up to 15 seconds */
1249 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
1250 store->name(), store->address, store->SDport);
1251 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
1252 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
1255 Dmsg0(120, _("Connected to storage daemon\n"));
1256 sd = jcr->store_bsock;
1257 sd->fsend("%s", cmd);
1258 if (sd->recv() >= 0) {
1259 ua->send_msg("%s", sd->msg);
1261 sd->signal(BNET_TERMINATE);
1262 free_bsock(ua->jcr->store_bsock);
1266 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
1270 /* Connect to File daemon */
1272 ua->jcr->client = client;
1273 /* Try to connect for 15 seconds */
1274 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
1275 client->name(), client->address(), client->FDport);
1276 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
1277 ua->error_msg(_("Failed to connect to Client.\n"));
1280 Dmsg0(120, "Connected to file daemon\n");
1281 fd = ua->jcr->file_bsock;
1282 fd->fsend("%s", cmd);
1283 if (fd->recv() >= 0) {
1284 ua->send_msg("%s", fd->msg);
1286 fd->signal(BNET_TERMINATE);
1287 free_bsock(ua->jcr->file_bsock);
1294 * .exit (no arg => .quit)
1296 static bool admin_cmds(UAContext *ua, const char *cmd)
1298 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1300 CLIENT *client=NULL;
1302 bool do_deadlock=false;
1303 const char *remote_cmd;
1307 if (strncmp(ua->argk[0], ".die", 4) == 0) {
1308 if (find_arg(ua, "deadlock") > 0) {
1310 remote_cmd = ".die deadlock";
1312 remote_cmd = ".die";
1314 } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
1315 remote_cmd = "sm_dump";
1316 } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
1317 remote_cmd = "exit";
1319 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1322 /* General debug? */
1323 for (i=1; i<ua->argc; i++) {
1324 if (strcasecmp(ua->argk[i], "dir") == 0 ||
1325 strcasecmp(ua->argk[i], "director") == 0) {
1328 if (strcasecmp(ua->argk[i], "client") == 0 ||
1329 strcasecmp(ua->argk[i], "fd") == 0) {
1332 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
1335 client = select_client_resource(ua, JT_SYSTEM);
1339 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
1340 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
1341 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
1344 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
1347 store = get_storage_resource(ua, false/*no default*/);
1352 if (!dir && !store && !client) {
1354 * We didn't find an appropriate keyword above, so
1357 start_prompt(ua, _("Available daemons are: \n"));
1358 add_prompt(ua, _("Director"));
1359 add_prompt(ua, _("Storage"));
1360 add_prompt(ua, _("Client"));
1361 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
1362 case 0: /* Director */
1366 store = get_storage_resource(ua, false/*no default*/);
1369 client = select_client_resource(ua, JT_BACKUP_RESTORE);
1377 do_storage_cmd(ua, store, remote_cmd);
1381 do_client_cmd(ua, client, remote_cmd);
1385 if (strncmp(remote_cmd, ".die", 4) == 0) {
1387 ua->send_msg(_("The Director will generate a deadlock.\n"));
1391 ua->send_msg(_("The Director will segment fault.\n"));
1392 a = jcr->JobId; /* ref NULL pointer */
1393 jcr->JobId = 1000; /* another ref NULL pointer */
1396 } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
1397 sm_dump(false, true);
1398 } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
1399 dot_quit_cmd(ua, cmd);
1409 * Dummy routine for non-development version
1411 static bool admin_cmds(UAContext *ua, const char *cmd)
1413 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1420 * Send a file to the director from bconsole @putfile command
1421 * The .putfile can not be used directly.
1423 static bool putfile_cmd(UAContext *ua, const char *cmd)
1425 int pos, i, pnl, fnl;
1427 POOLMEM *name = get_pool_memory(PM_FNAME);
1428 POOLMEM *path = get_pool_memory(PM_FNAME);
1429 POOLMEM *fname= get_pool_memory(PM_FNAME);
1430 const char *key = "putfile";
1433 if ((pos = find_arg_with_value(ua, "key")) > 0) {
1434 /* Check the string if the string is valid */
1435 for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1437 if (ua->argv[pos][i] == 0) {
1438 key = ua->argv[pos];
1441 ua->error_msg("Invalid key name for putfile command");
1447 /* the (intptr_t)ua will allow one file per console session */
1448 make_unique_filename(&name, (intptr_t)ua, (char *)key);
1450 fp = bfopen(name, "w");
1453 ua->error_msg("Unable to open destination file. ERR=%s\n",
1454 be.bstrerror(errno));
1459 while (ua->UA_sock->recv() > 0) {
1460 if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1462 ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1463 be.bstrerror(errno));
1465 /* TODO: Check if we need to quit here (data will still be in the
1470 split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1474 ua->send_msg("OK\n");
1477 ua->send_msg("ERROR\n");
1480 free_pool_memory(name);
1481 free_pool_memory(path);
1482 free_pool_memory(fname);
1489 /* .estimate command */
1490 static bool dotestimatecmd(UAContext *ua, const char *cmd)
1496 char *job = NULL, level = 0, *fileset = NULL, *client = NULL;
1497 memset(&jr, 0, sizeof(jr));
1499 for (int i = 1 ; i < ua->argc ; i++) {
1501 ua->error_msg(_("Invalid argument for %s\n"), ua->argk[i]);
1504 } else if (strcasecmp(ua->argk[i], "job") == 0) {
1507 } else if (strcasecmp(ua->argk[i], "level") == 0) {
1508 level = toupper(ua->argv[i][0]);
1510 } else if (strcasecmp(ua->argk[i], "fileset") == 0) {
1511 fileset = ua->argv[i];
1513 } else if (strcasecmp(ua->argk[i], "client") == 0) {
1514 client = ua->argv[i];
1518 ua->error_msg(_("Invalid argument for job\n"));
1521 if (!acl_access_ok(ua, Job_ACL, job) ||
1522 (fileset && !acl_access_ok(ua, FileSet_ACL, fileset)) ||
1523 (client && !acl_access_client_ok(ua, client, JT_BACKUP)))
1525 ua->error_msg(_("Access to specified Job, FileSet or Client not allowed.\n"));
1528 jres = (JOB *) GetResWithName(R_JOB, job);
1530 ua->error_msg(_("Invalid argument for job\n"));
1533 if (!open_client_db(ua)) {
1534 ua->error_msg(_("Unable to open the catalog.\n"));
1538 bstrncpy(jr.Name, jres->hdr.name, sizeof(jr.Name));
1539 jr.JobLevel = level ? level : jres->JobLevel;
1547 if (db_get_job_statistics(ua->jcr, ua->db, &jr)) {
1549 OutputWriter o(ua->api_opts);
1550 char *p = o.get_output(OT_START_OBJ,
1551 OT_JOBLEVEL, "level", jr.JobLevel,
1552 OT_INT, "nbjob", jr.CorrNbJob,
1553 OT_INT, "corrbytes", jr.CorrJobBytes,
1554 OT_SIZE, "jobbytes", jr.JobBytes,
1555 OT_INT, "corrfiles", jr.CorrJobFiles,
1556 OT_INT32, "jobfiles", jr.JobFiles,
1557 OT_INT, "duration", (int)0,
1558 OT_STRING, "job", jres->hdr.name,
1561 ua->send_msg("%s", p);
1563 /* We unlock the DB after the errmsg copy */
1564 pm_strcpy(ua->jcr->errmsg, ua->db->errmsg);
1566 ua->error_msg("Error with .estimate %s\n", ua->jcr->errmsg);
1573 * Can use an argument to filter on JobType
1574 * .jobs [type=B] or [type=!B]
1576 static bool jobscmd(UAContext *ua, const char *cmd)
1582 if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1583 if (ua->argv[pos][0] == '!') {
1585 type = ua->argv[pos][1];
1587 type = ua->argv[pos][0];
1591 foreach_res(job, R_JOB) {
1593 if ((exclude && type == job->JobType) || (!exclude && type != job->JobType)) {
1597 if (acl_access_ok(ua, Job_ACL, job->name())) {
1598 ua->send_msg("%s\n", job->name());
1605 static bool filesetscmd(UAContext *ua, const char *cmd)
1609 foreach_res(fs, R_FILESET) {
1610 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1611 ua->send_msg("%s\n", fs->name());
1618 static bool catalogscmd(UAContext *ua, const char *cmd)
1622 foreach_res(cat, R_CATALOG) {
1623 if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1624 ua->send_msg("%s\n", cat->name());
1631 /* This is not a good idea to lock the entire resource list to send information
1632 * on the network or query the DNS. So, we don't use the foreach_res() command
1633 * with a global lock and we do a copy of the client list in a specific list to
1634 * avoid any problem, I'm pretty sure we can use the res_head directly without
1635 * a global lock, but it needs testing to avoid race conditions.
1643 TmpClient(char *n, char *a):
1644 name(bstrdup(n)), address(bstrdup(a))
1653 static bool clientscmd(UAContext *ua, const char *cmd)
1657 const char *ip=NULL;
1659 alist *clientlist = NULL;
1662 if ((i = find_arg_with_value(ua, "address")) >= 0) {
1664 clientlist = New(alist(50, not_owned_by_alist));
1667 /* This is not a good idea to lock the entire resource list
1668 * to send information on the network or query the DNS. So,
1669 * we don't use the foreach_res() command with a global lock here.
1672 foreach_res(client, R_CLIENT) {
1673 if (acl_access_client_ok(ua, client->name(), JT_BACKUP_RESTORE)) {
1675 elt = new TmpClient(client->name(), client->address());
1676 clientlist->append(elt);
1679 /* do not check for a specific ip, display everything */
1680 ua->send_msg("%s\n", client->name());
1690 foreach_alist(elt, clientlist) {
1691 /* We look for a client that matches the specific ip address */
1692 dlist *addr_list=NULL;
1697 if (strcmp(elt->address, ip) == 0) {
1700 } else if ((addr_list = bnet_host2ipaddrs(elt->address, 0, &errstr)) == NULL) {
1701 Dmsg2(10, "bnet_host2ipaddrs() for host %s failed: ERR=%s\n",
1702 elt->address, errstr);
1705 /* Try to find the ip address from the list, we might have
1706 * other ways to compare ip addresses
1708 foreach_dlist(ipaddr, addr_list) {
1709 if (strcmp(ip, ipaddr->get_address(buf, sizeof(buf))) == 0) {
1714 free_addresses(addr_list);
1718 ua->send_msg("%s\n", elt->name);
1722 /* Cleanup the temp list */
1723 foreach_alist(elt, clientlist) {
1730 static bool msgscmd(UAContext *ua, const char *cmd)
1734 foreach_res(msgs, R_MSGS) {
1735 ua->send_msg("%s\n", msgs->name());
1741 static bool poolscmd(UAContext *ua, const char *cmd)
1745 foreach_res(pool, R_POOL) {
1746 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1747 ua->send_msg("%s\n", pool->name());
1754 static bool schedulescmd(UAContext *ua, const char *cmd)
1758 foreach_res(sched, R_SCHEDULE) {
1759 if (acl_access_ok(ua, Schedule_ACL, sched->name())) {
1760 ua->send_msg("%s\n", sched->name());
1767 static bool storagecmd(UAContext *ua, const char *cmd)
1772 alist *already_in = NULL;
1774 /* .storage unique */
1775 if (find_arg(ua, "unique") > 0) {
1777 already_in = New(alist(10, owned_by_alist));
1781 foreach_res(store, R_STORAGE) {
1782 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1787 Mmsg(tmp, "%s:%d", store->address, store->SDport);
1788 foreach_alist(elt, already_in) { /* TODO: See if we need a hash or an ordered list here */
1789 if (strcmp(tmp.c_str(), elt) == 0) {
1795 already_in->append(bstrdup(tmp.c_str()));
1799 ua->send_msg("%s\n", store->name());
1810 static bool aopcmd(UAContext *ua, const char *cmd)
1812 ua->send_msg("None\n");
1813 ua->send_msg("Truncate\n");
1817 static bool typescmd(UAContext *ua, const char *cmd)
1819 ua->send_msg("Backup\n");
1820 ua->send_msg("Restore\n");
1821 ua->send_msg("Admin\n");
1822 ua->send_msg("Verify\n");
1823 ua->send_msg("Migrate\n");
1824 ua->send_msg("Copy\n");
1828 static bool tagscmd(UAContext *ua, const char *cmd)
1831 for (const char *p = debug_get_tag(i++, NULL) ; p ; p = debug_get_tag(i++, NULL)) {
1832 ua->send_msg("%s\n", p);
1838 * If this command is called, it tells the director that we
1839 * are a program that wants a sort of API, and hence,
1840 * we will probably suppress certain output, include more
1841 * error codes, and most of all send back a good number
1842 * of new signals that indicate whether or not the command
1845 static bool api_cmd(UAContext *ua, const char *cmd)
1848 if (ua->argc >= 2) {
1849 ua->api = atoi(ua->argk[1]);
1851 /* Get output configuration options such as time format or separator */
1852 if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1853 bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1864 static int client_backups_handler(void *ctx, int num_field, char **row)
1866 UAContext *ua = (UAContext *)ctx;
1867 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1868 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1873 * Return the backups for this client
1875 * .backups client=xxx fileset=yyy
1878 static bool backupscmd(UAContext *ua, const char *cmd)
1880 if (!open_client_db(ua)) {
1883 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1884 strcmp(ua->argk[2], "fileset") != 0) {
1887 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE) ||
1888 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1889 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1892 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1893 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1894 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1900 static int sql_handler(void *ctx, int num_field, char **row)
1902 UAContext *ua = (UAContext *)ctx;
1903 POOL_MEM rows(PM_MESSAGE);
1905 /* Check for nonsense */
1906 if (num_field == 0 || row == NULL || row[0] == NULL) {
1907 return 0; /* nothing returned */
1909 for (int i=0; num_field--; i++) {
1911 pm_strcpy(rows, NPRT(row[0]));
1913 pm_strcat(rows, NPRT(row[i]));
1915 pm_strcat(rows, "\t");
1917 if (!rows.c_str() || !*rows.c_str()) {
1920 ua->send_msg("%s", rows.c_str());
1925 static bool sql_cmd(UAContext *ua, const char *cmd)
1928 if (!open_new_client_db(ua)) {
1931 index = find_arg_with_value(ua, "query");
1933 ua->error_msg(_("query keyword not found.\n"));
1936 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1937 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1938 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1944 static int one_handler(void *ctx, int num_field, char **row)
1946 UAContext *ua = (UAContext *)ctx;
1947 ua->send_msg("%s\n", row[0]);
1951 static bool mediatypescmd(UAContext *ua, const char *cmd)
1953 if (!open_client_db(ua)) {
1956 if (!db_sql_query(ua->db,
1957 "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1958 one_handler, (void *)ua))
1960 ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1965 static bool mediacmd(UAContext *ua, const char *cmd)
1967 if (!open_client_db(ua)) {
1970 if (!db_sql_query(ua->db,
1971 "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1972 one_handler, (void *)ua))
1974 ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1979 static bool locationscmd(UAContext *ua, const char *cmd)
1981 if (!open_client_db(ua)) {
1984 if (!db_sql_query(ua->db,
1985 "SELECT DISTINCT Location FROM Location ORDER BY Location",
1986 one_handler, (void *)ua))
1988 ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1993 static bool levelscmd(UAContext *ua, const char *cmd)
1996 /* Note some levels are blank, which means none is needed */
1997 if (ua->argc == 1) {
1998 for (i=0; joblevels[i].level_name; i++) {
1999 if (joblevels[i].level_name[0] != ' ') {
2000 ua->send_msg("%s\n", joblevels[i].level_name);
2003 } else if (ua->argc == 2) {
2005 /* Assume that first argument is the Job Type */
2006 for (i=0; jobtypes[i].type_name; i++) {
2007 if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
2008 jobtype = jobtypes[i].job_type;
2012 for (i=0; joblevels[i].level_name; i++) {
2013 if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
2014 ua->send_msg("%s\n", joblevels[i].level_name);
2022 static bool volstatuscmd(UAContext *ua, const char *cmd)
2024 ua->send_msg("Append\n");
2025 ua->send_msg("Full\n");
2026 ua->send_msg("Used\n");
2027 ua->send_msg("Recycle\n");
2028 ua->send_msg("Purged\n");
2029 ua->send_msg("Cleaning\n");
2030 ua->send_msg("Error\n");
2035 * Return default values for a job
2037 static bool defaultscmd(UAContext *ua, const char *cmd)
2040 if (ua->argc != 2 || !ua->argv[1]) {
2044 /* Send Job defaults */
2045 if (strcmp(ua->argk[1], "job") == 0) {
2046 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
2049 JOB *job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
2052 ua->send_msg("job=%s", job->name());
2053 ua->send_msg("pool=%s", job->pool->name());
2054 ua->send_msg("messages=%s", job->messages->name());
2055 ua->send_msg("client=%s", job->client?job->client->name():_("*None*"));
2056 get_job_storage(&store, job, NULL);
2057 ua->send_msg("storage=%s", store.store->name());
2058 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
2059 ua->send_msg("level=%s", level_to_str(job->JobLevel));
2060 ua->send_msg("type=%s", job_type_to_str(job->JobType));
2061 ua->send_msg("fileset=%s", job->fileset->name());
2062 ua->send_msg("enabled=%d", job->is_enabled());
2063 ua->send_msg("catalog=%s", job->client?job->client->catalog->name():_("*None*"));
2064 ua->send_msg("priority=%d", job->Priority);
2067 /* Send Pool defaults */
2068 else if (strcmp(ua->argk[1], "pool") == 0) {
2069 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
2072 POOL *pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
2074 ua->send_msg("pool=%s", pool->name());
2075 ua->send_msg("pool_type=%s", pool->pool_type);
2076 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
2077 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
2078 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
2079 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
2080 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
2081 ua->send_msg("max_volumes=%d", pool->max_volumes);
2082 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
2083 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
2084 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
2085 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
2086 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
2087 ua->send_msg("auto_prune=%d", pool->AutoPrune);
2088 ua->send_msg("recycle=%d", pool->Recycle);
2089 ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
2090 ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));
2093 /* Send Storage defaults */
2094 else if (strcmp(ua->argk[1], "storage") == 0) {
2095 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
2098 STORE *storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
2101 ua->send_msg("storage=%s", storage->name());
2102 ua->send_msg("address=%s", storage->address);
2103 ua->send_msg("enabled=%d", storage->is_enabled());
2104 ua->send_msg("media_type=%s", storage->media_type);
2105 ua->send_msg("sdport=%d", storage->SDport);
2106 device = (DEVICE *)storage->device->first();
2107 ua->send_msg("device=%s", device->name());
2108 if (storage->device && storage->device->size() > 1) {
2109 while ((device = (DEVICE *)storage->device->next())) {
2110 ua->send_msg(",%s", device->name());
2115 /* Send Client defaults */
2116 else if (strcmp(ua->argk[1], "client") == 0) {
2117 if (!acl_access_client_ok(ua, ua->argv[1], JT_BACKUP_RESTORE)) {
2120 CLIENT *client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
2122 ua->send_msg("client=%s", client->name());
2123 ua->send_msg("address=%s", client->address());
2124 ua->send_msg("fdport=%d", client->FDport);
2125 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
2126 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
2127 ua->send_msg("autoprune=%d", client->AutoPrune);
2128 ua->send_msg("catalog=%s", client->catalog->name());