]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_tree.c
Separate tree and bsr code from ua_restore.c
[bacula/bacula] / bacula / src / dird / ua_tree.c
1 /*
2  *
3  *   Bacula Director -- User Agent Database File tree for Restore
4  *      command.
5  *
6  *     Kern Sibbald, July MMII
7  *
8  *   Version $Id$
9  */
10
11 /*
12    Copyright (C) 2002-2003 Kern Sibbald and John Walker
13
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.
18
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.
23
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,
27    MA 02111-1307, USA.
28
29  */
30
31 #include "bacula.h"
32 #include "dird.h"
33 #include <fnmatch.h>
34 #include "findlib/find.h"
35
36
37 /* Forward referenced commands */
38
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);
49
50
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")},    
65              };
66 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
67
68
69 /*
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", ...
73  */
74 void user_select_files_from_tree(TREE_CTX *tree)
75 {
76    char cwd[2000];
77
78    bsendmsg(tree->ua, _( 
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"));
82    /*
83     * Enter interactive command handler allowing selection
84     *  of individual files.
85     */
86    tree->node = (TREE_NODE *)tree->root;
87    tree_getpath(tree->node, cwd, sizeof(cwd));
88    bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
89    for ( ;; ) {       
90       int found, len, stat, i;
91       if (!get_cmd(tree->ua, "$ ")) {
92          break;
93       }
94       parse_ua_args(tree->ua);
95       if (tree->ua->argc == 0) {
96          return;
97       }
98
99       len = strlen(tree->ua->argk[0]);
100       found = 0;
101       stat = 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 */
105             found = 1;
106             break;
107          }
108       if (!found) {
109          bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n"));
110          continue;
111       }
112       if (!stat) {
113          break;
114       }
115    }
116 }
117
118
119 /*
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)
125  */
126 int insert_tree_handler(void *ctx, int num_fields, char **row)
127 {
128    TREE_CTX *tree = (TREE_CTX *)ctx;
129    char fname[2000];
130    TREE_NODE *node, *new_node;
131    int type;
132
133    strip_trailing_junk(row[1]);
134    if (*row[1] == 0) {
135       if (*row[0] != '/') {           /* Must be Win32 directory */
136          type = TN_DIR_NLS;
137       } else {
138          type = TN_DIR;
139       }
140    } else {
141       type = TN_FILE;
142    }
143    sprintf(fname, "%s%s", row[0], row[1]);
144    if (tree->avail_node) {
145       node = tree->avail_node;
146    } else {
147       node = new_tree_node(tree->root, type);
148       tree->avail_node = node;
149    }
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;
155    } else {
156       tree->avail_node = NULL;
157    }
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 */
162    tree->cnt++;
163    return 0;
164 }
165
166
167 /*
168  * Set extract to value passed. We recursively walk
169  *  down the tree setting all children if the 
170  *  node is a directory.
171  */
172 static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value)
173 {
174    TREE_NODE *n;
175    FILE_DBR fdbr;
176    struct stat statp;
177
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);
183       }
184    } else if (value) {
185       char cwd[2000];
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.
189        */
190       tree_getpath(node, cwd, sizeof(cwd));
191       fdbr.FileId = 0;
192       fdbr.JobId = node->JobId;
193       if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
194          uint32_t LinkFI;
195          decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
196          /*
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.
200           */
201          if (LinkFI) {
202             for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
203                if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
204                   n->extract = 1;
205                   break;
206                }
207             }
208          }
209       }
210    }
211 }
212
213 static int markcmd(UAContext *ua, TREE_CTX *tree)
214 {
215    TREE_NODE *node;
216
217    if (ua->argc < 2)
218       return 1;
219    if (!tree->node->child) {     
220       return 1;
221    }
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);
225       }
226    }
227    return 1;
228 }
229
230 static int countcmd(UAContext *ua, TREE_CTX *tree)
231 {
232    int total, extract;
233
234    total = extract = 0;
235    for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
236       if (node->type != TN_NEWDIR) {
237          total++;
238          if (node->extract) {
239             extract++;
240          }
241       }
242    }
243    bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract);
244    return 1;
245 }
246
247 static int findcmd(UAContext *ua, TREE_CTX *tree)
248 {
249    char cwd[2000];
250
251    if (ua->argc == 1) {
252       bsendmsg(ua, _("No file specification given.\n"));
253       return 0;
254    }
255    
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);
261          }
262       }
263    }
264    return 1;
265 }
266
267
268
269 static int lscmd(UAContext *ua, TREE_CTX *tree)
270 {
271    TREE_NODE *node;
272
273    if (!tree->node->child) {     
274       return 1;
275    }
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)?"/":"");
280       }
281    }
282    return 1;
283 }
284
285 extern char *getuser(uid_t uid);
286 extern char *getgroup(gid_t gid);
287
288 /*
289  * This is actually the long form used for "dir"
290  */
291 static void ls_output(char *buf, char *fname, int extract, struct stat *statp)
292 {
293    char *p, *f;
294    char ec1[30];
295    int n;
296
297    p = encode_mode(statp->st_mode, buf);
298    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
299    p += n;
300    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
301    p += n;
302    n = sprintf(p, "%8.8s  ", edit_uint64(statp->st_size, ec1));
303    p += n;
304    p = encode_time(statp->st_ctime, p);
305    *p++ = ' ';
306    if (extract) {
307       *p++ = '*';
308    } else {
309       *p++ = ' ';
310    }
311    for (f=fname; *f; )
312       *p++ = *f++;
313    *p = 0;
314 }
315
316
317 /*
318  * Like ls command, but give more detail on each file
319  */
320 static int dircmd(UAContext *ua, TREE_CTX *tree)
321 {
322    TREE_NODE *node;
323    FILE_DBR fdbr;
324    struct stat statp;
325    char buf[1000];
326    char cwd[1100];
327
328    if (!tree->node->child) {     
329       return 1;
330    }
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));
334          fdbr.FileId = 0;
335          fdbr.JobId = node->JobId;
336          if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, &fdbr)) {
337             uint32_t LinkFI;
338             decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
339             ls_output(buf, cwd, node->extract, &statp);
340             bsendmsg(ua, "%s\n", buf);
341          } else {
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)?"/":"");
345          }
346       }
347    }
348    return 1;
349 }
350
351
352 static int helpcmd(UAContext *ua, TREE_CTX *tree) 
353 {
354    unsigned int i;
355
356 /* usage(); */
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));
360    }
361    bsendmsg(ua, "\n");
362    return 1;
363 }
364
365 /*
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.
369  */
370 static int cdcmd(UAContext *ua, TREE_CTX *tree) 
371 {
372    TREE_NODE *node;
373    char cwd[2000];
374
375    if (ua->argc != 2) {
376       return 1;
377    }
378    node = tree_cwd(ua->argk[1], tree->root, tree->node);
379    if (!node) {
380       /* Try once more if Win32 drive -- make absolute */
381       if (ua->argk[1][1] == ':') {  /* win32 drive */
382          strcpy(cwd, "/");
383          strcat(cwd, ua->argk[1]);
384          node = tree_cwd(cwd, tree->root, tree->node);
385       }
386       if (!node) {
387          bsendmsg(ua, _("Invalid path given.\n"));
388       } else {
389          tree->node = node;
390       }
391    } else {
392       tree->node = node;
393    }
394    tree_getpath(tree->node, cwd, sizeof(cwd));
395    bsendmsg(ua, _("cwd is: %s\n"), cwd);
396    return 1;
397 }
398
399 static int pwdcmd(UAContext *ua, TREE_CTX *tree) 
400 {
401    char cwd[2000];
402    tree_getpath(tree->node, cwd, sizeof(cwd));
403    bsendmsg(ua, _("cwd is: %s\n"), cwd);
404    return 1;
405 }
406
407
408 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
409 {
410    TREE_NODE *node;
411
412    if (ua->argc < 2)
413       return 1;
414    if (!tree->node->child) {     
415       return 1;
416    }
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);
420       }
421    }
422    return 1;
423 }
424
425 static int quitcmd(UAContext *ua, TREE_CTX *tree) 
426 {
427    return 0;
428 }