]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/ua_tree.c
Commit final code for 1.32b + Verify Job (name) + change Pool for a Volume
[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    /* Get a new context so we don't destroy restore command args */
78    UAContext *ua = new_ua_context(tree->ua->jcr);
79    ua->UA_sock = tree->ua->UA_sock;   /* patch in UA socket */
80
81    bsendmsg(tree->ua, _( 
82       "\nYou are now entering file selection mode where you add and\n"
83       "remove files to be restored. All files are initially added.\n"
84       "Enter \"done\" to leave this mode.\n\n"));
85    /*
86     * Enter interactive command handler allowing selection
87     *  of individual files.
88     */
89    tree->node = (TREE_NODE *)tree->root;
90    tree_getpath(tree->node, cwd, sizeof(cwd));
91    bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
92    for ( ;; ) {       
93       int found, len, stat, i;
94       if (!get_cmd(ua, "$ ")) {
95          break;
96       }
97       parse_ua_args(ua);
98       if (ua->argc == 0) {
99          break;
100       }
101
102       len = strlen(ua->argk[0]);
103       found = 0;
104       stat = 0;
105       for (i=0; i<(int)comsize; i++)       /* search for command */
106          if (strncasecmp(ua->argk[0],  _(commands[i].key), len) == 0) {
107             stat = (*commands[i].func)(ua, tree);   /* go execute command */
108             found = 1;
109             break;
110          }
111       if (!found) {
112          bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to exit.\n"));
113          continue;
114       }
115       if (!stat) {
116          break;
117       }
118    }
119    ua->UA_sock = NULL;                /* don't release restore socket */
120    free_ua_context(ua);               /* get rid of temp UA context */
121 }
122
123
124 /*
125  * This callback routine is responsible for inserting the
126  *  items it gets into the directory tree. For each JobId selected
127  *  this routine is called once for each file. We do not allow
128  *  duplicate filenames, but instead keep the info from the most
129  *  recent file entered (i.e. the JobIds are assumed to be sorted)
130  */
131 int insert_tree_handler(void *ctx, int num_fields, char **row)
132 {
133    TREE_CTX *tree = (TREE_CTX *)ctx;
134    char fname[5000];
135    TREE_NODE *node, *new_node;
136    int type;
137
138    strip_trailing_junk(row[1]);
139    if (*row[1] == 0) {                /* no filename => directory */
140       if (*row[0] != '/') {           /* Must be Win32 directory */
141          type = TN_DIR_NLS;
142       } else {
143          type = TN_DIR;
144       }
145    } else {
146       type = TN_FILE;
147    }
148    bsnprintf(fname, sizeof(fname), "%s%s", row[0], row[1]);
149    if (tree->avail_node) {
150       node = tree->avail_node;
151    } else {
152       node = new_tree_node(tree->root, type);
153       tree->avail_node = node;
154    }
155    Dmsg3(200, "FI=%d type=%d fname=%s\n", node->FileIndex, type, fname);
156    new_node = insert_tree_node(fname, node, tree->root, NULL);
157    /* Note, if node already exists, save new one for next time */
158    if (new_node != node) {
159       tree->avail_node = node;
160    } else {
161       tree->avail_node = NULL;
162    }
163    new_node->FileIndex = atoi(row[2]);
164    new_node->JobId = atoi(row[3]);
165    new_node->type = type;
166    new_node->extract = 1;             /* extract all by default */
167    tree->cnt++;
168    return 0;
169 }
170
171
172 /*
173  * Set extract to value passed. We recursively walk
174  *  down the tree setting all children if the 
175  *  node is a directory.
176  */
177 static void set_extract(UAContext *ua, TREE_NODE *node, TREE_CTX *tree, int value)
178 {
179    TREE_NODE *n;
180    FILE_DBR fdbr;
181    struct stat statp;
182
183    node->extract = value;
184    /* For a non-file (i.e. directory), we see all the children */
185    if (node->type != TN_FILE) {
186       for (n=node->child; n; n=n->sibling) {
187          set_extract(ua, n, tree, value);
188       }
189    } else if (value) {
190       char cwd[2000];
191       /* Ordinary file, we get the full path, look up the
192        * attributes, decode them, and if we are hard linked to
193        * a file that was saved, we must load that file too.
194        */
195       tree_getpath(node, cwd, sizeof(cwd));
196       fdbr.FileId = 0;
197       fdbr.JobId = node->JobId;
198       if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
199          int32_t LinkFI;
200          decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
201          /*
202           * If we point to a hard linked file, traverse the tree to
203           * find that file, and mark it for restoration as well. It
204           * must have the Link we just obtained and the same JobId.
205           */
206          if (LinkFI) {
207             for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
208                if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
209                   n->extract = 1;
210                   break;
211                }
212             }
213          }
214       }
215    }
216 }
217
218 static int markcmd(UAContext *ua, TREE_CTX *tree)
219 {
220    TREE_NODE *node;
221
222    if (ua->argc < 2)
223       return 1;
224    if (!tree->node->child) {     
225       return 1;
226    }
227    for (node = tree->node->child; node; node=node->sibling) {
228       if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
229          set_extract(ua, node, tree, 1);
230       }
231    }
232    return 1;
233 }
234
235 static int countcmd(UAContext *ua, TREE_CTX *tree)
236 {
237    int total, extract;
238
239    total = extract = 0;
240    for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
241       if (node->type != TN_NEWDIR) {
242          total++;
243          if (node->extract) {
244             extract++;
245          }
246       }
247    }
248    bsendmsg(ua, "%d total files. %d marked for restoration.\n", total, extract);
249    return 1;
250 }
251
252 static int findcmd(UAContext *ua, TREE_CTX *tree)
253 {
254    char cwd[2000];
255
256    if (ua->argc == 1) {
257       bsendmsg(ua, _("No file specification given.\n"));
258       return 0;
259    }
260    
261    for (int i=1; i < ua->argc; i++) {
262       for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
263          if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
264             tree_getpath(node, cwd, sizeof(cwd));
265             bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd);
266          }
267       }
268    }
269    return 1;
270 }
271
272
273
274 static int lscmd(UAContext *ua, TREE_CTX *tree)
275 {
276    TREE_NODE *node;
277
278    if (!tree->node->child) {     
279       return 1;
280    }
281    for (node = tree->node->child; node; node=node->sibling) {
282       if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
283          bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
284             (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
285       }
286    }
287    return 1;
288 }
289
290 extern char *getuser(uid_t uid);
291 extern char *getgroup(gid_t gid);
292
293 /*
294  * This is actually the long form used for "dir"
295  */
296 static void ls_output(char *buf, char *fname, int extract, struct stat *statp)
297 {
298    char *p, *f;
299    char ec1[30];
300    int n;
301
302    p = encode_mode(statp->st_mode, buf);
303    n = sprintf(p, "  %2d ", (uint32_t)statp->st_nlink);
304    p += n;
305    n = sprintf(p, "%-8.8s %-8.8s", getuser(statp->st_uid), getgroup(statp->st_gid));
306    p += n;
307    n = sprintf(p, "%8.8s  ", edit_uint64(statp->st_size, ec1));
308    p += n;
309    p = encode_time(statp->st_ctime, p);
310    *p++ = ' ';
311    if (extract) {
312       *p++ = '*';
313    } else {
314       *p++ = ' ';
315    }
316    for (f=fname; *f; )
317       *p++ = *f++;
318    *p = 0;
319 }
320
321
322 /*
323  * Like ls command, but give more detail on each file
324  */
325 static int dircmd(UAContext *ua, TREE_CTX *tree)
326 {
327    TREE_NODE *node;
328    FILE_DBR fdbr;
329    struct stat statp;
330    char buf[1000];
331    char cwd[1100];
332
333    if (!tree->node->child) {     
334       return 1;
335    }
336    for (node = tree->node->child; node; node=node->sibling) {
337       if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
338          tree_getpath(node, cwd, sizeof(cwd));
339          fdbr.FileId = 0;
340          fdbr.JobId = node->JobId;
341          if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
342             int32_t LinkFI;
343             decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
344             ls_output(buf, cwd, node->extract, &statp);
345             bsendmsg(ua, "%s\n", buf);
346          } else {
347             /* Something went wrong getting attributes -- print name */
348             bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
349                (node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
350          }
351       }
352    }
353    return 1;
354 }
355
356
357 static int helpcmd(UAContext *ua, TREE_CTX *tree) 
358 {
359    unsigned int i;
360
361 /* usage(); */
362    bsendmsg(ua, _("  Command    Description\n  =======    ===========\n"));
363    for (i=0; i<comsize; i++) {
364       bsendmsg(ua, _("  %-10s %s\n"), _(commands[i].key), _(commands[i].help));
365    }
366    bsendmsg(ua, "\n");
367    return 1;
368 }
369
370 /*
371  * Change directories.  Note, if the user specifies x: and it fails,
372  *   we assume it is a Win32 absolute cd rather than relative and
373  *   try a second time with /x: ...  Win32 kludge.
374  */
375 static int cdcmd(UAContext *ua, TREE_CTX *tree) 
376 {
377    TREE_NODE *node;
378    char cwd[2000];
379
380    if (ua->argc != 2) {
381       return 1;
382    }
383    node = tree_cwd(ua->argk[1], tree->root, tree->node);
384    if (!node) {
385       /* Try once more if Win32 drive -- make absolute */
386       if (ua->argk[1][1] == ':') {  /* win32 drive */
387          bstrncpy(cwd, "/", sizeof(cwd));
388          bstrncat(cwd, ua->argk[1], sizeof(cwd));
389          node = tree_cwd(cwd, tree->root, tree->node);
390       }
391       if (!node) {
392          bsendmsg(ua, _("Invalid path given.\n"));
393       } else {
394          tree->node = node;
395       }
396    } else {
397       tree->node = node;
398    }
399    tree_getpath(tree->node, cwd, sizeof(cwd));
400    bsendmsg(ua, _("cwd is: %s\n"), cwd);
401    return 1;
402 }
403
404 static int pwdcmd(UAContext *ua, TREE_CTX *tree) 
405 {
406    char cwd[2000];
407    tree_getpath(tree->node, cwd, sizeof(cwd));
408    bsendmsg(ua, _("cwd is: %s\n"), cwd);
409    return 1;
410 }
411
412
413 static int unmarkcmd(UAContext *ua, TREE_CTX *tree)
414 {
415    TREE_NODE *node;
416
417    if (ua->argc < 2)
418       return 1;
419    if (!tree->node->child) {     
420       return 1;
421    }
422    for (node = tree->node->child; node; node=node->sibling) {
423       if (fnmatch(ua->argk[1], node->fname, 0) == 0) {
424          set_extract(ua, node, tree, 0);
425       }
426    }
427    return 1;
428 }
429
430 static int quitcmd(UAContext *ua, TREE_CTX *tree) 
431 {
432    return 0;
433 }