2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Bacula Director -- User Agent Database File tree for Restore
31 * command. This file interacts with the user implementing the
34 * Kern Sibbald, July MMII
44 #include "lib/fnmatch.h"
46 #include "findlib/find.h"
49 /* Forward referenced commands */
51 static int markcmd(UAContext *ua, TREE_CTX *tree);
52 static int markdircmd(UAContext *ua, TREE_CTX *tree);
53 static int countcmd(UAContext *ua, TREE_CTX *tree);
54 static int findcmd(UAContext *ua, TREE_CTX *tree);
55 static int lscmd(UAContext *ua, TREE_CTX *tree);
56 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree);
57 static int dircmd(UAContext *ua, TREE_CTX *tree);
58 static int dot_dircmd(UAContext *ua, TREE_CTX *tree);
59 static int estimatecmd(UAContext *ua, TREE_CTX *tree);
60 static int helpcmd(UAContext *ua, TREE_CTX *tree);
61 static int cdcmd(UAContext *ua, TREE_CTX *tree);
62 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
63 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree);
64 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
65 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree);
66 static int quitcmd(UAContext *ua, TREE_CTX *tree);
67 static int donecmd(UAContext *ua, TREE_CTX *tree);
70 struct cmdstruct { const char *key; int (*func)(UAContext *ua, TREE_CTX *tree); const char *help; };
71 static struct cmdstruct commands[] = {
72 { NT_("cd"), cdcmd, _("change current directory")},
73 { NT_("count"), countcmd, _("count marked files in and below the cd")},
74 { NT_("dir"), dircmd, _("long list current directory, wildcards allowed")},
75 { NT_(".dir"), dot_dircmd, _("long list current directory, wildcards allowed")},
76 { NT_("done"), donecmd, _("leave file selection mode")},
77 { NT_("estimate"), estimatecmd, _("estimate restore size")},
78 { NT_("exit"), donecmd, _("same as done command")},
79 { NT_("find"), findcmd, _("find files, wildcards allowed")},
80 { NT_("help"), helpcmd, _("print help")},
81 { NT_("ls"), lscmd, _("list current directory, wildcards allowed")},
82 { NT_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")},
83 { NT_("mark"), markcmd, _("mark dir/file to be restored recursively, wildcards allowed")},
84 { NT_("markdir"), markdircmd, _("mark directory name to be restored (no files)")},
85 { NT_("pwd"), pwdcmd, _("print current working directory")},
86 { NT_(".pwd"), dot_pwdcmd, _("print current working directory")},
87 { NT_("unmark"), unmarkcmd, _("unmark dir/file to be restored recursively in dir")},
88 { NT_("unmarkdir"), unmarkdircmd, _("unmark directory name only no recursion")},
89 { NT_("quit"), quitcmd, _("quit and do not do restore")},
90 { NT_("?"), helpcmd, _("print help")},
92 #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct)))
95 * Enter a prompt mode where the user can select/deselect
96 * files to be restored. This is sort of like a mini-shell
97 * that allows "cd", "pwd", "add", "rm", ...
99 bool user_select_files_from_tree(TREE_CTX *tree)
103 /* Get a new context so we don't destroy restore command args */
104 UAContext *ua = new_ua_context(tree->ua->jcr);
105 ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */
106 ua->api = tree->ua->api; /* keep API flag too */
107 BSOCK *user = ua->UA_sock;
110 "\nYou are now entering file selection mode where you add (mark) and\n"
111 "remove (unmark) files to be restored. No files are initially added, unless\n"
112 "you used the \"all\" keyword on the command line.\n"
113 "Enter \"done\" to leave this mode.\n\n"));
114 if (ua->api) user->signal(BNET_START_RTREE);
116 * Enter interactive command handler allowing selection
117 * of individual files.
119 tree->node = (TREE_NODE *)tree->root;
120 tree_getpath(tree->node, cwd, sizeof(cwd));
121 ua->send_msg(_("cwd is: %s\n"), cwd);
124 if (!get_cmd(ua, "$ ")) {
127 if (ua->api) user->signal(BNET_CMD_BEGIN);
128 parse_args_only(ua->cmd, &ua->args, &ua->argc, ua->argk, ua->argv, MAX_CMD_ARGS);
130 ua->warning_msg(_("Invalid command. Enter \"done\" to exit.\n"));
131 if (ua->api) user->signal(BNET_CMD_FAILED);
135 len = strlen(ua->argk[0]);
138 for (i=0; i<comsize; i++) /* search for command */
139 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
140 stat = (*commands[i].func)(ua, tree); /* go execute command */
145 ua->warning_msg(_("Invalid command. Enter \"done\" to exit.\n"));
146 if (ua->api) user->signal(BNET_CMD_FAILED);
149 if (ua->api) user->signal(BNET_CMD_OK);
154 if (ua->api) user->signal(BNET_END_RTREE);
155 ua->UA_sock = NULL; /* don't release restore socket */
158 free_ua_context(ua); /* get rid of temp UA context */
164 * This callback routine is responsible for inserting the
165 * items it gets into the directory tree. For each JobId selected
166 * this routine is called once for each file. We do not allow
167 * duplicate filenames, but instead keep the info from the most
168 * recent file entered (i.e. the JobIds are assumed to be sorted)
170 * See uar_sel_files in sql_cmds.c for query that calls us.
171 * row[0]=Path, row[1]=Filename, row[2]=FileIndex
172 * row[3]=JobId row[4]=LStat
174 int insert_tree_handler(void *ctx, int num_fields, char **row)
177 TREE_CTX *tree = (TREE_CTX *)ctx;
184 // Dmsg4(000, "Path=%s%s FI=%s JobId=%s\n", row[0], row[1],
186 if (*row[1] == 0) { /* no filename => directory */
187 if (!IsPathSeparator(*row[0])) { /* Must be Win32 directory */
195 hard_link = (decode_LinkFI(row[4], &statp) != 0);
196 node = insert_tree_node(row[0], row[1], type, tree->root, NULL);
197 JobId = str_to_int64(row[3]);
198 FileIndex = str_to_int64(row[2]);
200 * - The first time we see a file (node->inserted==true), we accept it.
201 * - In the same JobId, we accept only the first copy of a
202 * hard linked file (the others are simply pointers).
203 * - In the same JobId, we accept the last copy of any other
204 * file -- in particular directories.
206 * All the code to set ok could be condensed to a single
207 * line, but it would be even harder to read.
210 if (!node->inserted && JobId == node->JobId) {
211 if ((hard_link && FileIndex > node->FileIndex) ||
212 (!hard_link && FileIndex < node->FileIndex)) {
217 node->hard_link = hard_link;
218 node->FileIndex = FileIndex;
221 node->soft_link = S_ISLNK(statp.st_mode) != 0;
223 node->extract = true; /* extract all by default */
224 if (type == TN_DIR || type == TN_DIR_NLS) {
225 node->extract_dir = true; /* if dir, extract it */
229 if (node->inserted) {
231 if (tree->DeltaCount > 0 && (tree->FileCount-tree->LastCount) > tree->DeltaCount) {
232 tree->ua->send_msg("+");
233 tree->LastCount = tree->FileCount;
242 * Set extract to value passed. We recursively walk
243 * down the tree setting all children if the
244 * node is a directory.
246 static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract)
253 node->extract = extract;
254 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
255 node->extract_dir = extract; /* set/clear dir too */
257 if (node->type != TN_NEWDIR) {
260 /* For a non-file (i.e. directory), we see all the children */
261 if (node->type != TN_FILE || (node->soft_link && tree_node_has_child(node))) {
262 /* Recursive set children within directory */
263 foreach_child(n, node) {
264 count += set_extract(ua, n, tree, extract);
267 * Walk up tree marking any unextracted parent to be
271 while (node->parent && !node->parent->extract_dir) {
273 node->extract_dir = true;
276 } else if (extract) {
279 * Ordinary file, we get the full path, look up the
280 * attributes, decode them, and if we are hard linked to
281 * a file that was saved, we must load that file too.
283 tree_getpath(node, cwd, sizeof(cwd));
285 fdbr.JobId = node->JobId;
286 if (node->hard_link && db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
288 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
290 * If we point to a hard linked file, traverse the tree to
291 * find that file, and mark it to be restored as well. It
292 * must have the Link we just obtained and the same JobId.
295 for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
296 if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
298 if (n->type == TN_DIR || n->type == TN_DIR_NLS) {
299 n->extract_dir = true;
310 static void strip_trailing_slash(char *arg)
312 int len = strlen(arg);
317 if (arg[len] == '/') { /* strip any trailing slash */
323 * Recursively mark the current directory to be restored as
324 * well as all directories and files below it.
326 static int markcmd(UAContext *ua, TREE_CTX *tree)
332 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
333 ua->send_msg(_("No files marked.\n"));
336 for (int i=1; i < ua->argc; i++) {
337 strip_trailing_slash(ua->argk[i]);
338 foreach_child(node, tree->node) {
339 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
340 count += set_extract(ua, node, tree, true);
345 ua->send_msg(_("No files marked.\n"));
346 } else if (count == 1) {
347 ua->send_msg(_("1 file marked.\n"));
349 ua->send_msg(_("%s files marked.\n"),
350 edit_uint64_with_commas(count, ec1));
355 static int markdircmd(UAContext *ua, TREE_CTX *tree)
361 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
362 ua->send_msg(_("No files marked.\n"));
365 for (int i=1; i < ua->argc; i++) {
366 strip_trailing_slash(ua->argk[i]);
367 foreach_child(node, tree->node) {
368 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
369 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
370 node->extract_dir = true;
377 ua->send_msg(_("No directories marked.\n"));
378 } else if (count == 1) {
379 ua->send_msg(_("1 directory marked.\n"));
381 ua->send_msg(_("%s directories marked.\n"),
382 edit_uint64_with_commas(count, ec1));
388 static int countcmd(UAContext *ua, TREE_CTX *tree)
390 int total, num_extract;
391 char ec1[50], ec2[50];
393 total = num_extract = 0;
394 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
395 if (node->type != TN_NEWDIR) {
397 if (node->extract || node->extract_dir) {
402 ua->send_msg(_("%s total files/dirs. %s marked to be restored.\n"),
403 edit_uint64_with_commas(total, ec1),
404 edit_uint64_with_commas(num_extract, ec2));
408 static int findcmd(UAContext *ua, TREE_CTX *tree)
413 ua->send_msg(_("No file specification given.\n"));
414 return 1; /* make it non-fatal */
417 for (int i=1; i < ua->argc; i++) {
418 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
419 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
421 tree_getpath(node, cwd, sizeof(cwd));
424 } else if (node->extract_dir) {
429 ua->send_msg("%s%s\n", tag, cwd);
438 static int lscmd(UAContext *ua, TREE_CTX *tree)
442 if (!tree_node_has_child(tree->node)) {
445 foreach_child(node, tree->node) {
446 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
450 } else if (node->extract_dir) {
455 ua->send_msg("%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
462 * Ls command that lists only the marked files
464 static void rlsmark(UAContext *ua, TREE_NODE *tnode)
467 if (!tree_node_has_child(tnode)) {
470 foreach_child(node, tnode) {
471 if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
472 (node->extract || node->extract_dir)) {
476 } else if (node->extract_dir) {
481 ua->send_msg("%s%s%s\n", tag, node->fname, tree_node_has_child(node)?"/":"");
482 if (tree_node_has_child(node)) {
489 static int lsmarkcmd(UAContext *ua, TREE_CTX *tree)
491 rlsmark(ua, tree->node);
496 * This is actually the long form used for "dir"
498 static void ls_output(guid_list *guid, char *buf, const char *fname, const char *tag,
499 struct stat *statp, bool dot_cmd)
504 char en1[30], en2[30];
508 p = encode_mode(statp->st_mode, buf);
511 n = sprintf(p, "%d,", (uint32_t)statp->st_nlink);
513 n = sprintf(p, "%s,%s,",
514 guid->uid_to_name(statp->st_uid, en1, sizeof(en1)),
515 guid->gid_to_name(statp->st_gid, en2, sizeof(en2)));
517 n = sprintf(p, "%s,", edit_int64(statp->st_size, ec1));
519 p = encode_time(statp->st_mtime, p);
524 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
526 n = sprintf(p, "%-8.8s %-8.8s",
527 guid->uid_to_name(statp->st_uid, en1, sizeof(en1)),
528 guid->gid_to_name(statp->st_gid, en2, sizeof(en2)));
530 n = sprintf(p, "%10.10s ", edit_int64(statp->st_size, ec1));
532 if (statp->st_ctime > statp->st_mtime) {
533 time = statp->st_ctime;
535 time = statp->st_mtime;
537 /* Display most recent time */
538 p = encode_time(time, p);
542 for (f=fname; *f; ) {
549 * Like ls command, but give more detail on each file
551 static int do_dircmd(UAContext *ua, TREE_CTX *tree, bool dot_cmd)
557 char cwd[1100], *pcwd;
560 if (!tree_node_has_child(tree->node)) {
561 ua->send_msg(_("Node %s has no children.\n"), tree->node->fname);
565 guid = new_guid_list();
566 foreach_child(node, tree->node) {
568 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
571 } else if (node->extract_dir) {
576 tree_getpath(node, cwd, sizeof(cwd));
578 fdbr.JobId = node->JobId;
580 * Strip / from soft links to directories.
581 * This is because soft links to files have a trailing slash
582 * when returned from tree_getpath, but db_get_file_attr...
583 * treats soft links as files, so they do not have a trailing
584 * slash like directory names.
586 if (node->type == TN_FILE && tree_node_has_child(node)) {
587 bstrncpy(buf, cwd, sizeof(buf));
589 int len = strlen(buf);
591 buf[len-1] = 0; /* strip trailing / */
596 if (db_get_file_attributes_record(ua->jcr, ua->db, pcwd, NULL, &fdbr)) {
598 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
600 /* Something went wrong getting attributes -- print name */
601 memset(&statp, 0, sizeof(statp));
603 ls_output(guid, buf, cwd, tag, &statp, dot_cmd);
604 ua->send_msg("%s\n", buf);
607 free_guid_list(guid);
611 int dot_dircmd(UAContext *ua, TREE_CTX *tree)
613 return do_dircmd(ua, tree, true/*dot command*/);
616 static int dircmd(UAContext *ua, TREE_CTX *tree)
618 return do_dircmd(ua, tree, false/*not dot command*/);
622 static int estimatecmd(UAContext *ua, TREE_CTX *tree)
624 int total, num_extract;
625 uint64_t total_bytes = 0;
631 total = num_extract = 0;
632 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
633 if (node->type != TN_NEWDIR) {
635 /* If regular file, get size */
636 if (node->extract && node->type == TN_FILE) {
638 tree_getpath(node, cwd, sizeof(cwd));
640 fdbr.JobId = node->JobId;
641 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
643 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
644 if (S_ISREG(statp.st_mode) && statp.st_size > 0) {
645 total_bytes += statp.st_size;
648 /* Directory, count only */
649 } else if (node->extract || node->extract_dir) {
654 ua->send_msg(_("%d total files; %d marked to be restored; %s bytes.\n"),
655 total, num_extract, edit_uint64_with_commas(total_bytes, ec1));
661 static int helpcmd(UAContext *ua, TREE_CTX *tree)
665 ua->send_msg(_(" Command Description\n ======= ===========\n"));
666 for (i=0; i<comsize; i++) {
667 /* List only non-dot commands */
668 if (commands[i].key[0] != '.') {
669 ua->send_msg(" %-10s %s\n", _(commands[i].key), _(commands[i].help));
677 * Change directories. Note, if the user specifies x: and it fails,
678 * we assume it is a Win32 absolute cd rather than relative and
679 * try a second time with /x: ... Win32 kludge.
681 static int cdcmd(UAContext *ua, TREE_CTX *tree)
688 ua->error_msg(_("Too few or too many arguments. Try using double quotes.\n"));
691 node = tree_cwd(ua->argk[1], tree->root, tree->node);
693 /* Try once more if Win32 drive -- make absolute */
694 if (ua->argk[1][1] == ':') { /* win32 drive */
695 bstrncpy(cwd, "/", sizeof(cwd));
696 bstrncat(cwd, ua->argk[1], sizeof(cwd));
697 node = tree_cwd(cwd, tree->root, tree->node);
700 ua->warning_msg(_("Invalid path given.\n"));
707 return pwdcmd(ua, tree);
710 static int pwdcmd(UAContext *ua, TREE_CTX *tree)
713 tree_getpath(tree->node, cwd, sizeof(cwd));
715 ua->send_msg("%s", cwd);
717 ua->send_msg(_("cwd is: %s\n"), cwd);
722 static int dot_pwdcmd(UAContext *ua, TREE_CTX *tree)
725 tree_getpath(tree->node, cwd, sizeof(cwd));
726 ua->send_msg("%s", cwd);
730 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
735 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
736 ua->send_msg(_("No files unmarked.\n"));
739 for (int i=1; i < ua->argc; i++) {
740 strip_trailing_slash(ua->argk[i]);
741 foreach_child(node, tree->node) {
742 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
743 count += set_extract(ua, node, tree, false);
748 ua->send_msg(_("No files unmarked.\n"));
749 } else if (count == 1) {
750 ua->send_msg(_("1 file unmarked.\n"));
753 ua->send_msg(_("%s files unmarked.\n"), edit_uint64_with_commas(count, ed1));
758 static int unmarkdircmd(UAContext *ua, TREE_CTX *tree)
763 if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
764 ua->send_msg(_("No directories unmarked.\n"));
768 for (int i=1; i < ua->argc; i++) {
769 strip_trailing_slash(ua->argk[i]);
770 foreach_child(node, tree->node) {
771 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
772 if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
773 node->extract_dir = false;
781 ua->send_msg(_("No directories unmarked.\n"));
782 } else if (count == 1) {
783 ua->send_msg(_("1 directory unmarked.\n"));
785 ua->send_msg(_("%d directories unmarked.\n"), count);
791 static int donecmd(UAContext *ua, TREE_CTX *tree)
796 static int quitcmd(UAContext *ua, TREE_CTX *tree)