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