]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/tools/testfind.c
Implement Plugin Directory and plugin events.
[bacula/bacula] / bacula / src / tools / testfind.c
1 /*
2  * Test program for find files
3  *
4  *  Kern Sibbald, MM
5  *
6  */
7 /*
8    Bacula® - The Network Backup Solution
9
10    Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
11
12    The main author of Bacula is Kern Sibbald, with contributions from
13    many others, a complete list can be found in the file AUTHORS.
14    This program is Free Software; you can redistribute it and/or
15    modify it under the terms of version two of the GNU General Public
16    License as published by the Free Software Foundation and included
17    in the file LICENSE.
18
19    This program is distributed in the hope that it will be useful, but
20    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 License
25    along with this program; if not, write to the Free Software
26    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27    02110-1301, USA.
28
29    Bacula® is a registered trademark of John Walker.
30    The licensor of Bacula is the Free Software Foundation Europe
31    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
32    Switzerland, email:ftf@fsfeurope.org.
33 */
34
35 #include "bacula.h"
36 #include "dird/dird.h"
37 #include "findlib/find.h"
38
39
40 #if defined(HAVE_WIN32)
41 #define isatty(fd) (fd==0)
42 #endif
43
44 /* Dummy functions */
45 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
46 int generate_job_event(JCR *jcr, const char *event) { return 1; }
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             if (fo->reader) {
195                free(fo->reader);
196             }
197             if (fo->writer) {
198                free(fo->writer);
199             }
200          }
201          incexe->opts_list.destroy();
202          incexe->name_list.destroy();
203       }
204       fileset->include_list.destroy();
205
206       /* Delete FileSet Exclude lists */
207       for (i=0; i<fileset->exclude_list.size(); i++) {
208          findINCEXE *incexe = (findINCEXE *)fileset->exclude_list.get(i);
209          for (j=0; j<incexe->opts_list.size(); j++) {
210             findFOPTS *fo = (findFOPTS *)incexe->opts_list.get(j);
211             fo->regex.destroy();
212             fo->regexdir.destroy();
213             fo->regexfile.destroy();
214             fo->wild.destroy();
215             fo->wilddir.destroy();
216             fo->wildfile.destroy();
217             fo->wildbase.destroy();
218             fo->fstype.destroy();
219             fo->drivetype.destroy();
220          }
221          incexe->opts_list.destroy();
222          incexe->name_list.destroy();
223       }
224       fileset->exclude_list.destroy();
225       free(fileset);
226    }
227    ff->fileset = NULL;
228    hard_links = term_find_files(ff);
229
230    printf(_("\n"
231 "Total files    : %d\n"
232 "Max file length: %d\n"
233 "Max path length: %d\n"
234 "Files truncated: %d\n"
235 "Paths truncated: %d\n"
236 "Hard links     : %d\n"),
237      num_files, max_file_len, max_path_len,
238      trunc_fname, trunc_path, hard_links);
239
240    term_msg();
241
242    close_memory_pool();
243    sm_dump(false);
244    exit(0);
245 }
246
247 static int print_file(FF_PKT *ff, void *pkt, bool top_level) 
248 {
249
250    switch (ff->type) {
251    case FT_LNKSAVED:
252       if (debug_level == 1) {
253          printf("%s\n", ff->fname);
254       } else if (debug_level > 1) {
255          printf("Lnka: %s -> %s\n", ff->fname, ff->link);
256       }
257       break;
258    case FT_REGE:
259       if (debug_level == 1) {
260          printf("%s\n", ff->fname);
261       } else if (debug_level > 1) {
262          printf("Empty: %s\n", ff->fname);
263       }
264       count_files(ff);
265       break;
266    case FT_REG:
267       if (debug_level == 1) {
268          printf("%s\n", ff->fname);
269       } else if (debug_level > 1) {
270          printf(_("Reg: %s\n"), ff->fname);
271       }
272       count_files(ff);
273       break;
274    case FT_LNK:
275       if (debug_level == 1) {
276          printf("%s\n", ff->fname);
277       } else if (debug_level > 1) {
278          printf("Lnk: %s -> %s\n", ff->fname, ff->link);
279       }
280       count_files(ff);
281       break;
282    case FT_DIRBEGIN:
283       return 1;
284    case FT_NORECURSE:
285    case FT_NOFSCHG:
286    case FT_INVALIDFS:
287    case FT_INVALIDDT:
288    case FT_DIREND:
289       if (debug_level) {
290          char errmsg[100] = "";
291          if (ff->type == FT_NORECURSE) {
292             bstrncpy(errmsg, _("\t[will not descend: recursion turned off]"), sizeof(errmsg));
293          } else if (ff->type == FT_NOFSCHG) {
294             bstrncpy(errmsg, _("\t[will not descend: file system change not allowed]"), sizeof(errmsg));
295          } else if (ff->type == FT_INVALIDFS) {
296             bstrncpy(errmsg, _("\t[will not descend: disallowed file system]"), sizeof(errmsg));
297          } else if (ff->type == FT_INVALIDDT) {
298             bstrncpy(errmsg, _("\t[will not descend: disallowed drive type]"), sizeof(errmsg));
299          }
300          printf("%s%s%s\n", (debug_level > 1 ? "Dir: " : ""), ff->fname, errmsg);
301       }
302       ff->type = FT_DIREND;
303       count_files(ff);
304       break;
305    case FT_SPEC:
306       if (debug_level == 1) {
307          printf("%s\n", ff->fname);
308       } else if (debug_level > 1) {
309          printf("Spec: %s\n", ff->fname);
310       }
311       count_files(ff);
312       break;
313    case FT_NOACCESS:
314       printf(_("Err: Could not access %s: %s\n"), ff->fname, strerror(errno));
315       break;
316    case FT_NOFOLLOW:
317       printf(_("Err: Could not follow ff->link %s: %s\n"), ff->fname, strerror(errno));
318       break;
319    case FT_NOSTAT:
320       printf(_("Err: Could not stat %s: %s\n"), ff->fname, strerror(errno));
321       break;
322    case FT_NOCHG:
323       printf(_("Skip: File not saved. No change. %s\n"), ff->fname);
324       break;
325    case FT_ISARCH:
326       printf(_("Err: Attempt to backup archive. Not saved. %s\n"), ff->fname);
327       break;
328    case FT_NOOPEN:
329       printf(_("Err: Could not open directory %s: %s\n"), ff->fname, strerror(errno));
330       break;
331    default:
332       printf(_("Err: Unknown file ff->type %d: %s\n"), ff->type, ff->fname);
333       break;
334    }
335    if (attrs) {
336       char attr[200];
337       encode_attribsEx(NULL, attr, ff);
338       if (*attr != 0) {
339          printf("AttrEx=%s\n", attr);
340       }
341 //    set_attribsEx(NULL, ff->fname, NULL, NULL, ff->type, attr);
342    }
343    return 1;
344 }
345
346 static void count_files(FF_PKT *ar)
347 {
348    int fnl, pnl;
349    char *l, *p;
350    char file[MAXSTRING];
351    char spath[MAXSTRING];
352
353    num_files++;
354
355    /* Find path without the filename.
356     * I.e. everything after the last / is a "filename".
357     * OK, maybe it is a directory name, but we treat it like
358     * a filename. If we don't find a / then the whole name
359     * must be a path name (e.g. c:).
360     */
361    for (p=l=ar->fname; *p; p++) {
362       if (IsPathSeparator(*p)) {
363          l = p;                       /* set pos of last slash */
364       }
365    }
366    if (IsPathSeparator(*l)) {                   /* did we find a slash? */
367       l++;                            /* yes, point to filename */
368    } else {                           /* no, whole thing must be path name */
369       l = p;
370    }
371
372    /* If filename doesn't exist (i.e. root directory), we
373     * simply create a blank name consisting of a single
374     * space. This makes handling zero length filenames
375     * easier.
376     */
377    fnl = p - l;
378    if (fnl > max_file_len) {
379       max_file_len = fnl;
380    }
381    if (fnl > 255) {
382       printf(_("===== Filename truncated to 255 chars: %s\n"), l);
383       fnl = 255;
384       trunc_fname++;
385    }
386    if (fnl > 0) {
387       strncpy(file, l, fnl);          /* copy filename */
388       file[fnl] = 0;
389    } else {
390       file[0] = ' ';                  /* blank filename */
391       file[1] = 0;
392    }
393
394    pnl = l - ar->fname;
395    if (pnl > max_path_len) {
396       max_path_len = pnl;
397    }
398    if (pnl > 255) {
399       printf(_("========== Path name truncated to 255 chars: %s\n"), ar->fname);
400       pnl = 255;
401       trunc_path++;
402    }
403    strncpy(spath, ar->fname, pnl);
404    spath[pnl] = 0;
405    if (pnl == 0) {
406       spath[0] = ' ';
407       spath[1] = 0;
408       printf(_("========== Path length is zero. File=%s\n"), ar->fname);
409    }
410    if (debug_level >= 10) {
411       printf(_("Path: %s\n"), spath);
412       printf(_("File: %s\n"), file);
413    }
414
415 }
416
417 bool python_set_prog(JCR*, char const*) { return false; }
418
419 static bool copy_fileset(FF_PKT *ff, JCR *jcr)
420 {
421    FILESET *jcr_fileset = jcr->fileset;
422    int num;
423    bool include = true;
424
425    findFILESET *fileset;
426    findFOPTS *current_opts;
427
428    fileset = (findFILESET *)malloc(sizeof(findFILESET));
429    memset(fileset, 0, sizeof(findFILESET));
430    ff->fileset = fileset;
431
432    fileset->state = state_none;
433    fileset->include_list.init(1, true);
434    fileset->exclude_list.init(1, true);
435
436    for ( ;; ) {
437       if (include) {
438          num = jcr_fileset->num_includes;
439       } else {
440          num = jcr_fileset->num_excludes;
441       }
442       for (int i=0; i<num; i++) {
443          INCEXE *ie;
444          int j, k;
445
446          if (include) {
447             ie = jcr_fileset->include_items[i];
448
449             /* New include */
450             fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
451             memset(fileset->incexe, 0, sizeof(findINCEXE));
452             fileset->incexe->opts_list.init(1, true);
453             fileset->incexe->name_list.init(0, 0);
454             fileset->include_list.append(fileset->incexe);
455          } else {
456             ie = jcr_fileset->exclude_items[i];
457
458             /* New exclude */
459             fileset->incexe = (findINCEXE *)malloc(sizeof(findINCEXE));
460             memset(fileset->incexe, 0, sizeof(findINCEXE));
461             fileset->incexe->opts_list.init(1, true);
462             fileset->incexe->name_list.init(0, 0);
463             fileset->exclude_list.append(fileset->incexe);
464          }
465
466          for (j=0; j<ie->num_opts; j++) {
467             FOPTS *fo = ie->opts_list[j];
468
469             current_opts = (findFOPTS *)malloc(sizeof(findFOPTS));
470             memset(current_opts, 0, sizeof(findFOPTS));
471             fileset->incexe->current_opts = current_opts;
472             fileset->incexe->opts_list.append(current_opts);
473
474             current_opts->regex.init(1, true);
475             current_opts->regexdir.init(1, true);
476             current_opts->regexfile.init(1, true);
477             current_opts->wild.init(1, true);
478             current_opts->wilddir.init(1, true);
479             current_opts->wildfile.init(1, true);
480             current_opts->wildbase.init(1, true);
481             current_opts->fstype.init(1, true);
482             current_opts->drivetype.init(1, true);
483
484             set_options(current_opts, fo->opts);
485
486             for (k=0; k<fo->regex.size(); k++) {
487                // bnet_fsend(fd, "R %s\n", fo->regex.get(k));
488                current_opts->regex.append(bstrdup((const char *)fo->regex.get(k)));
489             }
490             for (k=0; k<fo->regexdir.size(); k++) {
491                // bnet_fsend(fd, "RD %s\n", fo->regexdir.get(k));
492                current_opts->regexdir.append(bstrdup((const char *)fo->regexdir.get(k)));
493             }
494             for (k=0; k<fo->regexfile.size(); k++) {
495                // bnet_fsend(fd, "RF %s\n", fo->regexfile.get(k));
496                current_opts->regexfile.append(bstrdup((const char *)fo->regexfile.get(k)));
497             }
498             for (k=0; k<fo->wild.size(); k++) {
499                current_opts->wild.append(bstrdup((const char *)fo->wild.get(k)));
500             }
501             for (k=0; k<fo->wilddir.size(); k++) {
502                current_opts->wilddir.append(bstrdup((const char *)fo->wilddir.get(k)));
503             }
504             for (k=0; k<fo->wildfile.size(); k++) {
505                current_opts->wildfile.append(bstrdup((const char *)fo->wildfile.get(k)));
506             }
507             for (k=0; k<fo->wildbase.size(); k++) {
508                current_opts->wildbase.append(bstrdup((const char *)fo->wildbase.get(k)));
509             }
510             for (k=0; k<fo->fstype.size(); k++) {
511                current_opts->fstype.append(bstrdup((const char *)fo->fstype.get(k)));
512             }
513             for (k=0; k<fo->drivetype.size(); k++) {
514                current_opts->drivetype.append(bstrdup((const char *)fo->drivetype.get(k)));
515             }
516             if (fo->reader) {
517                current_opts->reader = bstrdup(fo->reader);
518             }
519             if (fo->writer) {
520                current_opts->writer = bstrdup(fo->writer);
521             }
522          }
523
524          for (j=0; j<ie->name_list.size(); j++) {
525             fileset->incexe->name_list.append(bstrdup((const char *)ie->name_list.get(j)));
526          }
527       }
528
529       if (!include) {                 /* If we just did excludes */
530          break;                       /*   all done */
531       }
532
533       include = false;                /* Now do excludes */
534    }
535
536    return true;
537 }
538
539 static void set_options(findFOPTS *fo, const char *opts)
540 {
541    int j;
542    const char *p;
543
544    for (p=opts; *p; p++) {
545       switch (*p) {
546       case 'a':                 /* alway replace */
547       case '0':                 /* no option */
548          break;
549       case 'e':
550          fo->flags |= FO_EXCLUDE;
551          break;
552       case 'f':
553          fo->flags |= FO_MULTIFS;
554          break;
555       case 'h':                 /* no recursion */
556          fo->flags |= FO_NO_RECURSION;
557          break;
558       case 'H':                 /* no hard link handling */
559          fo->flags |= FO_NO_HARDLINK;
560          break;
561       case 'i':
562          fo->flags |= FO_IGNORECASE;
563          break;
564       case 'M':                 /* MD5 */
565          fo->flags |= FO_MD5;
566          break;
567       case 'n':
568          fo->flags |= FO_NOREPLACE;
569          break;
570       case 'p':                 /* use portable data format */
571          fo->flags |= FO_PORTABLE;
572          break;
573       case 'R':                 /* Resource forks and Finder Info */
574          fo->flags |= FO_HFSPLUS;
575       case 'r':                 /* read fifo */
576          fo->flags |= FO_READFIFO;
577          break;
578       case 'S':
579          switch(*(p + 1)) {
580          case ' ':
581             /* Old director did not specify SHA variant */
582             fo->flags |= FO_SHA1;
583             break;
584          case '1':
585             fo->flags |= FO_SHA1;
586             p++;
587             break;
588 #ifdef HAVE_SHA2
589          case '2':
590             fo->flags |= FO_SHA256;
591             p++;
592             break;
593          case '3':
594             fo->flags |= FO_SHA512;
595             p++;
596             break;
597 #endif
598          default:
599             /* Automatically downgrade to SHA-1 if an unsupported
600              * SHA variant is specified */
601             fo->flags |= FO_SHA1;
602             p++;
603             break;
604          }
605          break;
606       case 's':
607          fo->flags |= FO_SPARSE;
608          break;
609       case 'm':
610          fo->flags |= FO_MTIMEONLY;
611          break;
612       case 'k':
613          fo->flags |= FO_KEEPATIME;
614          break;
615       case 'A':
616          fo->flags |= FO_ACL;
617          break;
618       case 'V':                  /* verify options */
619          /* Copy Verify Options */
620          for (j=0; *p && *p != ':'; p++) {
621             fo->VerifyOpts[j] = *p;
622             if (j < (int)sizeof(fo->VerifyOpts) - 1) {
623                j++;
624             }
625          }
626          fo->VerifyOpts[j] = 0;
627          break;
628       case 'w':
629          fo->flags |= FO_IF_NEWER;
630          break;
631       case 'W':
632          fo->flags |= FO_ENHANCEDWILD;
633          break;
634       case 'Z':                 /* gzip compression */
635          fo->flags |= FO_GZIP;
636          fo->GZIP_level = *++p - '0';
637          Dmsg1(200, "Compression level=%d\n", fo->GZIP_level);
638          break;
639       default:
640          Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
641          break;
642       }
643    }
644 }