557fee618a94017b2d8ad3438cc6bbeb62437a41
[debian/amanda] / client-src / client_util.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* 
27  * $Id: client_util.c,v 1.1.2.27 2003/07/02 17:03:32 martinea Exp $
28  *
29  */
30
31 #include "client_util.h"
32 #include "getfsent.h"
33 #include "util.h"
34
35 #define MAXMAXDUMPS 16
36
37 static char *fixup_relative(name, device)
38 char *name;
39 char *device;
40 {
41     char *newname;
42     if(*name != '/') {
43         char *dirname = amname_to_dirname(device);
44         newname = vstralloc(dirname, "/", name , NULL);
45         amfree(dirname);
46     }
47     else {
48         newname = stralloc(name);
49     }
50     return newname;
51 }
52
53
54 static char *get_name(diskname, exin, t, n)
55 char *diskname, *exin;
56 time_t t;
57 int n;
58 {
59     char number[NUM_STR_SIZE];
60     char *filename;
61     char *ts;
62
63     ts = construct_timestamp(&t);
64     if(n == 0)
65         number[0] = '\0';
66     else
67         ap_snprintf(number, sizeof(number), "%03d", n - 1);
68         
69     filename = vstralloc(get_pname(), ".", diskname, ".", ts, number, ".",
70                          exin, NULL);
71     amfree(ts);
72     return filename;
73 }
74
75
76 static char *build_name(disk, exin, verbose)
77 char *disk, *exin;
78 {
79     int n=0, fd=-1;
80     char *filename = NULL;
81     char *afilename = NULL;
82     char *diskname;
83     time_t curtime;
84     char *dbgdir = NULL;
85     char *e = NULL;
86     DIR *d;
87     struct dirent *entry;
88     char *test_name = NULL;
89     int match_len, d_name_len;
90
91
92     time(&curtime);
93     diskname = sanitise_filename(disk);
94
95     dbgdir = stralloc2(AMANDA_TMPDIR, "/");
96     if((d = opendir(AMANDA_TMPDIR)) == NULL) {
97         error("open debug directory \"%s\": %s",
98         AMANDA_TMPDIR, strerror(errno));
99     }
100     test_name = get_name(diskname, exin,
101                          curtime - (AMANDA_DEBUG_DAYS * 24 * 60 * 60), 0);
102     match_len = strlen(get_pname()) + strlen(diskname) + 2;
103     while((entry = readdir(d)) != NULL) {
104         if(is_dot_or_dotdot(entry->d_name)) {
105             continue;
106         }
107         d_name_len = strlen(entry->d_name);
108         if(strncmp(test_name, entry->d_name, match_len) != 0
109            || d_name_len < match_len + 14 + 8
110            || strcmp(entry->d_name+ d_name_len - 7, exin) != 0) {
111             continue;                           /* not one of our files */
112         }
113         if(strcmp(entry->d_name, test_name) < 0) {
114             e = newvstralloc(e, dbgdir, entry->d_name, NULL);
115             (void) unlink(e);                   /* get rid of old file */
116         }
117     }
118     amfree(test_name);
119     amfree(e);
120     closedir(d);
121
122     n=0;
123     do {
124         filename = get_name(diskname, exin, curtime, n);
125         afilename = newvstralloc(afilename, dbgdir, filename, NULL);
126         if((fd=open(afilename, O_WRONLY|O_CREAT|O_EXCL|O_APPEND, 0600)) < 0){
127             amfree(afilename);
128             n++;
129         }
130         else {
131             close(fd);
132         }
133         amfree(filename);
134     } while(!afilename && n < 1000);
135
136     if(afilename == NULL) {
137         filename = get_name(diskname, exin, curtime, 0);
138         afilename = newvstralloc(afilename, dbgdir, filename, NULL);
139         dbprintf(("%s: Cannot create '%s'\n", debug_prefix(NULL), afilename));
140         if(verbose)
141             printf("ERROR [cannot create: %s]\n", afilename);
142         amfree(filename);
143         amfree(afilename);
144     }
145
146     amfree(dbgdir);
147     amfree(diskname);
148
149     return afilename;
150 }
151
152
153 static int add_exclude(file_exclude, aexc, verbose)
154 FILE *file_exclude;
155 char *aexc;
156 {
157     int l;
158
159     l = strlen(aexc);
160     if(aexc[l-1] == '\n') {
161         aexc[l-1] = '\0';
162         l--;
163     }
164     fprintf(file_exclude, "%s\n", aexc);
165     return 1;
166 }
167
168 static int add_include(disk, device, file_include, ainc, verbose)
169 char *disk, *device;
170 FILE *file_include;
171 char *ainc;
172 {
173     int l;
174     int nb_exp=0;
175
176     l = strlen(ainc);
177     if(ainc[l-1] == '\n') {
178         ainc[l-1] = '\0';
179         l--;
180     }
181     if(l < 3) {
182         dbprintf(("%s: include must be at least 3 character long: %s\n",
183                   debug_prefix(NULL), ainc));
184         if(verbose)
185             printf("ERROR [include must be at least 3 character long: %s]\n", ainc);
186         return 0;
187     }
188     else if(ainc[0] != '.' && ainc[0] != '\0' && ainc[1] != '/') {
189         dbprintf(("%s: include must start with './': %s\n",
190                   debug_prefix(NULL), ainc));
191         if(verbose)
192             printf("ERROR [include must start with './': %s]\n", ainc);
193         return 0;
194     }
195     else {
196         char *incname = ainc+2;
197         if(strchr(incname, '/')) {
198             fprintf(file_include, "./%s\n", incname);
199             nb_exp++;
200         }
201         else {
202             char *regex;
203             DIR *d;
204             struct dirent *entry;
205
206             regex = glob_to_regex(incname);
207             if((d = opendir(device)) == NULL) {
208                 dbprintf(("%s: Can't open disk '%s']\n",
209                       debug_prefix(NULL), device));
210                 if(verbose)
211                     printf("ERROR [Can't open disk '%s']\n", device);
212                 return 0;
213             }
214             else {
215                 while((entry = readdir(d)) != NULL) {
216                     if(is_dot_or_dotdot(entry->d_name)) {
217                         continue;
218                     }
219                     if(match(regex, entry->d_name)) {
220                         fprintf(file_include, "./%s\n", entry->d_name);
221                         nb_exp++;
222                     }
223                 }
224                 closedir(d);
225             }
226         }
227     }
228     return nb_exp;
229 }
230
231 char *build_exclude(disk, device, options, verbose)
232 char *disk, *device;
233 option_t *options;
234 int verbose;
235 {
236     char *filename;
237     FILE *file_exclude;
238     FILE *exclude;
239     char *aexc = NULL;
240     sle_t *excl;
241     int nb_exclude = 0;
242
243     if(options->exclude_file) nb_exclude += options->exclude_file->nb_element;
244     if(options->exclude_list) nb_exclude += options->exclude_list->nb_element;
245
246     if(nb_exclude == 0) return NULL;
247
248     if((filename = build_name(disk, "exclude", verbose)) != NULL) {
249         if((file_exclude = fopen(filename,"w")) != NULL) {
250
251             if(options->exclude_file) {
252                 for(excl = options->exclude_file->first; excl != NULL;
253                     excl = excl->next) {
254                     add_exclude(file_exclude, excl->name,
255                                 verbose && options->exclude_optional == 0);
256                 }
257             }
258
259             if(options->exclude_list) {
260                 for(excl = options->exclude_list->first; excl != NULL;
261                     excl = excl->next) {
262                     char *exclname = fixup_relative(excl->name, device);
263                     if((exclude = fopen(exclname, "r")) != NULL) {
264                         while ((aexc = agets(exclude)) != NULL) {
265                             add_exclude(file_exclude, aexc,
266                                         verbose && options->exclude_optional == 0);
267                             amfree(aexc);
268                         }
269                         fclose(exclude);
270                     }
271                     else {
272                         dbprintf(("%s: Can't open exclude file '%s': %s\n",
273                                   debug_prefix(NULL),
274                                   exclname,
275                                   strerror(errno)));
276                         if(verbose && (options->exclude_optional == 0 ||
277                                        errno != ENOENT))
278                             printf("ERROR [Can't open exclude file '%s': %s]\n",
279                                    exclname, strerror(errno));
280                     }
281                     amfree(exclname);
282                 }
283             }
284             fclose(file_exclude);
285         }
286         else {
287             dbprintf(("%s: Can't create exclude file '%s': %s\n",
288                       debug_prefix(NULL),
289                       filename,
290                       strerror(errno)));
291             if(verbose)
292                 printf("ERROR [Can't create exclude file '%s': %s]\n", filename,
293                         strerror(errno));
294         }
295     }
296
297     return filename;
298 }
299
300 char *build_include(disk, device, options, verbose)
301 char *disk;
302 char *device;
303 option_t *options;
304 int verbose;
305 {
306     char *filename;
307     FILE *file_include;
308     FILE *include;
309     char *ainc = NULL;
310     sle_t *incl;
311     int nb_include = 0;
312     int nb_exp = 0;
313
314     if(options->include_file) nb_include += options->include_file->nb_element;
315     if(options->include_list) nb_include += options->include_list->nb_element;
316
317     if(nb_include == 0) return NULL;
318
319     if((filename = build_name(disk, "include", verbose)) != NULL) {
320         if((file_include = fopen(filename,"w")) != NULL) {
321
322             if(options->include_file) {
323                 for(incl = options->include_file->first; incl != NULL;
324                     incl = incl->next) {
325                     nb_exp += add_include(disk, device, file_include,
326                                   incl->name,
327                                    verbose && options->include_optional == 0);
328                 }
329             }
330
331             if(options->include_list) {
332                 for(incl = options->include_list->first; incl != NULL;
333                     incl = incl->next) {
334                     char *inclname = fixup_relative(incl->name, device);
335                     if((include = fopen(inclname, "r")) != NULL) {
336                         while ((ainc = agets(include)) != NULL) {
337                             nb_exp += add_include(disk, device,
338                                                   file_include, ainc,
339                                                   verbose && options->include_optional == 0);
340                             amfree(ainc);
341                         }
342                         fclose(include);
343                     }
344                     else {
345                         dbprintf(("%s: Can't open include file '%s': %s\n",
346                                   debug_prefix(NULL),
347                                   inclname,
348                                   strerror(errno)));
349                         if(verbose && (options->include_optional == 0 ||
350                                        errno != ENOENT))
351                             printf("ERROR [Can't open include file '%s': %s]\n",
352                                    inclname, strerror(errno));
353                    }
354                    amfree(inclname);
355                 }
356             }
357             fclose(file_include);
358         }
359         else {
360             dbprintf(("%s: Can't create include file '%s': %s\n",
361                       debug_prefix(NULL),
362                       filename,
363                       strerror(errno)));
364             if(verbose)
365                 printf("ERROR [Can't create include file '%s': %s]\n", filename,
366                         strerror(errno));
367         }
368     }
369         
370     if(nb_exp == 0) {
371         dbprintf(("%s: No include for '%s'\n", debug_prefix(NULL), disk));
372         if(verbose && options->include_optional == 0)
373             printf("ERROR [No include for '%s']\n", disk);
374     }
375
376     return filename;
377 }
378
379
380 void init_options(options)
381 option_t *options;
382 {
383     options->str = NULL;
384     options->compress = NO_COMPR;
385     options->no_record = 0;
386     options->bsd_auth = 0;
387     options->createindex = 0;
388 #ifdef KRB4_SECURITY
389     options->krb4_auth = 0;
390     options->kencrypt = 0;
391 #endif
392     options->exclude_file = NULL;
393     options->exclude_list = NULL;
394     options->include_file = NULL;
395     options->include_list = NULL;
396     options->exclude_optional = 0;
397     options->include_optional = 0;
398 }
399
400
401 option_t *parse_options(str, disk, device, fs, verbose)
402 char *str;
403 char *disk, *device;
404 am_feature_t *fs;
405 int verbose;
406 {
407     char *exc;
408     option_t *options;
409     char *p, *tok;
410
411     options = alloc(sizeof(option_t));
412     init_options(options);
413     options->str = stralloc(str);
414
415     p = stralloc(str);
416     tok = strtok(p,";");
417
418     while (tok != NULL) {
419         if(am_has_feature(fs, fe_options_auth)
420            && strncmp(tok, "auth=", 5) == 0) {
421             if(options->bsd_auth
422 #ifdef KRB4_SECURITY
423                + options->krb4_auth
424 #endif
425                > 0) {
426                 dbprintf(("%s: multiple auth option\n", 
427                           debug_prefix(NULL)));
428                 if(verbose) {
429                     printf("ERROR [multiple auth option]\n");
430                 }
431             }
432             if(strcasecmp(tok + 5, "bsd") == 0) {
433                 options->bsd_auth = 1;
434             }
435 #ifdef KRB4_SECURITY
436             else if(strcasecmp(tok + 5, "krb4") == 0) {
437                 options->krb4_auth = 1;
438             }
439 #endif
440             else {
441                 dbprintf(("%s: unknown auth= value \"%s\"\n",
442                           debug_prefix(NULL), tok + 5));
443                 if(verbose) {
444                     printf("ERROR [unknown auth= value \"%s\"]\n", tok + 5);
445                 }
446             }
447         }
448         else if(strcmp(tok, "compress-fast") == 0) {
449             if(options->compress != NO_COMPR) {
450                 dbprintf(("%s: multiple compress option\n", 
451                           debug_prefix(NULL)));
452                 if(verbose) {
453                     printf("ERROR [multiple compress option]\n");
454                 }
455             }
456             options->compress = COMPR_FAST;
457         }
458         else if(strcmp(tok, "compress-best") == 0) {
459             if(options->compress != NO_COMPR) {
460                 dbprintf(("%s: multiple compress option\n", 
461                           debug_prefix(NULL)));
462                 if(verbose) {
463                     printf("ERROR [multiple compress option]\n");
464                 }
465             }
466             options->compress = COMPR_BEST;
467         }
468         else if(strcmp(tok, "srvcomp-fast") == 0) {
469             if(options->compress != NO_COMPR) {
470                 dbprintf(("%s: multiple compress option\n", 
471                           debug_prefix(NULL)));
472                 if(verbose) {
473                     printf("ERROR [multiple compress option]\n");
474                 }
475             }
476             options->compress = COMPR_SERVER_FAST;
477         }
478         else if(strcmp(tok, "srvcomp-best") == 0) {
479             if(options->compress != NO_COMPR) {
480                 dbprintf(("%s: multiple compress option\n", 
481                           debug_prefix(NULL)));
482                 if(verbose) {
483                     printf("ERROR [multiple compress option]\n");
484                 }
485             }
486             options->compress = COMPR_SERVER_BEST;
487         }
488         else if(strcmp(tok, "no-record") == 0) {
489             if(options->no_record != 0) {
490                 dbprintf(("%s: multiple no-record option\n", 
491                           debug_prefix(NULL)));
492                 if(verbose) {
493                     printf("ERROR [multiple no-record option]\n");
494                 }
495             }
496             options->no_record = 1;
497         }
498         else if(strcmp(tok, "bsd-auth") == 0) {
499             if(options->bsd_auth
500 #ifdef KRB4_SECURITY
501                + options->krb4_auth
502 #endif
503                > 0) {
504                 dbprintf(("%s: multiple auth option\n", 
505                           debug_prefix(NULL)));
506                 if(verbose) {
507                     printf("ERROR [multiple auth option]\n");
508                 }
509             }
510             options->bsd_auth = 1;
511         }
512         else if(strcmp(tok, "index") == 0) {
513             if(options->createindex != 0) {
514                 dbprintf(("%s: multiple index option\n", 
515                           debug_prefix(NULL)));
516                 if(verbose) {
517                     printf("ERROR [multiple index option]\n");
518                 }
519             }
520             options->createindex = 1;
521         }
522 #ifdef KRB4_SECURITY
523         else if(strcmp(tok, "krb4-auth") == 0) {
524             if(options->bsd_auth + options->krb4_auth > 0) {
525                 dbprintf(("%s: multiple auth option\n", 
526                           debug_prefix(NULL)));
527                 if(verbose) {
528                     printf("ERROR [multiple auth option]\n");
529                 }
530             }
531             options->krb4_auth = 1;
532         }
533         else if(strcmp(tok, "kencrypt") == 0) {
534             if(options->kencrypt != 0) {
535                 dbprintf(("%s: multiple kencrypt option\n", 
536                           debug_prefix(NULL)));
537                 if(verbose) {
538                     printf("ERROR [multiple kencrypt option]\n");
539                 }
540             }
541             options->kencrypt = 1;
542         }
543 #endif
544         else if(strcmp(tok, "exclude-optional") == 0) {
545             if(options->exclude_optional != 0) {
546                 dbprintf(("%s: multiple exclude-optional option\n", 
547                           debug_prefix(NULL)));
548                 if(verbose) {
549                     printf("ERROR [multiple exclude-optional option]\n");
550                 }
551             }
552             options->exclude_optional = 1;
553         }
554         else if(strcmp(tok, "include-optional") == 0) {
555             if(options->include_optional != 0) {
556                 dbprintf(("%s: multiple include-optional option\n", 
557                           debug_prefix(NULL)));
558                 if(verbose) {
559                     printf("ERROR [multiple include-optional option]\n");
560                 }
561             }
562             options->include_optional = 1;
563         }
564         else if(strncmp(tok,"exclude-file=", 13) == 0) {
565             exc = &tok[13];
566             options->exclude_file = append_sl(options->exclude_file,exc);
567         }
568         else if(strncmp(tok,"exclude-list=", 13) == 0) {
569             exc = &tok[13];
570             options->exclude_list = append_sl(options->exclude_list, exc);
571         }
572         else if(strncmp(tok,"include-file=", 13) == 0) {
573             exc = &tok[13];
574             options->include_file = append_sl(options->include_file,exc);
575         }
576         else if(strncmp(tok,"include-list=", 13) == 0) {
577             exc = &tok[13];
578             options->include_list = append_sl(options->include_list, exc);
579         }
580         else if(strcmp(tok,"|") == 0) {
581         }
582         else {
583             dbprintf(("%s: unknown option \"%s\"\n",
584                                   debug_prefix(NULL), tok));
585             if(verbose) {
586                 printf("ERROR [unknown option \"%s\"]\n", tok);
587             }
588         }
589         tok = strtok(NULL, ";");
590     }
591     amfree(p);
592     return options;
593 }
594
595
596 void init_g_options(g_options)
597 g_option_t *g_options;
598 {
599     g_options->features = NULL;
600     g_options->hostname = NULL;
601     g_options->maxdumps = 0;
602 }
603
604
605 g_option_t *parse_g_options(str, verbose)
606 char *str;
607 int verbose;
608 {
609     g_option_t *g_options;
610     char *p, *tok;
611     int new_maxdumps;
612
613     g_options = alloc(sizeof(g_option_t));
614     init_g_options(g_options);
615     g_options->str = stralloc(str);
616
617     p = stralloc(str);
618     tok = strtok(p,";");
619
620     while (tok != NULL) {
621         if(strncmp(tok,"features=", 9) == 0) {
622             if(g_options->features != NULL) {
623                 dbprintf(("%s: multiple features option\n", 
624                           debug_prefix(NULL)));
625                 if(verbose) {
626                     printf("ERROR [multiple features option]\n");
627                 }
628             }
629             if((g_options->features = am_string_to_feature(tok+9)) == NULL) {
630                 dbprintf(("%s: bad features value \"%s\n",
631                           debug_prefix(NULL), tok+10));
632                 if(verbose) {
633                     printf("ERROR [bad features value \"%s\"]\n", tok+10);
634                 }
635             }
636         }
637         else if(strncmp(tok,"hostname=", 9) == 0) {
638             if(g_options->hostname != NULL) {
639                 dbprintf(("%s: multiple hostname option\n", 
640                           debug_prefix(NULL)));
641                 if(verbose) {
642                     printf("ERROR [multiple hostname option]\n");
643                 }
644             }
645             g_options->hostname = stralloc(tok+9);
646         }
647         else if(strncmp(tok,"maxdumps=", 9) == 0) {
648             if(g_options->maxdumps != 0) {
649                 dbprintf(("%s: multiple maxdumps option\n", 
650                           debug_prefix(NULL)));
651                 if(verbose) {
652                     printf("ERROR [multiple maxdumps option]\n");
653                 }
654             }
655             if(sscanf(tok+9, "%d;", &new_maxdumps) == 1) {
656                 if (new_maxdumps > MAXMAXDUMPS) {
657                     g_options->maxdumps = MAXMAXDUMPS;
658                 }
659                 else if (new_maxdumps > 0) {
660                     g_options->maxdumps = new_maxdumps;
661                 }
662                 else {
663                     dbprintf(("%s: bad maxdumps value \"%s\"\n",
664                               debug_prefix(NULL), tok+9));
665                     if(verbose) {
666                         printf("ERROR [bad maxdumps value \"%s\"]\n",
667                                tok+9);
668                     }
669                 }
670             }
671             else {
672                 dbprintf(("%s: bad maxdumps value \"%s\"\n",
673                           debug_prefix(NULL), tok+9));
674                 if(verbose) {
675                     printf("ERROR [bad maxdumps value \"%s\"]\n",
676                            tok+9);
677                 }
678             }
679         }
680         else {
681             dbprintf(("%s: unknown option \"%s\"\n",
682                                   debug_prefix(NULL), tok));
683             if(verbose) {
684                 printf("ERROR [unknown option \"%s\"]\n", tok);
685             }
686         }
687         tok = strtok(NULL, ";");
688     }
689     if(g_options->features == NULL) {
690         g_options->features = am_set_default_feature_set();
691     }
692     if(g_options->maxdumps == 0) /* default */
693         g_options->maxdumps = 1;
694     amfree(p);
695     return g_options;
696 }