2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from many
7 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 Bacula® is a registered trademark of Kern Sibbald.
18 * Bacula Director -- User Agent Commands
19 * These are "dot" commands, i.e. commands preceded
20 * by a period. These commands are meant to be used
21 * by a program, so there is no prompting, and the
22 * returned results are (supposed to be) predictable.
24 * Kern Sibbald, April MMII
30 #include "cats/bvfs.h"
31 #include "findlib/find.h"
33 /* Imported variables */
34 extern struct s_jl joblevels[];
35 extern struct s_jt jobtypes[];
37 /* Imported functions */
38 extern void do_messages(UAContext *ua, const char *cmd);
39 extern int quit_cmd(UAContext *ua, const char *cmd);
40 extern int qhelp_cmd(UAContext *ua, const char *cmd);
41 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
44 /* Forward referenced functions */
45 static bool admin_cmds(UAContext *ua, const char *cmd);
46 static bool jobscmd(UAContext *ua, const char *cmd);
47 static bool filesetscmd(UAContext *ua, const char *cmd);
48 static bool clientscmd(UAContext *ua, const char *cmd);
49 static bool msgscmd(UAContext *ua, const char *cmd);
50 static bool poolscmd(UAContext *ua, const char *cmd);
51 static bool storagecmd(UAContext *ua, const char *cmd);
52 static bool defaultscmd(UAContext *ua, const char *cmd);
53 static bool typescmd(UAContext *ua, const char *cmd);
54 static bool backupscmd(UAContext *ua, const char *cmd);
55 static bool levelscmd(UAContext *ua, const char *cmd);
56 static bool getmsgscmd(UAContext *ua, const char *cmd);
57 static bool volstatuscmd(UAContext *ua, const char *cmd);
58 static bool mediatypescmd(UAContext *ua, const char *cmd);
59 static bool locationscmd(UAContext *ua, const char *cmd);
60 static bool mediacmd(UAContext *ua, const char *cmd);
61 static bool aopcmd(UAContext *ua, const char *cmd);
62 static bool catalogscmd(UAContext *ua, const char *cmd);
64 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd);
65 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd);
66 static bool dot_bvfs_update(UAContext *ua, const char *cmd);
67 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd);
68 static bool dot_bvfs_versions(UAContext *ua, const char *cmd);
69 static bool dot_bvfs_restore(UAContext *ua, const char *cmd);
70 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd);
71 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd);
72 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd);
73 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd);
75 static bool putfile_cmd(UAContext *ua, const char *cmd);
76 static bool api_cmd(UAContext *ua, const char *cmd);
77 static bool sql_cmd(UAContext *ua, const char *cmd);
78 static bool dot_quit_cmd(UAContext *ua, const char *cmd);
79 static bool dot_help_cmd(UAContext *ua, const char *cmd);
80 static int one_handler(void *ctx, int num_field, char **row);
82 struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
83 static struct cmdstruct commands[] = { /* help */ /* can be used in runscript */
84 { NT_(".api"), api_cmd, NULL, false},
85 { NT_(".backups"), backupscmd, NULL, false},
86 { NT_(".clients"), clientscmd, NULL, true},
87 { NT_(".catalogs"), catalogscmd, NULL, false},
88 { NT_(".defaults"), defaultscmd, NULL, false},
89 { NT_(".die"), admin_cmds, NULL, false},
90 { NT_(".dump"), admin_cmds, NULL, false},
91 { NT_(".exit"), admin_cmds, NULL, false},
92 { NT_(".filesets"), filesetscmd, NULL, false},
93 { NT_(".help"), dot_help_cmd, NULL, false},
94 { NT_(".jobs"), jobscmd, NULL, true},
95 { NT_(".levels"), levelscmd, NULL, false},
96 { NT_(".messages"), getmsgscmd, NULL, false},
97 { NT_(".msgs"), msgscmd, NULL, false},
98 { NT_(".pools"), poolscmd, NULL, true},
99 { NT_(".quit"), dot_quit_cmd, NULL, false},
100 { NT_(".putfile"), putfile_cmd, NULL, false}, /* use @putfile */
101 { NT_(".sql"), sql_cmd, NULL, false},
102 { NT_(".status"), dot_status_cmd, NULL, false},
103 { NT_(".storage"), storagecmd, NULL, true},
104 { NT_(".volstatus"), volstatuscmd, NULL, true},
105 { NT_(".media"), mediacmd, NULL, true},
106 { NT_(".mediatypes"), mediatypescmd, NULL, true},
107 { NT_(".locations"), locationscmd, NULL, true},
108 { NT_(".actiononpurge"),aopcmd, NULL, true},
109 { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL, true},
110 { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles, NULL, true},
111 { NT_(".bvfs_get_volumes"),dot_bvfs_get_volumes,NULL, true},
112 { NT_(".bvfs_update"), dot_bvfs_update, NULL, true},
113 { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL, true},
114 { NT_(".bvfs_versions"), dot_bvfs_versions, NULL, true},
115 { NT_(".bvfs_restore"), dot_bvfs_restore, NULL, true},
116 { NT_(".bvfs_cleanup"), dot_bvfs_cleanup, NULL, true},
117 { NT_(".bvfs_decode_lstat"),dot_bvfs_decode_lstat,NULL, true},
118 { NT_(".bvfs_clear_cache"),dot_bvfs_clear_cache,NULL, false},
119 { NT_(".types"), typescmd, NULL, false}
121 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
124 * Execute a command from the UA
126 bool do_a_dot_command(UAContext *ua)
132 BSOCK *user = ua->UA_sock;
134 Dmsg1(1400, "Dot command: %s\n", user->msg);
139 len = strlen(ua->argk[0]);
141 if (ua->api) user->signal(BNET_CMD_BEGIN);
142 if (ua->api) user->signal(BNET_CMD_OK);
143 return true; /* no op */
145 for (i=0; i<comsize; i++) { /* search for command */
146 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
147 /* Check if this command is authorized in RunScript */
148 if (ua->runscript && !commands[i].use_in_rs) {
149 ua->error_msg(_("Can't use %s command in a runscript"), ua->argk[0]);
153 /* Check if command permitted, but "quit" is always OK */
154 if (strcmp(ua->argk[0], NT_(".quit")) != 0 &&
155 !acl_access_ok(ua, Command_ACL, ua->argk[0], len)) {
158 Dmsg1(100, "Cmd: %s\n", ua->cmd);
160 if (ua->api) user->signal(BNET_CMD_BEGIN);
161 ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */
162 if (ua->api) user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED);
169 ua->error_msg("%s%s", ua->argk[0], _(": is an invalid command.\n"));
176 static bool dot_bvfs_decode_lstat(UAContext *ua, const char *cmd)
181 int pos = find_arg_with_value(ua, "lstat");
184 for (char *p = ua->argv[pos] ; *p ; p++) {
185 if (! (B_ISALPHA(*p) || B_ISDIGIT(*p) || B_ISSPACE(*p) || *p == '/' || *p == '+' || *p == '-')) {
186 ua->error_msg("Can't accept %c in lstat\n", *p);
191 decode_stat(ua->argv[pos], &sp, sizeof(sp), &LinkFI);
192 Mmsg(q, "st_nlink=%lld\nst_mode=%lld\nst_uid=%lld\nst_gid=%lld\nst_size=%lld\n"
193 "st_blocks=%lld\nst_ino=%lld\nst_ctime=%lld\nst_mtime=%lld\nst_mtime=%lld\n"
194 "st_dev=%lld\nLinkFI=%lld\n",
195 (int64_t) sp.st_nlink,
196 (int64_t) sp.st_mode,
199 (int64_t) sp.st_size,
200 (int64_t) sp.st_blocks,
202 (int64_t) sp.st_ctime,
203 (int64_t) sp.st_mtime,
204 (int64_t) sp.st_atime,
209 ua->send_msg("%s", q.c_str());
214 static bool dot_bvfs_update(UAContext *ua, const char *cmd)
216 if (!open_new_client_db(ua)) {
220 int pos = find_arg_with_value(ua, "jobid");
221 if (pos != -1 && is_a_number_list(ua->argv[pos])) {
222 if (!bvfs_update_path_hierarchy_cache(ua->jcr, ua->db, ua->argv[pos])) {
223 ua->error_msg("ERROR: BVFS reported a problem for %s\n",
227 /* update cache for all jobids */
228 bvfs_update_cache(ua->jcr, ua->db);
234 static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd)
236 if (!open_client_db(ua)) {
240 int pos = find_arg(ua, "yes");
242 Bvfs fs(ua->jcr, ua->db);
244 ua->info_msg("OK\n");
246 ua->error_msg("Can't find 'yes' argument\n");
252 static int bvfs_result_handler(void *ctx, int fields, char **row)
254 UAContext *ua = (UAContext *)ctx;
257 char *fileid=row[BVFS_FileId];
258 char *lstat=row[BVFS_LStat];
259 char *jobid=row[BVFS_JobId];
261 char empty[] = "A A A A A A A A A A A A A A";
264 /* We need to deal with non existant path */
265 if (!fileid || !is_a_number(fileid)) {
271 memset(&statp, 0, sizeof(struct stat));
272 decode_stat(lstat, &statp, sizeof(statp), &LinkFI);
274 Dmsg1(100, "type=%s\n", row[0]);
275 if (bvfs_is_dir(row)) {
276 char *path = bvfs_basename_dir(row[BVFS_Name]);
277 ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid,
280 } else if (bvfs_is_version(row)) {
281 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
282 row[BVFS_FilenameId], fileid, jobid,
283 lstat, row[BVFS_Md5], row[BVFS_VolName],
284 row[BVFS_VolInchanger]);
286 } else if (bvfs_is_file(row)) {
287 ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId],
288 row[BVFS_FilenameId], fileid, jobid,
289 lstat, row[BVFS_Name]);
291 } else if (bvfs_is_volume_list(row)) {
292 ua->send_msg("%s\t%s\n", row[BVFS_VolName],
293 row[BVFS_VolInchanger]);
299 static bool bvfs_parse_arg_version(UAContext *ua,
310 for (int i=1; i<ua->argc; i++) {
311 if (fnid && strcasecmp(ua->argk[i], NT_("fnid")) == 0) {
312 if (is_a_number(ua->argv[i])) {
313 *fnid = str_to_int64(ua->argv[i]);
317 if (strcasecmp(ua->argk[i], NT_("client")) == 0) {
318 *client = ua->argv[i];
321 if (copies && strcasecmp(ua->argk[i], NT_("copies")) == 0) {
325 if (versions && strcasecmp(ua->argk[i], NT_("versions")) == 0) {
329 return (*client && *fnid > 0);
332 static bool bvfs_parse_arg(UAContext *ua,
333 DBId_t *pathid, char **path, char **jobid,
335 int *limit, int *offset)
346 for (int i=1; i<ua->argc; i++) {
347 if (strcasecmp(ua->argk[i], NT_("pathid")) == 0) {
348 if (is_a_number(ua->argv[i])) {
349 *pathid = str_to_int64(ua->argv[i]);
353 if (strcasecmp(ua->argk[i], NT_("path")) == 0) {
357 if (strcasecmp(ua->argk[i], NT_("username")) == 0) {
358 *username = ua->argv[i];
361 if (jobid && strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
362 if (is_a_number_list(ua->argv[i])) {
363 *jobid = ua->argv[i];
367 if (strcasecmp(ua->argk[i], NT_("limit")) == 0) {
368 if (is_a_number(ua->argv[i])) {
369 *limit = str_to_int64(ua->argv[i]);
373 if (strcasecmp(ua->argk[i], NT_("offset")) == 0) {
374 if (is_a_number(ua->argv[i])) {
375 *offset = str_to_int64(ua->argv[i]);
380 if (jobid && *jobid == NULL) {
384 if (!(*pathid || *path)) {
391 /* .bvfs_cleanup path=b2XXXXX
393 static bool dot_bvfs_cleanup(UAContext *ua, const char *cmd)
396 if ((i = find_arg_with_value(ua, "path")) >= 0) {
397 if (!open_client_db(ua)) {
400 Bvfs fs(ua->jcr, ua->db);
401 fs.drop_restore_list(ua->argv[i]);
406 /* .bvfs_restore path=b2XXXXX jobid=1,2 fileid=1,2 dirid=1,2 hardlink=1,2,3,4
408 static bool dot_bvfs_restore(UAContext *ua, const char *cmd)
411 int limit=2000, offset=0, i;
412 char *path=NULL, *jobid=NULL, *username=NULL;
413 char *empty = (char *)"";
414 char *fileid, *dirid, *hardlink;
415 fileid = dirid = hardlink = empty;
417 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
420 ua->error_msg("Can't find jobid, pathid or path argument\n");
421 return true; /* not enough param */
424 if (!open_new_client_db(ua)) {
428 Bvfs fs(ua->jcr, ua->db);
429 fs.set_username(username);
430 fs.set_jobids(jobid);
432 if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
433 fileid = ua->argv[i];
435 if ((i = find_arg_with_value(ua, "dirid")) >= 0) {
438 if ((i = find_arg_with_value(ua, "hardlink")) >= 0) {
439 hardlink = ua->argv[i];
442 if (fs.compute_restore_list(fileid, dirid, hardlink, path)) {
443 ua->send_msg("OK\n");
445 ua->error_msg("Can't create restore list\n");
452 * .bvfs_get_volumes [path=/ filename=test jobid=1 | fileid=1]
457 static bool dot_bvfs_get_volumes(UAContext *ua, const char *cmd)
461 char *path=NULL, *jobid=NULL, *username=NULL;
463 int limit=2000, offset=0;
466 bvfs_parse_arg(ua, &pathid, &path, &jobid, &username, &limit, &offset);
468 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
469 if (!(jobid && (path || pathid))) { /* Need JobId and Path/PathId */
470 ua->error_msg("Can't find jobid, pathid or path argument\n");
474 filename = ua->argv[i];
476 } else if ((i = find_arg_with_value(ua, "fileid")) >= 0) {
477 if (!is_a_number(ua->argv[i])) {
478 ua->error_msg("Expecting integer for FileId, got %s\n", ua->argv[i]);
481 fileid = str_to_int64(ua->argv[i]);
484 if (!open_new_client_db(ua)) {
488 Bvfs fs(ua->jcr, ua->db);
489 fs.set_username(username);
490 fs.set_handler(bvfs_result_handler, ua);
497 fs.get_volumes(fileid);
504 * .bvfs_lsfiles jobid=1,2,3,4 pathid=10
505 * .bvfs_lsfiles jobid=1,2,3,4 path=/
507 static bool dot_bvfs_lsfiles(UAContext *ua, const char *cmd)
510 int limit=2000, offset=0;
511 char *path=NULL, *jobid=NULL, *username=NULL;
512 char *pattern=NULL, *filename=NULL;
515 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
518 ua->error_msg("Can't find jobid, pathid or path argument\n");
519 return true; /* not enough param */
521 if ((i = find_arg_with_value(ua, "pattern")) >= 0) {
522 pattern = ua->argv[i];
524 if ((i = find_arg_with_value(ua, "filename")) >= 0) {
525 filename = ua->argv[i];
528 if (!open_new_client_db(ua)) {
532 Bvfs fs(ua->jcr, ua->db);
533 fs.set_username(username);
534 fs.set_jobids(jobid);
535 fs.set_handler(bvfs_result_handler, ua);
538 fs.set_pattern(pattern);
541 fs.set_filename(filename);
549 fs.set_offset(offset);
557 * .bvfs_lsdirs jobid=1,2,3,4 pathid=10
558 * .bvfs_lsdirs jobid=1,2,3,4 path=/
559 * .bvfs_lsdirs jobid=1,2,3,4 path=
561 static bool dot_bvfs_lsdirs(UAContext *ua, const char *cmd)
564 int limit=2000, offset=0;
565 char *path=NULL, *jobid=NULL, *username=NULL;
567 if (!bvfs_parse_arg(ua, &pathid, &path, &jobid, &username,
570 ua->error_msg("Can't find jobid, pathid or path argument\n");
571 return true; /* not enough param */
574 if (!open_new_client_db(ua)) {
578 Bvfs fs(ua->jcr, ua->db);
579 fs.set_username(username);
580 fs.set_jobids(jobid);
582 fs.set_handler(bvfs_result_handler, ua);
590 fs.set_offset(offset);
592 fs.ls_special_dirs();
599 * .bvfs_versions fnid=10 pathid=10 copies versions
602 static bool dot_bvfs_versions(UAContext *ua, const char *cmd)
606 int limit=2000, offset=0;
607 char *path=NULL, *client=NULL, *username=NULL;
608 bool copies=false, versions=false;
609 if (!bvfs_parse_arg(ua, &pathid, &path, NULL, &username,
612 ua->error_msg("Can't find pathid or path argument\n");
613 return true; /* not enough param */
616 if (!bvfs_parse_arg_version(ua, &client, &fnid, &versions, &copies))
618 ua->error_msg("Can't find client or fnid argument\n");
619 return true; /* not enough param */
622 if (!open_new_client_db(ua)) {
626 Bvfs fs(ua->jcr, ua->db);
628 fs.set_see_all_versions(versions);
629 fs.set_see_copies(copies);
630 fs.set_handler(bvfs_result_handler, ua);
631 fs.set_offset(offset);
632 fs.get_all_file_versions(pathid, fnid, client);
637 /* .bvfs_get_jobids jobid=1
638 * -> returns needed jobids to restore
639 * .bvfs_get_jobids ujobid=xxx only
640 * -> returns the jobid of the job
641 * .bvfs_get_jobids jobid=1 jobname
642 * -> returns the jobname
643 * .bvfs_get_jobids client=xxx
644 * -> returns all jobid for the client
645 * .bvfs_get_jobids jobid=1 all
646 * -> returns needed jobids to restore with all filesets a JobId=1 time
647 * .bvfs_get_jobids job=XXXXX
648 * -> returns needed jobids to restore with the jobname
649 * .bvfs_get_jobids ujobid=JobName
650 * -> returns needed jobids to restore
652 static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd)
655 memset(&jr, 0, sizeof(JOB_DBR));
657 db_list_ctx jobids, tempids;
661 dbid_list ids; /* Store all FileSetIds for this client */
663 if (!open_new_client_db(ua)) {
667 Bvfs fs(ua->jcr, ua->db);
669 if ((pos = find_arg_with_value(ua, "username")) >= 0) {
670 fs.set_username(ua->argv[pos]);
673 if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
674 bstrncpy(jr.Job, ua->argv[pos], sizeof(jr.Job));
677 if ((pos = find_arg_with_value(ua, "jobid")) >= 0) {
678 jr.JobId = str_to_int64(ua->argv[pos]);
680 /* Guess JobId from Job name, take the last successful jobid */
681 } else if ((pos = find_arg_with_value(ua, "job")) >= 0) {
686 bstrncpy(jr.Name, ua->argv[pos], MAX_NAME_LENGTH);
687 /* TODO: enhance this function to take client and/or fileset as argument*/
689 job = GetJobResWithName(jr.Name);
691 ua->error_msg(_("Unable to get Job record for Job=%s\n"), jr.Name);
697 "FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) "
698 "WHERE Client.Name = '%s' AND FileSet.FileSet = '%s' "
699 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
700 "ORDER By JobTDate DESC LIMIT 1",
701 job->client->name(), job->fileset->name());
702 ret = db_sql_query(ua->db, ua->db->cmd, db_int_handler, &JobId);
706 ua->error_msg(_("Unable to get last Job record for Job=%s\n"),jr.Name);
711 /* Get JobId from ujobid */
712 } else if ((pos = find_arg_with_value(ua, "ujobid")) >= 0) {
713 bstrncpy(jr.Job, ua->argv[pos], MAX_NAME_LENGTH);
715 /* Return all backup jobid for a client */
716 } else if ((pos = find_arg_with_value(ua, "client")) >= 0) {
720 cli = GetClientResWithName(ua->argv[pos]);
722 ua->error_msg(_("Unable to get Client record for Client=%s\n"),
729 "FROM Job JOIN Client USING (ClientId) "
730 "WHERE Client.Name = '%s' "
731 "AND Job.Type = 'B' AND Job.JobStatus IN ('T', 'W') "
732 "ORDER By JobTDate ASC",
734 ret = db_sql_query(ua->db, ua->db->cmd, db_list_handler, &jobids);
738 ua->error_msg(_("Unable to get last Job record for Client=%s\n"),
742 /* Apply the ACL filter on JobIds */
743 fs.set_jobids(jobids.list);
744 ua->send_msg("%s\n", fs.get_jobids());
748 if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
749 ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"),
750 ua->cmd, db_strerror(ua->db));
754 /* Display only the requested jobid or
755 * When in level base, we don't rely on any Full/Incr/Diff
757 if (find_arg(ua, "only") > 0 || jr.JobLevel == L_BASE) {
758 /* Apply the ACL filter on JobIds */
759 fs.set_jobid(jr.JobId);
760 ua->send_msg("%s\n", fs.get_jobids());
764 /* Display only the requested job name
766 if (find_arg(ua, "jobname") > 0) {
767 /* Apply the ACL filter on JobIds */
768 fs.set_jobid(jr.JobId);
769 if (str_to_int64(fs.get_jobids()) == (int64_t)jr.JobId) {
770 ua->send_msg("%s\n", jr.Job);
775 /* If we have the "all" option, we do a search on all defined fileset
778 if (find_arg(ua, "all") > 0) {
779 edit_int64(jr.ClientId, ed1);
780 Mmsg(query, uar_sel_filesetid, ed1);
781 db_get_query_dbids(ua->jcr, ua->db, query, ids);
784 ids.DBId[0] = jr.FileSetId;
787 jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */
789 /* Foreach different FileSet, we build a restore jobid list */
790 for (int i=0; i < ids.num_ids; i++) {
791 jr.FileSetId = ids.DBId[i];
792 if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) {
798 fs.set_jobids(jobids.list);
799 ua->send_msg("%s\n", fs.get_jobids());
803 static bool dot_quit_cmd(UAContext *ua, const char *cmd)
809 static bool dot_help_cmd(UAContext *ua, const char *cmd)
815 static bool getmsgscmd(UAContext *ua, const char *cmd)
817 if (console_msg_pending) {
818 do_messages(ua, cmd);
824 static void do_storage_cmd(UAContext *ua, STORE *store, const char *cmd)
830 lstore.store = store;
831 pm_strcpy(lstore.store_source, _("unknown source"));
832 set_wstorage(jcr, &lstore);
833 /* Try connecting for up to 15 seconds */
834 ua->send_msg(_("Connecting to Storage daemon %s at %s:%d\n"),
835 store->name(), store->address, store->SDport);
836 if (!connect_to_storage_daemon(jcr, 1, 15, 0)) {
837 ua->error_msg(_("Failed to connect to Storage daemon.\n"));
840 Dmsg0(120, _("Connected to storage daemon\n"));
841 sd = jcr->store_bsock;
842 sd->fsend("%s", cmd);
843 if (sd->recv() >= 0) {
844 ua->send_msg("%s", sd->msg);
846 sd->signal(BNET_TERMINATE);
847 free_bsock(ua->jcr->store_bsock);
851 static void do_client_cmd(UAContext *ua, CLIENT *client, const char *cmd)
855 /* Connect to File daemon */
857 ua->jcr->client = client;
858 /* Try to connect for 15 seconds */
859 ua->send_msg(_("Connecting to Client %s at %s:%d\n"),
860 client->name(), client->address, client->FDport);
861 if (!connect_to_file_daemon(ua->jcr, 1, 15, 0)) {
862 ua->error_msg(_("Failed to connect to Client.\n"));
865 Dmsg0(120, "Connected to file daemon\n");
866 fd = ua->jcr->file_bsock;
867 fd->fsend("%s", cmd);
868 if (fd->recv() >= 0) {
869 ua->send_msg("%s", fd->msg);
871 fd->signal(BNET_TERMINATE);
872 free_bsock(ua->jcr->file_bsock);
879 * .exit (no arg => .quit)
881 static bool admin_cmds(UAContext *ua, const char *cmd)
883 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
887 bool do_deadlock=false;
888 const char *remote_cmd;
892 if (strncmp(ua->argk[0], ".die", 4) == 0) {
893 if (find_arg(ua, "deadlock") > 0) {
895 remote_cmd = ".die deadlock";
899 } else if (strncmp(ua->argk[0], ".dump", 5) == 0) {
900 remote_cmd = "sm_dump";
901 } else if (strncmp(ua->argk[0], ".exit", 5) == 0) {
904 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
908 for (i=1; i<ua->argc; i++) {
909 if (strcasecmp(ua->argk[i], "dir") == 0 ||
910 strcasecmp(ua->argk[i], "director") == 0) {
913 if (strcasecmp(ua->argk[i], "client") == 0 ||
914 strcasecmp(ua->argk[i], "fd") == 0) {
917 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[i]);
920 client = select_client_resource(ua);
924 if (strcasecmp(ua->argk[i], NT_("store")) == 0 ||
925 strcasecmp(ua->argk[i], NT_("storage")) == 0 ||
926 strcasecmp(ua->argk[i], NT_("sd")) == 0) {
929 store = (STORE *)GetResWithName(R_STORAGE, ua->argv[i]);
932 store = get_storage_resource(ua, false/*no default*/);
937 if (!dir && !store && !client) {
939 * We didn't find an appropriate keyword above, so
942 start_prompt(ua, _("Available daemons are: \n"));
943 add_prompt(ua, _("Director"));
944 add_prompt(ua, _("Storage"));
945 add_prompt(ua, _("Client"));
946 switch(do_prompt(ua, "", _("Select daemon type to make die"), NULL, 0)) {
947 case 0: /* Director */
951 store = get_storage_resource(ua, false/*no default*/);
954 client = select_client_resource(ua);
962 do_storage_cmd(ua, store, remote_cmd);
966 do_client_cmd(ua, client, remote_cmd);
970 if (strncmp(remote_cmd, ".die", 4) == 0) {
972 ua->send_msg(_("The Director will generate a deadlock.\n"));
976 ua->send_msg(_("The Director will segment fault.\n"));
977 a = jcr->JobId; /* ref NULL pointer */
978 jcr->JobId = 1000; /* another ref NULL pointer */
981 } else if (strncmp(remote_cmd, ".dump", 5) == 0) {
982 sm_dump(false, true);
983 } else if (strncmp(remote_cmd, ".exit", 5) == 0) {
984 dot_quit_cmd(ua, cmd);
994 * Dummy routine for non-development version
996 static bool admin_cmds(UAContext *ua, const char *cmd)
998 ua->error_msg(_("Unknown command: %s\n"), ua->argk[0]);
1005 * Send a file to the director from bconsole @putfile command
1006 * The .putfile can not be used directly.
1008 static bool putfile_cmd(UAContext *ua, const char *cmd)
1010 int pos, i, pnl, fnl;
1012 POOLMEM *name = get_pool_memory(PM_FNAME);
1013 POOLMEM *path = get_pool_memory(PM_FNAME);
1014 POOLMEM *fname= get_pool_memory(PM_FNAME);
1015 const char *key = "putfile";
1018 if ((pos = find_arg_with_value(ua, "key")) > 0) {
1019 /* Check the string if the string is valid */
1020 for (i=0; ua->argv[pos][i] && isalnum(ua->argv[pos][i]) && i < 16; i++);
1022 if (ua->argv[pos][i] == 0) {
1023 key = ua->argv[pos];
1026 ua->error_msg("Invalid key name for putfile command");
1032 /* the (intptr_t)ua will allow one file per console session */
1033 make_unique_filename(&name, (intptr_t)ua, (char *)key);
1035 fp = fopen(name, "w");
1038 ua->error_msg("Unable to open destination file. ERR=%s\n",
1039 be.bstrerror(errno));
1044 while (ua->UA_sock->recv() > 0) {
1045 if (fwrite(ua->UA_sock->msg, ua->UA_sock->msglen, 1, fp) != 1) {
1047 ua->error_msg("Unable to write to the destination file. ERR=%s\n",
1048 be.bstrerror(errno));
1050 /* TODO: Check if we need to quit here (data will still be in the
1055 split_path_and_filename(name, &path, &pnl, &fname, &fnl);
1059 ua->send_msg("OK\n");
1062 ua->send_msg("ERROR\n");
1065 free_pool_memory(name);
1066 free_pool_memory(path);
1067 free_pool_memory(fname);
1075 * Can use an argument to filter on JobType
1078 static bool jobscmd(UAContext *ua, const char *cmd)
1083 if ((pos = find_arg_with_value(ua, "type")) >= 0) {
1084 type = ua->argv[pos][0];
1087 foreach_res(job, R_JOB) {
1088 if (!type || type == job->JobType) {
1089 if (acl_access_ok(ua, Job_ACL, job->name())) {
1090 ua->send_msg("%s\n", job->name());
1098 static bool filesetscmd(UAContext *ua, const char *cmd)
1102 foreach_res(fs, R_FILESET) {
1103 if (acl_access_ok(ua, FileSet_ACL, fs->name())) {
1104 ua->send_msg("%s\n", fs->name());
1111 static bool catalogscmd(UAContext *ua, const char *cmd)
1115 foreach_res(cat, R_CATALOG) {
1116 if (acl_access_ok(ua, Catalog_ACL, cat->name())) {
1117 ua->send_msg("%s\n", cat->name());
1124 static bool clientscmd(UAContext *ua, const char *cmd)
1128 foreach_res(client, R_CLIENT) {
1129 if (acl_access_ok(ua, Client_ACL, client->name())) {
1130 ua->send_msg("%s\n", client->name());
1137 static bool msgscmd(UAContext *ua, const char *cmd)
1141 foreach_res(msgs, R_MSGS) {
1142 ua->send_msg("%s\n", msgs->name());
1148 static bool poolscmd(UAContext *ua, const char *cmd)
1152 foreach_res(pool, R_POOL) {
1153 if (acl_access_ok(ua, Pool_ACL, pool->name())) {
1154 ua->send_msg("%s\n", pool->name());
1161 static bool storagecmd(UAContext *ua, const char *cmd)
1165 foreach_res(store, R_STORAGE) {
1166 if (acl_access_ok(ua, Storage_ACL, store->name())) {
1167 ua->send_msg("%s\n", store->name());
1174 static bool aopcmd(UAContext *ua, const char *cmd)
1176 ua->send_msg("None\n");
1177 ua->send_msg("Truncate\n");
1181 static bool typescmd(UAContext *ua, const char *cmd)
1183 ua->send_msg("Backup\n");
1184 ua->send_msg("Restore\n");
1185 ua->send_msg("Admin\n");
1186 ua->send_msg("Verify\n");
1187 ua->send_msg("Migrate\n");
1188 ua->send_msg("Copy\n");
1193 * If this command is called, it tells the director that we
1194 * are a program that wants a sort of API, and hence,
1195 * we will probably suppress certain output, include more
1196 * error codes, and most of all send back a good number
1197 * of new signals that indicate whether or not the command
1200 static bool api_cmd(UAContext *ua, const char *cmd)
1203 if (ua->argc >= 2) {
1204 ua->api = atoi(ua->argk[1]);
1206 /* Get output configuration options such as time format or separator */
1207 if ((i = find_arg_with_value(ua, "api_opts")) > 0) {
1208 bstrncpy(ua->api_opts, ua->argv[i], sizeof(ua->api_opts));
1219 static int client_backups_handler(void *ctx, int num_field, char **row)
1221 UAContext *ua = (UAContext *)ctx;
1222 ua->send_msg("| %s | %s | %s | %s | %s | %s | %s | %s |\n",
1223 row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8]);
1228 * Return the backups for this client
1230 * .backups client=xxx fileset=yyy
1233 static bool backupscmd(UAContext *ua, const char *cmd)
1235 if (!open_client_db(ua)) {
1238 if (ua->argc != 3 || strcmp(ua->argk[1], "client") != 0 ||
1239 strcmp(ua->argk[2], "fileset") != 0) {
1242 if (!acl_access_ok(ua, Client_ACL, ua->argv[1]) ||
1243 !acl_access_ok(ua, FileSet_ACL, ua->argv[2])) {
1244 ua->error_msg(_("Access to specified Client or FileSet not allowed.\n"));
1247 Mmsg(ua->cmd, client_backups, ua->argv[1], ua->argv[2]);
1248 if (!db_sql_query(ua->db, ua->cmd, client_backups_handler, (void *)ua)) {
1249 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1255 static int sql_handler(void *ctx, int num_field, char **row)
1257 UAContext *ua = (UAContext *)ctx;
1258 POOL_MEM rows(PM_MESSAGE);
1260 /* Check for nonsense */
1261 if (num_field == 0 || row == NULL || row[0] == NULL) {
1262 return 0; /* nothing returned */
1264 for (int i=0; num_field--; i++) {
1266 pm_strcpy(rows, NPRT(row[0]));
1268 pm_strcat(rows, NPRT(row[i]));
1270 pm_strcat(rows, "\t");
1272 if (!rows.c_str() || !*rows.c_str()) {
1275 ua->send_msg("%s", rows.c_str());
1280 static bool sql_cmd(UAContext *ua, const char *cmd)
1283 if (!open_new_client_db(ua)) {
1286 index = find_arg_with_value(ua, "query");
1288 ua->error_msg(_("query keyword not found.\n"));
1291 if (!db_sql_query(ua->db, ua->argv[index], sql_handler, (void *)ua)) {
1292 Dmsg1(100, "Query failed: ERR=%s\n", db_strerror(ua->db));
1293 ua->error_msg(_("Query failed: %s. ERR=%s\n"), ua->cmd, db_strerror(ua->db));
1299 static int one_handler(void *ctx, int num_field, char **row)
1301 UAContext *ua = (UAContext *)ctx;
1302 ua->send_msg("%s\n", row[0]);
1306 static bool mediatypescmd(UAContext *ua, const char *cmd)
1308 if (!open_client_db(ua)) {
1311 if (!db_sql_query(ua->db,
1312 "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType",
1313 one_handler, (void *)ua))
1315 ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db));
1320 static bool mediacmd(UAContext *ua, const char *cmd)
1322 if (!open_client_db(ua)) {
1325 if (!db_sql_query(ua->db,
1326 "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName",
1327 one_handler, (void *)ua))
1329 ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db));
1334 static bool locationscmd(UAContext *ua, const char *cmd)
1336 if (!open_client_db(ua)) {
1339 if (!db_sql_query(ua->db,
1340 "SELECT DISTINCT Location FROM Location ORDER BY Location",
1341 one_handler, (void *)ua))
1343 ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db));
1348 static bool levelscmd(UAContext *ua, const char *cmd)
1351 /* Note some levels are blank, which means none is needed */
1352 if (ua->argc == 1) {
1353 for (i=0; joblevels[i].level_name; i++) {
1354 if (joblevels[i].level_name[0] != ' ') {
1355 ua->send_msg("%s\n", joblevels[i].level_name);
1358 } else if (ua->argc == 2) {
1360 /* Assume that first argument is the Job Type */
1361 for (i=0; jobtypes[i].type_name; i++) {
1362 if (strcasecmp(ua->argk[1], jobtypes[i].type_name) == 0) {
1363 jobtype = jobtypes[i].job_type;
1367 for (i=0; joblevels[i].level_name; i++) {
1368 if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) {
1369 ua->send_msg("%s\n", joblevels[i].level_name);
1377 static bool volstatuscmd(UAContext *ua, const char *cmd)
1379 ua->send_msg("Append\n");
1380 ua->send_msg("Full\n");
1381 ua->send_msg("Used\n");
1382 ua->send_msg("Recycle\n");
1383 ua->send_msg("Purged\n");
1384 ua->send_msg("Cleaning\n");
1385 ua->send_msg("Error\n");
1390 * Return default values for a job
1392 static bool defaultscmd(UAContext *ua, const char *cmd)
1400 if (ua->argc != 2 || !ua->argv[1]) {
1405 if (strcmp(ua->argk[1], "job") == 0) {
1406 if (!acl_access_ok(ua, Job_ACL, ua->argv[1])) {
1409 job = (JOB *)GetResWithName(R_JOB, ua->argv[1]);
1412 ua->send_msg("job=%s", job->name());
1413 ua->send_msg("pool=%s", job->pool->name());
1414 ua->send_msg("messages=%s", job->messages->name());
1415 ua->send_msg("client=%s", job->client->name());
1416 get_job_storage(&store, job, NULL);
1417 ua->send_msg("storage=%s", store.store->name());
1418 ua->send_msg("where=%s", job->RestoreWhere?job->RestoreWhere:"");
1419 ua->send_msg("level=%s", level_to_str(job->JobLevel));
1420 ua->send_msg("type=%s", job_type_to_str(job->JobType));
1421 ua->send_msg("fileset=%s", job->fileset->name());
1422 ua->send_msg("enabled=%d", job->enabled);
1423 ua->send_msg("catalog=%s", job->client->catalog->name());
1426 /* Client defaults */
1427 else if (strcmp(ua->argk[1], "client") == 0) {
1428 if (!acl_access_ok(ua, Client_ACL, ua->argv[1])) {
1431 client = (CLIENT *)GetResWithName(R_CLIENT, ua->argv[1]);
1433 ua->send_msg("client=%s", client->name());
1434 ua->send_msg("address=%s", client->address);
1435 ua->send_msg("fdport=%d", client->FDport);
1436 ua->send_msg("file_retention=%s", edit_uint64(client->FileRetention, ed1));
1437 ua->send_msg("job_retention=%s", edit_uint64(client->JobRetention, ed1));
1438 ua->send_msg("autoprune=%d", client->AutoPrune);
1439 ua->send_msg("catalog=%s", client->catalog->name());
1442 /* Storage defaults */
1443 else if (strcmp(ua->argk[1], "storage") == 0) {
1444 if (!acl_access_ok(ua, Storage_ACL, ua->argv[1])) {
1447 storage = (STORE *)GetResWithName(R_STORAGE, ua->argv[1]);
1450 ua->send_msg("storage=%s", storage->name());
1451 ua->send_msg("address=%s", storage->address);
1452 ua->send_msg("enabled=%d", storage->enabled);
1453 ua->send_msg("media_type=%s", storage->media_type);
1454 ua->send_msg("sdport=%d", storage->SDport);
1455 device = (DEVICE *)storage->device->first();
1456 ua->send_msg("device=%s", device->name());
1457 if (storage->device->size() > 1) {
1458 while ((device = (DEVICE *)storage->device->next())) {
1459 ua->send_msg(",%s", device->name());
1465 else if (strcmp(ua->argk[1], "pool") == 0) {
1466 if (!acl_access_ok(ua, Pool_ACL, ua->argv[1])) {
1469 pool = (POOL *)GetResWithName(R_POOL, ua->argv[1]);
1471 ua->send_msg("pool=%s", pool->name());
1472 ua->send_msg("pool_type=%s", pool->pool_type);
1473 ua->send_msg("label_format=%s", pool->label_format?pool->label_format:"");
1474 ua->send_msg("use_volume_once=%d", pool->use_volume_once);
1475 ua->send_msg("purge_oldest_volume=%d", pool->purge_oldest_volume);
1476 ua->send_msg("recycle_oldest_volume=%d", pool->recycle_oldest_volume);
1477 ua->send_msg("recycle_current_volume=%d", pool->recycle_current_volume);
1478 ua->send_msg("max_volumes=%d", pool->max_volumes);
1479 ua->send_msg("vol_retention=%s", edit_uint64(pool->VolRetention, ed1));
1480 ua->send_msg("vol_use_duration=%s", edit_uint64(pool->VolUseDuration, ed1));
1481 ua->send_msg("max_vol_jobs=%d", pool->MaxVolJobs);
1482 ua->send_msg("max_vol_files=%d", pool->MaxVolFiles);
1483 ua->send_msg("max_vol_bytes=%s", edit_uint64(pool->MaxVolBytes, ed1));
1484 ua->send_msg("auto_prune=%d", pool->AutoPrune);
1485 ua->send_msg("recycle=%d", pool->Recycle);
1486 ua->send_msg("file_retention=%s", edit_uint64(pool->FileRetention, ed1));
1487 ua->send_msg("job_retention=%s", edit_uint64(pool->JobRetention, ed1));