]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/testfind.c
More plugin implementation
[bacula/bacula] / bacula / src / tools / testfind.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2008 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  * Test program for find files
30  *
31  *  Kern Sibbald, MM
32  *
33  */
34
35 #include "bacula.h"
36 #include "dird/dird.h"
37 #include "findlib/find.h"
38
39 #if defined(HAVE_WIN32)
40 #define isatty(fd) (fd==0)
41 #endif
42
43 /* Dummy functions */
44 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
45 int generate_job_event(JCR *jcr, const char *event) { return 1; }
46 void generate_plugin_event(JCR *jcr, bEventType eventType, void *value) { }
47
48 /* Global variables */
49 static int num_files = 0;
50 static int max_file_len = 0;
51 static int max_path_len = 0;
52 static int trunc_fname = 0;
53 static int trunc_path = 0;
54 static int attrs = 0;
55
56 static JCR *jcr;
57
58 static int print_file(FF_PKT *ff, void *pkt, bool);
59 static void count_files(FF_PKT *ff);
60 static bool copy_fileset(FF_PKT *ff, JCR *jcr);
61 static void set_options(findFOPTS *fo, const char *opts);
62
63 static void usage()
64 {
65    fprintf(stderr, _(
66 "\n"
67 "Usage: testfind [-d debug_level] [-] [pattern1 ...]\n"
68 "       -a          print extended attributes (Win32 debug)\n"
69 "       -d <nn>     set debug level to <nn>\n"
70 "       -dt         print timestamp in debug output\n"
71 "       -c          specify config file containing FileSet resources\n"
72 "       -f          specify which FileSet to use\n"
73 "       -?          print this message.\n"
74 "\n"
75 "Patterns are used for file inclusion -- normally directories.\n"
76 "Debug level >= 1 prints each file found.\n"
77 "Debug level >= 10 prints path/file for catalog.\n"
78 "Errors are always printed.\n"
79 "Files/paths truncated is the number of files/paths with len > 255.\n"
80 "Truncation is only in the catalog.\n"
81 "\n"));
82
83    exit(1);
84 }
85
86
87 int
88 main (int argc, char *const *argv)
89 {
90    FF_PKT *ff;
91    const char *configfile = "bacula-dir.conf";
92    const char *fileset_name = "Windows-Full-Set";
93    int ch, hard_links;
94
95    OSDependentInit();
96
97    setlocale(LC_ALL, "");
98    bindtextdomain("bacula", LOCALEDIR);
99    textdomain("bacula");
100
101    while ((ch = getopt(argc, argv, "ac:d:f:?")) != -1) {
102       switch (ch) {
103          case 'a':                    /* print extended attributes *debug* */
104             attrs = 1;
105             break;
106
107          case 'c':                    /* set debug level */
108             configfile = optarg;
109             break;
110
111          case 'd':                    /* set debug level */
112          if (*optarg == 't') {
113             dbg_timestamp = true;
114          } else {
115             debug_level = atoi(optarg);
116             if (debug_level <= 0) {
117                debug_level = 1;
118             }
119          }
120             break;
121
122          case 'f':                    /* exclude patterns */
123             fileset_name = optarg;
124             break;
125
126          case '?':
127          default:
128             usage();
129
130       }
131    }
132
133    argc -= optind;
134    argv += optind;
135
136    parse_config(configfile);
137
138    MSGS *msg;
139
140    foreach_res(msg, R_MSGS)
141    {
142       init_msg(NULL, msg);
143    }
144
145    jcr = new_jcr(sizeof(JCR), NULL);
146    jcr->fileset = (FILESET *)GetResWithName(R_FILESET, fileset_name);
147
148    if (jcr->fileset == NULL) {
149       fprintf(stderr, "%s: Fileset not found\n", fileset_name);
150
151       FILESET *var;
152
153       fprintf(stderr, "Valid FileSets:\n");
154       
155       foreach_res(var, R_FILESET) {
156          fprintf(stderr, "    %s\n", var->hdr.name);
157       }
158
159       exit(1);
160    }
161
162    ff = init_find_files();
163    
164    copy_fileset(ff, jcr);
165
166    find_files(jcr, ff, print_file, NULL);
167
168    free_jcr(jcr);
169    free_config_resources();
170    term_last_jobs_list();
171
172    /* Clean up fileset */
173    findFILESET *fileset = ff->fileset;
174
175    if (fileset) {
176       int i, j, k;
177       /* Delete FileSet Include lists */
178       for (i=0; i<fileset->include_list.size(); i++) {
179          findINCEXE *incexe = (findINCEXE *)fileset->include_list.get(i);
180          for (j=0; j<incexe->opts_list.size(); j++) {
181             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
182             for (k=0; k<fo->regex.size(); k++) {
183                regfree((regex_t *)fo->regex.get(k));
184             }
185             fo->regex.destroy();
186             fo->regexdir.destroy();
187             fo->regexfile.destroy();
188             fo->wild.destroy();
189             fo->wilddir.destroy();
190             fo->wildfile.destroy();
191             fo->wildbase.destroy();
192             fo->fstype.destroy();
193             fo->drivetype.destroy();
194          }
195          incexe->opts_list.destroy();
196          incexe->name_list.destroy();
197       }
198       fileset->include_list.destroy();
199
200       /* Delete FileSet Exclude lists */
201       for (i=0; i<fileset->exclude_list.size(); i++) {
202          findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
203          for (j=0; j<incexe->opts_list.size(); j++) {
204             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
205             fo->regex.destroy();
206             fo->regexdir.destroy();
207             fo->regexfile.destroy();
208             fo->wild.destroy();
209             fo->wilddir.destroy();
210             fo->wildfile.destroy();
211             fo->wildbase.destroy();
212             fo->fstype.destroy();
213             fo->drivetype.destroy();
214          }
215          incexe->opts_list.destroy();
216          incexe->name_list.destroy();
217       }
218       fileset->exclude_list.destroy();
219       free(fileset);
220    }
221    ff->fileset = NULL;
222    hard_links = term_find_files(ff);
223
224    printf(_("\n"
225 "Total files    : %d\n"
226 "Max file length: %d\n"
227 "Max path length: %d\n"
228 "Files truncated: %d\n"
229 "Paths truncated: %d\n"
230 "Hard links     : %d\n"),
231      num_files, max_file_len, max_path_len,
232      trunc_fname, trunc_path, hard_links);
233
234    term_msg();
235
236    close_memory_pool();
237    sm_dump(false);
238    exit(0);
239 }
240
241 static int print_file(FF_PKT *ff, void *pkt, bool top_level) 
242 {
243
244    switch (ff->type) {
245    case FT_LNKSAVED:
246       if (debug_level == 1) {
247          printf("%s\n", ff->fname);
248       } else if (debug_level > 1) {
249          printf("Lnka: %s -> %s\n", ff->fname, ff->link);
250       }
251       break;
252    case FT_REGE:
253       if (debug_level == 1) {
254          printf("%s\n", ff->fname);
255       } else if (debug_level > 1) {
256          printf("Empty: %s\n", ff->fname);
257       }
258       count_files(ff);
259       break;
260    case FT_REG:
261       if (debug_level == 1) {
262          printf("%s\n", ff->fname);
263       } else if (debug_level > 1) {
264          printf(_("Reg: %s\n"), ff->fname);
265       }
266       count_files(ff);
267       break;
268    case FT_LNK:
269       if (debug_level == 1) {
270          printf("%s\n", ff->fname);
271       } else if (debug_level > 1) {
272          printf("Lnk: %s -> %s\n", ff->fname, ff->link);
273       }
274       count_files(ff);
275       break;
276    case FT_DIRBEGIN:
277       return 1;
278    case FT_NORECURSE:
279    case FT_NOFSCHG:
280    case FT_INVALIDFS:
281    case FT_INVALIDDT:
282    case FT_DIREND:
283       if (debug_level) {
284          char errmsg[100] = "";
285          if (ff->type == FT_NORECURSE) {
286             bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
287          } else if (ff->type == FT_NOFSCHG) {
288             bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
289          } else if (ff->type == FT_INVALIDFS) {
290             bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
291          } else if (ff->type == FT_INVALIDDT) {
292             bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
293          }
294          printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
295       }
296       ff->type = FT_DIREND;
297       count_files(ff);
298       break;
299    case FT_SPEC:
300       if (debug_level == 1) {
301          printf("%s\n", ff->fname);
302       } else if (debug_level > 1) {
303          printf("Spec: %s\n", ff->fname);
304       }
305       count_files(ff);
306       break;
307    case FT_NOACCESS:
308       printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
309       break;
310    case FT_NOFOLLOW:
311       printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
312       break;
313    case FT_NOSTAT:
314       printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
315       break;
316    case FT_NOCHG:
317       printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
318       break;
319    case FT_ISARCH:
320       printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
321       break;
322    case FT_NOOPEN:
323       printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
324       break;
325    default:
326       printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
327       break;
328    }
329    if (attrs) {
330       char attr[200];
331       encode_attribsEx(NULL, attr, ff);
332       if (*attr != 0) {
333          printf("AttrEx=%s\n", attr);
334       }
335 //    set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
336    }
337    return 1;
338 }
339
340 static void count_files(FF_PKT *ar)
341 {
342    int fnl, pnl;
343    char *l, *p;
344    char file[MAXSTRING];
345    char spath[MAXSTRING];
346
347    num_files++;
348
349    /* Find path without the filename.
350     * I.e. everything after the last / is a "filename".
351     * OK, maybe it is a directory name, but we treat it like
352     * a filename. If we don't find a / then the whole name
353     * must be a path name (e.g. c:).
354     */
355    for (p=l=ar->fname; *p; p++) {
356       if (IsPathSeparator(*p)) {
357          l = p;                       /* set pos of last slash */
358       }
359    }
360    if (IsPathSeparator(*l)) {                   /* did we find a slash? */
361       l++;                            /* yes, point to filename */
362    } else {                           /* no, whole thing must be path name */
363       l = p;
364    }
365
366    /* If filename doesn't exist (i.e. root directory), we
367     * simply create a blank name consisting of a single
368     * space. This makes handling zero length filenames
369     * easier.
370     */
371    fnl = p - l;
372    if (fnl > max_file_len) {
373       max_file_len = fnl;
374    }
375    if (fnl > 255) {
376       printf(_("===== Filename truncated to 255 chars: %s\n"), l);
377       fnl = 255;
378       trunc_fname++;
379    }
380    if (fnl > 0) {
381       strncpy(file, l, fnl);          /* copy filename */
382       file[fnl] = 0;
383    } else {
384       file[0] = ' ';                  /* blank filename */
385       file[1] = 0;
386    }
387
388    pnl = l - ar->fname;
389    if (pnl > max_path_len) {
390       max_path_len = pnl;
391    }
392    if (pnl > 255) {
393       printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
394       pnl = 255;
395       trunc_path++;
396    }
397    strncpy(spath, ar->fname, pnl);
398    spath[pnl] = 0;
399    if (pnl == 0) {
400       spath[0] = ' ';
401       spath[1] = 0;
402       printf(_("========== Path length is zero. File=%s\n"), ar->fname);
403    }
404    if (debug_level >= 10) {
405       printf(_("Path: %s\n"), spath);
406       printf(_("File: %s\n"), file);
407    }
408
409 }
410
411 bool python_set_prog(JCR*, char const*) { return false; }
412
413 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
414 {
415    FILESET *jcr_fileset = jcr->fileset;
416    int num;
417    bool include = true;
418
419    findFILESET *fileset;
420    findFOPTS *current_opts;
421
422    fileset = (findFILESET *)malloc(sizeof(findFILESET));
423    memset(fileset, 0, sizeof(findFILESET));
424    ff->fileset = fileset;
425
426    fileset->state = state_none;
427    fileset->include_list.init(1, true);
428    fileset->exclude_list.init(1, true);
429
430    for ( ;; ) {
431       if (include) {
432          num = jcr_fileset->num_includes;
433       } else {
434          num = jcr_fileset->num_excludes;
435       }
436       for (int i=0; i<num; i++) {
437          INCEXE *ie;
438          int j, k;
439
440          if (include) {
441             ie = jcr_fileset->include_items[i];
442
443             /* New include */
444             fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
445             memset(fileset->incexe, 0, sizeof(findINCEXE));
446             fileset->incexe->opts_list.init(1, true);
447             fileset->incexe->name_list.init(0, 0);
448             fileset->include_list.append(fileset->incexe);
449          } else {
450             ie = jcr_fileset->exclude_items[i];
451
452             /* New exclude */
453             fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
454             memset(fileset->incexe, 0, sizeof(findINCEXE));
455             fileset->incexe->opts_list.init(1, true);
456             fileset->incexe->name_list.init(0, 0);
457             fileset->exclude_list.append(fileset->incexe);
458          }
459
460          for (j=0; j<ie->num_opts; j++) {
461             FOPTS *fo = ie->opts_list[j];
462
463             current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
464             memset(current_opts, 0, sizeof(findFOPTS));
465             fileset->incexe->current_opts = current_opts;
466             fileset->incexe->opts_list.append(current_opts);
467
468             current_opts->regex.init(1, true);
469             current_opts->regexdir.init(1, true);
470             current_opts->regexfile.init(1, true);
471             current_opts->wild.init(1, true);
472             current_opts->wilddir.init(1, true);
473             current_opts->wildfile.init(1, true);
474             current_opts->wildbase.init(1, true);
475             current_opts->fstype.init(1, true);
476             current_opts->drivetype.init(1, true);
477
478             set_options(current_opts, fo->opts);
479
480             for (k=0; k<fo->regex.size(); k++) {
481                // bnet_fsend(fd, "R %s\n", fo->regex.get(k));
482                current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
483             }
484             for (k=0; k<fo->regexdir.size(); k++) {
485                // bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
486                current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
487             }
488             for (k=0; k<fo->regexfile.size(); k++) {
489                // bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
490                current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
491             }
492             for (k=0; k<fo->wild.size(); k++) {
493                current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
494             }
495             for (k=0; k<fo->wilddir.size(); k++) {
496                current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
497             }
498             for (k=0; k<fo->wildfile.size(); k++) {
499                current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
500             }
501             for (k=0; k<fo->wildbase.size(); k++) {
502                current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
503             }
504             for (k=0; k<fo->fstype.size(); k++) {
505                current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
506             }
507             for (k=0; k<fo->drivetype.size(); k++) {
508                current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
509             }
510          }
511
512          for (j=0; j<ie->name_list.size(); j++) {
513             fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
514          }
515       }
516
517       if (!include) {                 /* If we just did excludes */
518          break;                       /*   all done */
519       }
520
521       include = false;                /* Now do excludes */
522    }
523
524    return true;
525 }
526
527 static void set_options(findFOPTS *fo, const char *opts)
528 {
529    int j;
530    const char *p;
531
532    for (p=opts; *p; p++) {
533       switch (*p) {
534       case 'a':                 /* alway replace */
535       case '0':                 /* no option */
536          break;
537       case 'e':
538          fo->flags |= FO_EXCLUDE;
539          break;
540       case 'f':
541          fo->flags |= FO_MULTIFS;
542          break;
543       case 'h':                 /* no recursion */
544          fo->flags |= FO_NO_RECURSION;
545          break;
546       case 'H':                 /* no hard link handling */
547          fo->flags |= FO_NO_HARDLINK;
548          break;
549       case 'i':
550          fo->flags |= FO_IGNORECASE;
551          break;
552       case 'M':                 /* MD5 */
553          fo->flags |= FO_MD5;
554          break;
555       case 'n':
556          fo->flags |= FO_NOREPLACE;
557          break;
558       case 'p':                 /* use portable data format */
559          fo->flags |= FO_PORTABLE;
560          break;
561       case 'R':                 /* Resource forks and Finder Info */
562          fo->flags |= FO_HFSPLUS;
563       case 'r':                 /* read fifo */
564          fo->flags |= FO_READFIFO;
565          break;
566       case 'S':
567          switch(*(p + 1)) {
568          case ' ':
569             /* Old director did not specify SHA variant */
570             fo->flags |= FO_SHA1;
571             break;
572          case '1':
573             fo->flags |= FO_SHA1;
574             p++;
575             break;
576 #ifdef HAVE_SHA2
577          case '2':
578             fo->flags |= FO_SHA256;
579             p++;
580             break;
581          case '3':
582             fo->flags |= FO_SHA512;
583             p++;
584             break;
585 #endif
586          default:
587             /* Automatically downgrade to SHA-1 if an unsupported
588              * SHA variant is specified */
589             fo->flags |= FO_SHA1;
590             p++;
591             break;
592          }
593          break;
594       case 's':
595          fo->flags |= FO_SPARSE;
596          break;
597       case 'm':
598          fo->flags |= FO_MTIMEONLY;
599          break;
600       case 'k':
601          fo->flags |= FO_KEEPATIME;
602          break;
603       case 'A':
604          fo->flags |= FO_ACL;
605          break;
606       case 'V':                  /* verify options */
607          /* Copy Verify Options */
608          for (j=0; *p && *p != ':'; p++) {
609             fo->VerifyOpts[j] = *p;
610             if (j < (int)sizeof(fo->VerifyOpts) - 1) {
611                j++;
612             }
613          }
614          fo->VerifyOpts[j] = 0;
615          break;
616       case 'w':
617          fo->flags |= FO_IF_NEWER;
618          break;
619       case 'W':
620          fo->flags |= FO_ENHANCEDWILD;
621          break;
622       case 'Z':                 /* gzip compression */
623          fo->flags |= FO_GZIP;
624          fo->GZIP_level = *++p - '0';
625          Dmsg1(200, "Compression level=%d\n", fo->GZIP_level);
626          break;
627       default:
628          Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
629          break;
630       }
631    }
632 }