3 * Bacula Director -- User Agent Database File tree for Restore
6 * Kern Sibbald, July MMII
12 Copyright (C) 2002-2003 Kern Sibbald and John Walker
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of
17 the License, or (at your option) any later version.
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public
25 License along with this program; if not, write to the Free
26 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
34 #include "findlib/find.h"
37 /* Forward referenced commands */
39 static int markcmd(UAContext *ua, TREE_CTX *tree);
40 static int countcmd(UAContext *ua, TREE_CTX *tree);
41 static int findcmd(UAContext *ua, TREE_CTX *tree);
42 static int lscmd(UAContext *ua, TREE_CTX *tree);
43 static int dircmd(UAContext *ua, TREE_CTX *tree);
44 static int helpcmd(UAContext *ua, TREE_CTX *tree);
45 static int cdcmd(UAContext *ua, TREE_CTX *tree);
46 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
47 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
48 static int quitcmd(UAContext *ua, TREE_CTX *tree);
51 struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; };
52 static struct cmdstruct commands[] = {
53 { N_("mark"), markcmd, _("mark file for restoration")},
54 { N_("unmark"), unmarkcmd, _("unmark file for restoration")},
55 { N_("cd"), cdcmd, _("change current directory")},
56 { N_("pwd"), pwdcmd, _("print current working directory")},
57 { N_("ls"), lscmd, _("list current directory")},
58 { N_("dir"), dircmd, _("list current directory")},
59 { N_("count"), countcmd, _("count marked files")},
60 { N_("find"), findcmd, _("find files")},
61 { N_("done"), quitcmd, _("leave file selection mode")},
62 { N_("exit"), quitcmd, _("exit = done")},
63 { N_("help"), helpcmd, _("print help")},
64 { N_("?"), helpcmd, _("print help")},
66 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
70 * Enter a prompt mode where the user can select/deselect
71 * files to be restored. This is sort of like a mini-shell
72 * that allows "cd", "pwd", "add", "rm", ...
74 void user_select_files_from_tree(TREE_CTX *tree)
79 "\nYou are now entering file selection mode where you add and\n"
80 "remove files to be restored. All files are initially added.\n"
81 "Enter \"done\" to leave this mode.\n\n"));
83 * Enter interactive command handler allowing selection
84 * of individual files.
86 tree->node = (TREE_NODE *)tree->root;
87 tree_getpath(tree->node, cwd, sizeof(cwd));
88 bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
90 int found, len, stat, i;
91 if (!get_cmd(tree->ua, "$ ")) {
94 parse_ua_args(tree->ua);
95 if (tree->ua->argc == 0) {
99 len = strlen(tree->ua->argk[0]);
102 for (i=0; i<(int)comsize; i++) /* search for command */
103 if (strncasecmp(tree->ua->argk[0], _(commands[i].key), len) == 0) {
104 stat = (*commands[i].func)(tree->ua, tree); /* go execute command */
109 bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n"));
120 * This callback routine is responsible for inserting the
121 * items it gets into the directory tree. For each JobId selected
122 * this routine is called once for each file. We do not allow
123 * duplicate filenames, but instead keep the info from the most
124 * recent file entered (i.e. the JobIds are assumed to be sorted)
126 int insert_tree_handler(void *ctx, int num_fields, char **row)
128 TREE_CTX *tree = (TREE_CTX *)ctx;
130 TREE_NODE *node, *new_node;
133 strip_trailing_junk(row[1]);
135 if (*row[0] != '/') { /* Must be Win32 directory */
143 sprintf(fname, "%s%s", row[0], row[1]);
144 if (tree->avail_node) {
145 node = tree->avail_node;
147 node = new_tree_node(tree->root, type);
148 tree->avail_node = node;
150 Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname);
151 new_node = insert_tree_node(fname, node, tree->root, NULL);
152 /* Note, if node already exists, save new one for next time */
153 if (new_node != node) {
154 tree->avail_node = node;
156 tree->avail_node = NULL;
158 new_node->FileIndex = atoi(row[2]);
159 new_node->JobId = atoi(row[3]);
160 new_node->type = type;
161 new_node->extract = 1; /* extract all by default */
168 * Set extract to value passed. We recursively walk
169 * down the tree setting all children if the
170 * node is a directory.
172 static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value)
178 node->extract = value;
179 /* For a non-file (i.e. directory), we see all the children */
180 if (node->type != TN_FILE) {
181 for (n=node->child; n; n=n->sibling) {
182 set_extract(ua, n, tree, value);
186 /* Ordinary file, we get the full path, look up the
187 * attributes, decode them, and if we are hard linked to
188 * a file that was saved, we must load that file too.
190 tree_getpath(node, cwd, sizeof(cwd));
192 fdbr.JobId = node->JobId;
193 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
195 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
197 * If we point to a hard linked file, traverse the tree to
198 * find that file, and mark it for restoration as well. It
199 * must have the Link we just obtained and the same JobId.
202 for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
203 if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
213 static int markcmd(UAContext *ua, TREE_CTX *tree)
219 if (!tree->node->child) {
222 for (node = tree->node->child; node; node=node->sibling) {
223 if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
224 set_extract(ua, node, tree, 1);
230 static int countcmd(UAContext *ua, TREE_CTX *tree)
235 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
236 if (node->type != TN_NEWDIR) {
243 bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract);
247 static int findcmd(UAContext *ua, TREE_CTX *tree)
252 bsendmsg(ua, _("No file specification given.\n"));
256 for (int i=1; i < ua->argc; i++) {
257 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
258 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
259 tree_getpath(node, cwd, sizeof(cwd));
260 bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd);
269 static int lscmd(UAContext *ua, TREE_CTX *tree)
273 if (!tree->node->child) {
276 for (node = tree->node->child; node; node=node->sibling) {
277 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
278 bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
279 (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
285 extern char *getuser(uid_t uid);
286 extern char *getgroup(gid_t gid);
289 * This is actually the long form used for "dir"
291 static void ls_output(char *buf, char *fname, int extract, struct stat *statp)
297 p = encode_mode(statp->st_mode, buf);
298 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
300 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
302 n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
304 p = encode_time(statp->st_ctime, p);
318 * Like ls command, but give more detail on each file
320 static int dircmd(UAContext *ua, TREE_CTX *tree)
328 if (!tree->node->child) {
331 for (node = tree->node->child; node; node=node->sibling) {
332 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
333 tree_getpath(node, cwd, sizeof(cwd));
335 fdbr.JobId = node->JobId;
336 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
338 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
339 ls_output(buf, cwd, node->extract, &statp);
340 bsendmsg(ua, "%s\n", buf);
342 /* Something went wrong getting attributes -- print name */
343 bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
344 (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
352 static int helpcmd(UAContext *ua, TREE_CTX *tree)
357 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
358 for (i=0; i<comsize; i++) {
359 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
366 * Change directories. Note, if the user specifies x: and it fails,
367 * we assume it is a Win32 absolute cd rather than relative and
368 * try a second time with /x: ... Win32 kludge.
370 static int cdcmd(UAContext *ua, TREE_CTX *tree)
378 node = tree_cwd(ua->argk[1], tree->root, tree->node);
380 /* Try once more if Win32 drive -- make absolute */
381 if (ua->argk[1][1] == ':') { /* win32 drive */
383 strcat(cwd, ua->argk[1]);
384 node = tree_cwd(cwd, tree->root, tree->node);
387 bsendmsg(ua, _("Invalid path given.\n"));
394 tree_getpath(tree->node, cwd, sizeof(cwd));
395 bsendmsg(ua, _("cwd is: %s\n"), cwd);
399 static int pwdcmd(UAContext *ua, TREE_CTX *tree)
402 tree_getpath(tree->node, cwd, sizeof(cwd));
403 bsendmsg(ua, _("cwd is: %s\n"), cwd);
408 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
414 if (!tree->node->child) {
417 for (node = tree->node->child; node; node=node->sibling) {
418 if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
419 set_extract(ua, node, tree, 0);
425 static int quitcmd(UAContext *ua, TREE_CTX *tree)