3 * Bacula Director -- User Agent Database File tree for Restore
4 * command. This file interacts with the user implementing the
7 * Kern Sibbald, July MMII
13 Copyright (C) 2002-2003 Kern Sibbald and John Walker
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation; either version 2 of
18 the License, or (at your option) any later version.
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
25 You should have received a copy of the GNU General Public
26 License along with this program; if not, write to the Free
27 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
35 #include "findlib/find.h"
38 /* Forward referenced commands */
40 static int markcmd(UAContext *ua, TREE_CTX *tree);
41 static int countcmd(UAContext *ua, TREE_CTX *tree);
42 static int findcmd(UAContext *ua, TREE_CTX *tree);
43 static int lscmd(UAContext *ua, TREE_CTX *tree);
44 static int lsmark(UAContext *ua, TREE_CTX *tree);
45 static int dircmd(UAContext *ua, TREE_CTX *tree);
46 static int estimatecmd(UAContext *ua, TREE_CTX *tree);
47 static int helpcmd(UAContext *ua, TREE_CTX *tree);
48 static int cdcmd(UAContext *ua, TREE_CTX *tree);
49 static int pwdcmd(UAContext *ua, TREE_CTX *tree);
50 static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
51 static int quitcmd(UAContext *ua, TREE_CTX *tree);
54 struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; };
55 static struct cmdstruct commands[] = {
56 { N_("cd"), cdcmd, _("change current directory")},
57 { N_("count"), countcmd, _("count marked files")},
58 { N_("dir"), dircmd, _("list current directory")},
59 { N_("done"), quitcmd, _("leave file selection mode")},
60 { N_("estimate"), estimatecmd, _("estimate restore size")},
61 { N_("exit"), quitcmd, _("exit = done")},
62 { N_("find"), findcmd, _("find files")},
63 { N_("help"), helpcmd, _("print help")},
64 { N_("lsmark"), lsmark, _("list the marked files")},
65 { N_("ls"), lscmd, _("list current directory")},
66 { N_("mark"), markcmd, _("mark file to be restored")},
67 { N_("pwd"), pwdcmd, _("print current working directory")},
68 { N_("unmark"), unmarkcmd, _("unmark file to be restored")},
69 { N_("?"), helpcmd, _("print help")},
71 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
75 * Enter a prompt mode where the user can select/deselect
76 * files to be restored. This is sort of like a mini-shell
77 * that allows "cd", "pwd", "add", "rm", ...
79 void user_select_files_from_tree(TREE_CTX *tree)
82 /* Get a new context so we don't destroy restore command args */
83 UAContext *ua = new_ua_context(tree->ua->jcr);
84 ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */
87 "\nYou are now entering file selection mode where you add and\n"
88 "remove files to be restored. All files are initially added.\n"
89 "Enter \"done\" to leave this mode.\n\n"));
91 * Enter interactive command handler allowing selection
92 * of individual files.
94 tree->node = (TREE_NODE *)tree->root;
95 tree_getpath(tree->node, cwd, sizeof(cwd));
96 bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
98 int found, len, stat, i;
99 if (!get_cmd(ua, "$ ")) {
107 len = strlen(ua->argk[0]);
110 for (i=0; i<(int)comsize; i++) /* search for command */
111 if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
112 stat = (*commands[i].func)(ua, tree); /* go execute command */
117 bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n"));
124 ua->UA_sock = NULL; /* don't release restore socket */
125 free_ua_context(ua); /* get rid of temp UA context */
130 * This callback routine is responsible for inserting the
131 * items it gets into the directory tree. For each JobId selected
132 * this routine is called once for each file. We do not allow
133 * duplicate filenames, but instead keep the info from the most
134 * recent file entered (i.e. the JobIds are assumed to be sorted)
136 int insert_tree_handler(void *ctx, int num_fields, char **row)
138 TREE_CTX *tree = (TREE_CTX *)ctx;
140 TREE_NODE *node, *new_node;
143 strip_trailing_junk(row[1]);
144 if (*row[1] == 0) { /* no filename => directory */
145 if (*row[0] != '/') { /* Must be Win32 directory */
153 bsnprintf(fname, sizeof(fname), "%s%s", row[0], row[1]);
154 if (tree->avail_node) {
155 node = tree->avail_node;
157 node = new_tree_node(tree->root, type);
158 tree->avail_node = node;
160 Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname);
161 new_node = insert_tree_node(fname, node, tree->root, NULL);
162 /* Note, if node already exists, save new one for next time */
163 if (new_node != node) {
164 tree->avail_node = node;
166 tree->avail_node = NULL;
168 new_node->FileIndex = atoi(row[2]);
169 new_node->JobId = atoi(row[3]);
170 new_node->type = type;
171 new_node->extract = true; /* extract all by default */
178 * Set extract to value passed. We recursively walk
179 * down the tree setting all children if the
180 * node is a directory.
182 static int set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, bool extract)
189 node->extract = extract;
190 if (node->type != TN_NEWDIR) {
193 /* For a non-file (i.e. directory), we see all the children */
194 if (node->type != TN_FILE) {
195 for (n=node->child; n; n=n->sibling) {
196 count += set_extract(ua, n, tree, extract);
198 } else if (extract) {
200 /* Ordinary file, we get the full path, look up the
201 * attributes, decode them, and if we are hard linked to
202 * a file that was saved, we must load that file too.
204 tree_getpath(node, cwd, sizeof(cwd));
206 fdbr.JobId = node->JobId;
207 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
209 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
211 * If we point to a hard linked file, traverse the tree to
212 * find that file, and mark it to be restored as well. It
213 * must have the Link we just obtained and the same JobId.
216 for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
217 if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
228 static int markcmd(UAContext *ua, TREE_CTX *tree)
233 if (ua->argc < 2 || !tree->node->child) {
234 bsendmsg(ua, _("No files marked.\n"));
237 for (node = tree->node->child; node; node=node->sibling) {
238 if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
239 count += set_extract(ua, node, tree, true);
243 bsendmsg(ua, _("No files marked.\n"));
245 bsendmsg(ua, _("%d file%s marked.\n"), count, count==0?"":"s");
250 static int countcmd(UAContext *ua, TREE_CTX *tree)
252 int total, num_extract;
254 total = num_extract = 0;
255 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
256 if (node->type != TN_NEWDIR) {
263 bsendmsg(ua, "%d total files. %d marked to be restored.\n", total, num_extract);
267 static int findcmd(UAContext *ua, TREE_CTX *tree)
272 bsendmsg(ua, _("No file specification given.\n"));
276 for (int i=1; i < ua->argc; i++) {
277 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
278 if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
279 tree_getpath(node, cwd, sizeof(cwd));
280 bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd);
289 static int lscmd(UAContext *ua, TREE_CTX *tree)
293 if (!tree->node->child) {
296 for (node = tree->node->child; node; node=node->sibling) {
297 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
298 bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
299 (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
306 * Ls command that lists only the marked files
308 static int lsmark(UAContext *ua, TREE_CTX *tree)
312 if (!tree->node->child) {
315 for (node = tree->node->child; node; node=node->sibling) {
316 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0 &&
318 bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
319 (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
326 extern char *getuser(uid_t uid);
327 extern char *getgroup(gid_t gid);
330 * This is actually the long form used for "dir"
332 static void ls_output(char *buf, char *fname, bool extract, struct stat *statp)
338 p = encode_mode(statp->st_mode, buf);
339 n = sprintf(p, " %2d ", (uint32_t)statp->st_nlink);
341 n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
343 n = sprintf(p, "%8.8s ", edit_uint64(statp->st_size, ec1));
345 p = encode_time(statp->st_ctime, p);
359 * Like ls command, but give more detail on each file
361 static int dircmd(UAContext *ua, TREE_CTX *tree)
369 if (!tree->node->child) {
372 for (node = tree->node->child; node; node=node->sibling) {
373 if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
374 tree_getpath(node, cwd, sizeof(cwd));
376 fdbr.JobId = node->JobId;
377 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
379 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
380 ls_output(buf, cwd, node->extract, &statp);
381 bsendmsg(ua, "%s\n", buf);
383 /* Something went wrong getting attributes -- print name */
384 bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
385 (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
393 static int estimatecmd(UAContext *ua, TREE_CTX *tree)
395 int total, num_extract;
396 uint64_t total_bytes = 0;
402 total = num_extract = 0;
403 for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
404 if (node->type != TN_NEWDIR) {
406 /* If regular file, get size */
407 if (node->extract && node->type == TN_FILE) {
409 tree_getpath(node, cwd, sizeof(cwd));
411 fdbr.JobId = node->JobId;
412 if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
414 decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
415 if (S_ISREG(statp.st_mode) && statp.st_size > 0) {
416 total_bytes += statp.st_size;
419 /* Directory, count only */
420 } else if (node->extract) {
425 bsendmsg(ua, "%d total files; %d marked to be restored; %s bytes.\n",
426 total, num_extract, edit_uint64_with_commas(total_bytes, ec1));
432 static int helpcmd(UAContext *ua, TREE_CTX *tree)
437 bsendmsg(ua, _(" Command Description\n ======= ===========\n"));
438 for (i=0; i<comsize; i++) {
439 bsendmsg(ua, _(" %-10s %s\n"), _(commands[i].key), _(commands[i].help));
446 * Change directories. Note, if the user specifies x: and it fails,
447 * we assume it is a Win32 absolute cd rather than relative and
448 * try a second time with /x: ... Win32 kludge.
450 static int cdcmd(UAContext *ua, TREE_CTX *tree)
458 node = tree_cwd(ua->argk[1], tree->root, tree->node);
460 /* Try once more if Win32 drive -- make absolute */
461 if (ua->argk[1][1] == ':') { /* win32 drive */
462 bstrncpy(cwd, "/", sizeof(cwd));
463 bstrncat(cwd, ua->argk[1], sizeof(cwd));
464 node = tree_cwd(cwd, tree->root, tree->node);
467 bsendmsg(ua, _("Invalid path given.\n"));
474 tree_getpath(tree->node, cwd, sizeof(cwd));
475 bsendmsg(ua, _("cwd is: %s\n"), cwd);
479 static int pwdcmd(UAContext *ua, TREE_CTX *tree)
482 tree_getpath(tree->node, cwd, sizeof(cwd));
483 bsendmsg(ua, _("cwd is: %s\n"), cwd);
488 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
493 if (ua->argc < 2 || !tree->node->child) {
494 bsendmsg(ua, _("No files unmarked.\n"));
497 for (node = tree->node->child; node; node=node->sibling) {
498 if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
499 count += set_extract(ua, node, tree, false);
503 bsendmsg(ua, _("No files unmarked.\n"));
505 bsendmsg(ua, _("%d file%s unmarked.\n"), count, count==0?"":"s");
510 static int quitcmd(UAContext *ua, TREE_CTX *tree)