Imported Upstream version 2.5.1
[debian/amanda] / server-src / diskfile.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  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: diskfile.c,v 1.95 2006/07/26 15:17:37 martinea Exp $
29  *
30  * read disklist file
31  */
32 #include "amanda.h"
33 #include "arglist.h"
34 #include "conffile.h"
35 #include "diskfile.h"
36 #include "util.h"
37
38 static am_host_t *hostlist;
39
40 /* local functions */
41 static char *upcase(char *st);
42 static int parse_diskline(disklist_t *, const char *, FILE *, int *, char **);
43 static void disk_parserror(const char *, int, const char *, ...)
44     __attribute__ ((format (printf, 3, 4)));
45
46
47 int
48 read_diskfile(
49     const char *filename,
50     disklist_t *lst)
51 {
52     FILE *diskf;
53     int line_num;
54     char *line;
55
56     /* initialize */
57     hostlist = NULL;
58     lst->head = lst->tail = NULL;
59     line_num = 0;
60
61     if ((diskf = fopen(filename, "r")) == NULL) {
62         return -1;
63         /*NOTREACHED*/
64     }
65
66     while ((line = agets(diskf)) != NULL) {
67         line_num++;
68         if (line[0] != '\0') {
69             if (parse_diskline(lst, filename, diskf, &line_num, &line) < 0) {
70                 amfree(line);
71                 afclose(diskf);
72                 return (-1);
73             }
74         }
75         amfree(line);
76     }
77
78     afclose(diskf);
79     return (0);
80 }
81
82 am_host_t *
83 lookup_host(
84     const char *hostname)
85 {
86     am_host_t *p;
87
88     for (p = hostlist; p != NULL; p = p->next) {
89         if(strcasecmp(p->hostname, hostname) == 0) return p;
90     }
91     return (NULL);
92 }
93
94 disk_t *
95 lookup_disk(
96     const char *hostname,
97     const char *diskname)
98 {
99     am_host_t *host;
100     disk_t *disk;
101
102     host = lookup_host(hostname);
103     if (host == NULL)
104         return (NULL);
105
106     for (disk = host->disks; disk != NULL; disk = disk->hostnext) {
107         if (strcmp(disk->name, diskname) == 0)
108             return (disk);
109     }
110     return (NULL);
111 }
112
113
114 /*
115  * put disk on end of queue
116  */
117
118 void
119 enqueue_disk(
120     disklist_t *list,
121     disk_t *    disk)
122 {
123     if(list->tail == NULL) list->head = disk;
124     else list->tail->next = disk;
125     disk->prev = list->tail;
126
127     list->tail = disk;
128     disk->next = NULL;
129 }
130
131
132 /*
133  * put disk on head of queue
134  */
135
136 void
137 headqueue_disk(
138     disklist_t *list,
139     disk_t *    disk)
140 {
141     if(list->head == NULL) list->tail = disk;
142     else list->head->prev = disk;
143     disk->next = list->head;
144
145     list->head = disk;
146     disk->prev = NULL;
147 }
148
149
150 /*
151  * insert in sorted order
152  */
153
154 void
155 insert_disk(
156     disklist_t *list,
157     disk_t *    disk,
158     int         (*cmp)(disk_t *a, disk_t *b))
159 {
160     disk_t *prev, *ptr;
161
162     prev = NULL;
163     ptr = list->head;
164
165     while(ptr != NULL) {
166         if(cmp(disk, ptr) < 0) break;
167         prev = ptr;
168         ptr = ptr->next;
169     }
170     disk->next = ptr;
171     disk->prev = prev;
172
173     if(prev == NULL) list->head = disk;
174     else prev->next = disk;
175     if(ptr == NULL) list->tail = disk;
176     else ptr->prev = disk;
177 }
178
179 disk_t *
180 add_disk(
181     disklist_t *list,
182     char *      hostname,
183     char *      diskname)
184 {
185     disk_t *disk;
186     am_host_t *host;
187
188     disk = alloc(SIZEOF(disk_t));
189     disk->line = 0;
190     disk->tape_splitsize = (off_t)0;
191     disk->split_diskbuffer = NULL;
192     disk->fallback_splitsize = (off_t)0;
193     disk->name = stralloc(diskname);
194     disk->device = stralloc(diskname);
195     disk->spindle = -1;
196     disk->up = NULL;
197     disk->compress = COMP_NONE;
198     disk->encrypt  = ENCRYPT_NONE;
199     disk->start_t = 0;
200     disk->todo = 1;
201     disk->index = 1;
202     disk->exclude_list = NULL;
203     disk->exclude_file = NULL;
204     disk->include_list = NULL;
205     disk->include_file = NULL;
206
207     host = lookup_host(hostname);
208     if(host == NULL) {
209         host = alloc(SIZEOF(am_host_t));
210         host->next = hostlist;
211         hostlist = host;
212
213         host->hostname = stralloc(hostname);
214         host->disks = NULL;
215         host->inprogress = 0;
216         host->maxdumps = 1;
217         host->netif = NULL;
218         host->start_t = 0;
219         host->up = NULL;
220         host->features = NULL;
221     }
222     enqueue_disk(list, disk);
223
224     disk->host = host;
225     disk->hostnext = host->disks;
226     host->disks = disk;
227
228     return disk;
229 }
230
231
232 /*
233  * check if disk is present in list. Return true if so, false otherwise.
234  */
235
236 int
237 find_disk(
238     disklist_t *list,
239     disk_t *    disk)
240 {
241     disk_t *t;
242
243     t = list->head;
244     while ((t != NULL) && (t != disk)) {
245         t = t->next;
246     }
247     return (t == disk);
248 }
249
250
251 /*
252  * sort a whole queue
253  */
254
255 void
256 sort_disk(
257     disklist_t *in,
258     disklist_t *out,
259     int         (*cmp)(disk_t *a, disk_t *b))
260 {
261     disklist_t *tmp;
262     disk_t *disk;
263
264     tmp = in;           /* just in case in == out */
265
266     out->head = (disk_t *)0;
267     out->tail = (disk_t *)0;
268
269     while((disk = dequeue_disk(tmp)))
270         insert_disk(out, disk, cmp);
271 }
272
273
274 /*
275  * remove disk from front of queue
276  */
277
278 disk_t *
279 dequeue_disk(
280     disklist_t *list)
281 {
282     disk_t *disk;
283
284     if(list->head == NULL) return NULL;
285
286     disk = list->head;
287     list->head = disk->next;
288
289     if(list->head == NULL) list->tail = NULL;
290     else list->head->prev = NULL;
291
292     disk->prev = disk->next = NULL;     /* for debugging */
293     return disk;
294 }
295
296 void
297 remove_disk(
298     disklist_t *list,
299     disk_t *    disk)
300 {
301     if(disk->prev == NULL) list->head = disk->next;
302     else disk->prev->next = disk->next;
303
304     if(disk->next == NULL) list->tail = disk->prev;
305     else disk->next->prev = disk->prev;
306
307     disk->prev = disk->next = NULL;
308 }
309
310 void
311 free_disklist(
312     disklist_t* dl)
313 {
314     disk_t    *dp;
315     am_host_t *host, *hostnext;
316
317     while (dl->head != NULL) {
318         dp = dequeue_disk(dl);
319         amfree(dp->name);
320         free_sl(dp->exclude_file);
321         free_sl(dp->exclude_list);
322         free_sl(dp->include_file);
323         free_sl(dp->include_list);
324         free(dp);
325     }
326
327     for(host=hostlist; host != NULL; host = hostnext) {
328         amfree(host->hostname);
329         am_release_feature_set(host->features);
330         host->features = NULL;
331         hostnext = host->next;
332         amfree(host);
333     }
334     hostlist=NULL;
335 }
336
337 static char *
338 upcase(
339     char *st)
340 {
341     char *s = st;
342
343     while(*s) {
344         if(islower((int)*s)) *s = (char)toupper((int)*s);
345         s++;
346     }
347     return st;
348 }
349
350
351 /* return  0 on success */
352 /* return -1 on error   */
353 static int
354 parse_diskline(
355     disklist_t *lst,
356     const char *filename,
357     FILE *      diskf,
358     int *       line_num_p,
359     /*@keep@*/ char **  line_p)
360 {
361     am_host_t *host;
362     disk_t *disk;
363     dumptype_t *dtype;
364     interface_t *netif = 0;
365     char *hostname = NULL;
366     char *diskname, *diskdevice;
367     char *dumptype;
368     char *s, *fp;
369     int ch, dup = 0;
370     char *line = *line_p;
371     int line_num = *line_num_p;
372
373     assert(filename != NULL);
374     assert(line_num > 0);
375     assert(line != NULL);
376
377     s = line;
378     ch = *s++;
379     skip_whitespace(s, ch);
380     if(ch == '\0' || ch == '#')
381         return (0);
382
383     fp = s - 1;
384     skip_non_whitespace(s, ch);
385     s[-1] = '\0';
386     host = lookup_host(fp);
387     if (host == NULL) {
388       hostname = stralloc(fp);
389       malloc_mark(hostname);
390     } else {
391       hostname = host->hostname;
392     }
393
394     skip_whitespace(s, ch);
395     if(ch == '\0' || ch == '#') {
396         disk_parserror(filename, line_num, "disk device name expected");
397         amfree(hostname);
398         return (-1);
399     }
400
401     fp = s - 1;
402     skip_quoted_string(s, ch);
403     s[-1] = '\0';
404     diskname = unquote_string(fp);
405
406     skip_whitespace(s, ch);
407     if(ch == '\0' || ch == '#') {
408         disk_parserror(filename, line_num, "disk dumptype expected");
409         amfree(hostname);
410         amfree(diskname);
411         return (-1);
412     }
413     fp = s - 1;
414     skip_quoted_string(s, ch);
415     s[-1] = '\0';
416
417     /* diskdevice */
418     dumptype = NULL;
419     diskdevice = NULL;
420     dumptype = unquote_string(fp);
421     if(fp[0] != '{') {
422         if ((dtype = lookup_dumptype(dumptype)) == NULL) {
423             diskdevice = dumptype;
424             skip_whitespace(s, ch);
425             if(ch == '\0' || ch == '#') {
426                 disk_parserror(filename, line_num,
427                         "disk dumptype '%s' not found", dumptype);
428                 amfree(hostname);
429                 amfree(diskdevice);
430                 amfree(diskname);
431                 return (-1);
432             }
433
434             fp = s - 1;
435             skip_quoted_string(s, ch);
436             s[-1] = '\0';
437             dumptype = unquote_string(fp);
438         }
439     }
440     else
441         amfree(dumptype);
442
443     /* check for duplicate disk */
444     if(host && (disk = lookup_disk(hostname, diskname)) != NULL) {
445         disk_parserror(filename, line_num,
446             "duplicate disk record, previous on line %d", disk->line);
447         dup = 1;
448     } else {
449         disk = alloc(SIZEOF(disk_t));
450         malloc_mark(disk);
451         disk->line = line_num;
452         disk->name = diskname;
453         disk->device = diskdevice;
454         malloc_mark(disk->name);
455         disk->spindle = -1;
456         disk->up = NULL;
457         disk->inprogress = 0;
458     }
459
460     if (fp[0] == '{') {
461         s[-1] = (char)ch;
462         s = fp+2;
463         skip_whitespace(s, ch);
464         if (ch != '\0' && ch != '#') {
465             disk_parserror(filename, line_num,
466                       "expected line break after `{\', ignoring rest of line");
467         }
468
469         if (strchr(s-1, '}') &&
470             (strchr(s-1, '#') == NULL ||
471              strchr(s-1, '}') < strchr(s-1, '#'))) {
472             disk_parserror(filename, line_num,"'}' on same line than '{'");
473             amfree(hostname);
474             if(!dup) {
475                 amfree(disk->device);
476                 amfree(disk->name);
477                 amfree(disk);
478             } else {
479                 amfree(diskdevice);
480                 amfree(diskname);
481             }
482             return (-1);
483         }
484         amfree(line);
485
486         dtype = read_dumptype(vstralloc("custom(", hostname,
487                                         ":", disk->name, ")", 0),
488                               diskf, (char*)filename, line_num_p);
489         if (dtype == NULL || dup) {
490             disk_parserror(filename, line_num,
491                            "read of custom dumptype failed");
492             amfree(hostname);
493             if(!dup) {
494                 amfree(disk->device);
495                 amfree(disk->name);
496                 amfree(disk);
497             } else {
498                 amfree(diskdevice);
499                 amfree(diskname);
500             }
501             return (-1);
502         }
503
504         *line_p = line = agets(diskf);
505         line_num = *line_num_p; /* no incr, read_dumptype did it already */
506
507         if (line == NULL)
508             *line_p = line = stralloc("");
509         s = line;
510         ch = *s++;
511     } else {
512         if((dtype = lookup_dumptype(dumptype)) == NULL) {
513             char *qdt = quote_string(dumptype);
514
515             disk_parserror(filename, line_num, "undefined dumptype `%s'", qdt);
516             amfree(qdt);
517             amfree(dumptype);
518             amfree(hostname);
519             if (!dup) {
520                 amfree(disk->device);
521                 amfree(disk->name);
522                 amfree(disk);
523             } else {
524                 amfree(diskdevice);
525                 amfree(diskname);
526             }
527             return (-1);
528         }
529     }
530
531     if (dup) {
532         amfree(hostname);
533         amfree(diskdevice);
534         amfree(diskname);
535         return (-1);
536     }
537
538     disk->dtype_name         = dtype->name;
539     disk->program            = dumptype_get_program(dtype);
540     if(dumptype_get_exclude(dtype).type == 0) {
541         disk->exclude_list   = duplicate_sl(dumptype_get_exclude(dtype).sl);
542         disk->exclude_file   = NULL;
543     }
544     else {
545         disk->exclude_file   = duplicate_sl(dumptype_get_exclude(dtype).sl);
546         disk->exclude_list   = NULL;
547     }
548     disk->exclude_optional   = dumptype_get_exclude(dtype).optional;
549     if(dumptype_get_include(dtype).type == 0) {
550         disk->include_list   = duplicate_sl(dumptype_get_include(dtype).sl);
551         disk->include_file   = NULL;
552     }
553     else {
554         disk->include_file   = duplicate_sl(dumptype_get_include(dtype).sl);
555         disk->include_list   = NULL;
556     }
557     disk->include_optional   = dumptype_get_include(dtype).optional;
558     disk->priority           = dumptype_get_priority(dtype);
559     disk->dumpcycle          = dumptype_get_dumpcycle(dtype);
560 /*    disk->frequency        = dumptype_get_frequency(dtype);*/
561     disk->security_driver    = dumptype_get_security_driver(dtype);
562     disk->maxdumps           = dumptype_get_maxdumps(dtype);
563     disk->tape_splitsize     = dumptype_get_tape_splitsize(dtype);
564     disk->split_diskbuffer   = dumptype_get_split_diskbuffer(dtype);
565     disk->fallback_splitsize = dumptype_get_fallback_splitsize(dtype);
566     disk->maxpromoteday      = dumptype_get_maxpromoteday(dtype);
567     disk->bumppercent        = dumptype_get_bumppercent(dtype);
568     disk->bumpsize           = dumptype_get_bumpsize(dtype);
569     disk->bumpdays           = dumptype_get_bumpdays(dtype);
570     disk->bumpmult           = dumptype_get_bumpmult(dtype);
571     disk->start_t            = dumptype_get_start_t(dtype);
572     disk->strategy           = dumptype_get_strategy(dtype);
573     disk->estimate           = dumptype_get_estimate(dtype);
574     disk->compress           = dumptype_get_compress(dtype);
575     disk->srvcompprog        = dumptype_get_srvcompprog(dtype);
576     disk->clntcompprog       = dumptype_get_clntcompprog(dtype);
577     disk->encrypt            = dumptype_get_encrypt(dtype);
578     disk->srv_decrypt_opt    = dumptype_get_srv_decrypt_opt(dtype);
579     disk->clnt_decrypt_opt   = dumptype_get_clnt_decrypt_opt(dtype);
580     disk->srv_encrypt        = dumptype_get_srv_encrypt(dtype);
581     disk->clnt_encrypt       = dumptype_get_clnt_encrypt(dtype);
582     disk->amandad_path       = dumptype_get_amandad_path(dtype);
583     disk->client_username    = dumptype_get_client_username(dtype);
584     disk->ssh_keys           = dumptype_get_ssh_keys(dtype);
585     disk->comprate[0]        = dumptype_get_comprate(dtype)[0];
586     disk->comprate[1]        = dumptype_get_comprate(dtype)[1];
587
588     /*
589      * Boolean parameters with no value (Appears here as value 2) defaults
590      * to TRUE for backward compatibility and for logical consistency.
591      */
592     disk->record             = dumptype_get_record(dtype) != 0;
593     disk->skip_incr          = dumptype_get_skip_incr(dtype) != 0;
594     disk->skip_full          = dumptype_get_skip_full(dtype) != 0;
595     disk->to_holdingdisk     = dumptype_get_to_holdingdisk(dtype);
596     disk->kencrypt           = dumptype_get_kencrypt(dtype) != 0;
597     disk->index              = dumptype_get_index(dtype) != 0; 
598
599     disk->todo               = 1;
600
601     skip_whitespace(s, ch);
602     fp = s - 1;
603     if(ch && ch != '#') {               /* get optional spindle number */
604         char *fp1;
605         int is_digit=1;
606
607         skip_non_whitespace(s, ch);
608         s[-1] = '\0';
609         fp1=fp;
610         if (*fp1 == '-') fp1++;
611         for(;*fp1!='\0';fp1++) {
612             if(!isdigit((int)*fp1)) {
613                 is_digit = 0;
614             }
615         }
616         if(is_digit == 0) {
617             disk_parserror(filename, line_num, "non-integer spindle `%s'", fp);
618             amfree(hostname);
619             amfree(disk->name);
620             amfree(disk);
621             return (-1);
622         }
623         disk->spindle = atoi(fp);
624         skip_integer(s, ch);
625     }
626
627     skip_whitespace(s, ch);
628     fp = s - 1;
629     if(ch && ch != '#') {               /* get optional network interface */
630         skip_non_whitespace(s, ch);
631         s[-1] = '\0';
632         if((netif = lookup_interface(upcase(fp))) == NULL) {
633             disk_parserror(filename, line_num,
634                 "undefined network interface `%s'", fp);
635             amfree(hostname);
636             amfree(disk->name);
637             amfree(disk);
638             return (-1);
639         }
640     } else {
641         netif = lookup_interface("default");
642     }
643
644     skip_whitespace(s, ch);
645     if(ch && ch != '#') {               /* now we have garbage, ignore it */
646         disk_parserror(filename, line_num, "end of line expected");
647     }
648
649     if(dumptype_get_ignore(dtype) || dumptype_get_strategy(dtype) == DS_SKIP) {
650         amfree(hostname);
651         amfree(disk->name);
652         amfree(disk);
653         return (0);
654     }
655
656     /* success, add disk to lists */
657
658     if(host == NULL) {                  /* new host */
659         host = alloc(SIZEOF(am_host_t));
660         malloc_mark(host);
661         host->next = hostlist;
662         hostlist = host;
663
664         host->hostname = hostname;
665         hostname = NULL;
666         host->disks = NULL;
667         host->inprogress = 0;
668         host->maxdumps = 1;             /* will be overwritten */
669         host->netif = NULL;
670         host->start_t = 0;
671         host->up = NULL;
672         host->features = NULL;
673     }
674
675     host->netif = netif;
676
677     enqueue_disk(lst, disk);
678
679     disk->host = host;
680     disk->hostnext = host->disks;
681     host->disks = disk;
682     host->maxdumps = disk->maxdumps;
683
684     return (0);
685 }
686
687
688 printf_arglist_function2(void disk_parserror, const char *, filename,
689     int, line_num, const char *, format)
690 {
691     va_list argp;
692
693     /* print error message */
694
695     fprintf(stderr, "\"%s\", line %d: ", filename, line_num);
696     arglist_start(argp, format);
697     vfprintf(stderr, format, argp);
698     arglist_end(argp);
699     fputc('\n', stderr);
700 }
701
702
703 void
704 dump_queue(
705     char *      st,
706     disklist_t  q,
707     int         npr,    /* we print first npr disks on queue, plus last two */
708     FILE *      f)
709 {
710     disk_t *d,*p;
711     int pos;
712     char *qname;
713
714     if(empty(q)) {
715         fprintf(f, "%s QUEUE: empty\n", st);
716         return;
717     }
718     fprintf(f, "%s QUEUE:\n", st);
719     for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
720         qname = quote_string(d->name);
721         if(pos < npr) fprintf(f, "%3d: %-10s %-4s\n",
722                               pos, d->host->hostname, qname);
723         amfree(qname);
724     }
725     if(pos > npr) {
726         if(pos > npr+2) fprintf(f, "  ...\n");
727         if(pos > npr+1) {
728             d = p->prev;
729             fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
730         }
731         d = p;
732         fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
733     }
734 }
735
736 char *
737 optionstr(
738     disk_t *            dp,
739     am_feature_t *      their_features,
740     FILE *              fdout)
741 {
742     char *auth_opt = NULL;
743     char *kencrypt_opt = "";
744     char *compress_opt = "";
745     char *encrypt_opt = stralloc("");
746     char *decrypt_opt = stralloc("");
747     char *record_opt = "";
748     char *index_opt = "";
749     char *exclude_file = NULL;
750     char *exclude_list = NULL;
751     char *include_file = NULL;
752     char *include_list = NULL;
753     char *excl_opt = "";
754     char *incl_opt = "";
755     char *exc = NULL;
756     char *result = NULL;
757     sle_t *excl;
758     int nb_exclude_file;
759     int nb_include_file;
760     char *qdpname;
761     char *qname;
762     int err=0;
763
764     assert(dp != NULL);
765     assert(dp->host != NULL);
766
767     qdpname = quote_string(dp->name);
768     if(am_has_feature(dp->host->features, fe_options_auth)) {
769         auth_opt = vstralloc("auth=", dp->security_driver, ";", NULL);
770     } else if(strcasecmp(dp->security_driver, "bsd") == 0) {
771         if(am_has_feature(dp->host->features, fe_options_bsd_auth))
772             auth_opt = stralloc("bsd-auth;");
773         else if(fdout) {
774             fprintf(fdout,
775                     "WARNING: %s:%s does not support auth or bsd-auth\n",
776                     dp->host->hostname, qdpname);
777         }
778     } else if(strcasecmp(dp->security_driver, "krb4") == 0) {
779         if(am_has_feature(dp->host->features, fe_options_krb4_auth))
780             auth_opt = stralloc("krb4-auth;");
781         else if(fdout) {
782             fprintf(fdout,
783                     "WARNING: %s:%s does not support auth or krb4-auth\n",
784                     dp->host->hostname, qdpname);
785         }
786         if(dp->kencrypt) {
787             if(am_has_feature(dp->host->features, fe_options_kencrypt)) {
788                 kencrypt_opt = "kencrypt;";
789             }
790             else if(fdout) {
791             fprintf(fdout,
792                     "WARNING: %s:%s does not support kencrypt\n",
793                     dp->host->hostname, qdpname);
794             }
795         }
796     }
797
798     switch(dp->compress) {
799     case COMP_FAST:
800         if(am_has_feature(their_features, fe_options_compress_fast)) {
801             compress_opt = "compress-fast;";
802         }
803         else if(fdout) {
804             fprintf(fdout,
805                     "WARNING: %s:%s does not support fast compression\n",
806                     dp->host->hostname, qdpname);
807         }
808         break;
809     case COMP_BEST:
810         if(am_has_feature(their_features, fe_options_compress_best)) {
811             compress_opt = "compress-best;";
812         }
813         else if(fdout) {
814             fprintf(fdout,
815                     "WARNING: %s:%s does not support best compression\n",
816                     dp->host->hostname, qdpname);
817         }
818         break;
819     case COMP_CUST:
820         if(am_has_feature(their_features, fe_options_compress_cust)) {
821           compress_opt = vstralloc("comp-cust=", dp->clntcompprog, ";", NULL);
822           if (BSTRNCMP(compress_opt, "comp-cust=;") == 0){
823             if(fdout) {
824               fprintf(fdout,
825                       "ERROR: %s:%s client custom compression with no compression program specified\n",
826                       dp->host->hostname, qdpname);
827             }
828             err++;
829           }
830         }
831         else if(fdout) {
832             fprintf(fdout,
833                     "WARNING: %s:%s does not support client custom compression\n",
834                     dp->host->hostname, qdpname);
835         }
836         break;
837     case COMP_SERV_FAST:
838         if(am_has_feature(their_features, fe_options_srvcomp_fast)) {
839             compress_opt = "srvcomp-fast;";
840         }
841         break;
842     case COMP_SERV_BEST:
843         if(am_has_feature(their_features, fe_options_srvcomp_best)) {
844             compress_opt = "srvcomp-best;";
845         }
846         break;
847     case COMP_SERV_CUST:
848         if(am_has_feature(their_features, fe_options_srvcomp_cust)) {
849           compress_opt = vstralloc("srvcomp-cust=", dp->srvcompprog, ";", NULL);
850           if (BSTRNCMP(compress_opt, "srvcomp-cust=;") == 0){
851             if(fdout) {
852               fprintf(fdout,
853                       "ERROR: %s:%s server custom compression with no compression program specified\n",
854                       dp->host->hostname, qdpname);
855             }
856             err++;
857           }
858         }
859         else if(fdout) {
860           fprintf(fdout,
861                   "WARNING: %s:%s does not support server custom compression\n",
862                   dp->host->hostname, qdpname);
863         }
864         break;
865     }
866
867     switch(dp->encrypt) {
868     case ENCRYPT_CUST:
869       if(am_has_feature(their_features, fe_options_encrypt_cust)) {
870          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-cust=",
871                                     dp->clnt_encrypt, ";", NULL);
872          if (BSTRNCMP(encrypt_opt, "encrypt-cust=;") == 0) {
873             if(fdout) {
874               fprintf(fdout,
875                       "ERROR: %s:%s encrypt client with no encryption program specified\n",
876                       dp->host->hostname, qdpname);
877             }
878             err++;
879           }
880          if ( dp->compress == COMP_SERV_FAST || 
881               dp->compress == COMP_SERV_BEST ||
882               dp->compress == COMP_SERV_CUST ) {
883            if(fdout) {
884               fprintf(fdout,
885                       "ERROR: %s:Client encryption with server compression is not supported. See amanda.conf(5) for detail.\n", dp->host->hostname);
886             }
887             err++;
888           }
889          if(dp->clnt_decrypt_opt) {
890            if(am_has_feature(their_features, fe_options_client_decrypt_option)) {
891              decrypt_opt = newvstralloc(decrypt_opt, "client-decrypt-option=",
892                                         dp->clnt_decrypt_opt, ";", NULL);
893            }
894            else if(fdout) {
895             fprintf(fdout,
896                     "WARNING: %s:%s does not support client decrypt option\n",
897                     dp->host->hostname, qdpname);
898            }
899          }
900       }
901       else if(fdout) {
902             fprintf(fdout,
903                     "WARNING: %s:%s does not support client data encryption\n",
904                     dp->host->hostname, qdpname);
905      }
906          break;
907     case ENCRYPT_SERV_CUST:
908       if(am_has_feature(their_features, fe_options_encrypt_serv_cust)) {
909          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-serv-cust=",
910                                     dp->srv_encrypt, ";", NULL);
911          if (BSTRNCMP(encrypt_opt, "encrypt-serv-cust=;") == 0){
912             if(fdout) {
913               fprintf(fdout,
914                       "ERROR: %s:%s encrypt server with no encryption program specified\n",
915                       dp->host->hostname, qdpname);
916             }
917             err++;
918           }
919          if(dp->srv_decrypt_opt) {
920            if(am_has_feature(their_features, fe_options_server_decrypt_option)) {
921              decrypt_opt = newvstralloc(decrypt_opt, "server-decrypt-option=",
922                                         dp->srv_decrypt_opt, ";", NULL);
923            }
924            else if(fdout) {
925             fprintf(fdout,
926                     "WARNING: %s:%s does not support server decrypt option\n",
927                     dp->host->hostname, qdpname);
928            }
929          }
930       }
931       else if(fdout) {
932             fprintf(fdout,
933                     "WARNING: %s:%s does not support server data encryption\n",
934                     dp->host->hostname, qdpname);
935       }
936          break;
937     }
938     
939     if(!dp->record) {
940         if(am_has_feature(their_features, fe_options_no_record)) {
941             record_opt = "no-record;";
942         }
943         else if(fdout) {
944             fprintf(fdout, "WARNING: %s:%s does not support no record\n",
945                     dp->host->hostname, qdpname);
946         }
947     }
948
949     if(dp->index) {
950         if(am_has_feature(their_features, fe_options_index)) {
951             index_opt = "index;";
952         }
953         else if(fdout) {
954             fprintf(fdout, "WARNING: %s:%s does not support index\n",
955                     dp->host->hostname, qdpname);
956         }
957     }
958
959     if(dp->kencrypt) kencrypt_opt = "kencrypt;";
960
961
962     exclude_file = stralloc("");
963     nb_exclude_file = 0;
964     if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
965         nb_exclude_file = dp->exclude_file->nb_element;
966         if(am_has_feature(their_features, fe_options_exclude_file)) {
967             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
968                dp->exclude_file->nb_element == 1) {
969                 for(excl = dp->exclude_file->first; excl != NULL;
970                                                     excl = excl->next) {
971                     qname = quote_string(excl->name);
972                     exc = newvstralloc( exc, "exclude-file=", qname, ";", NULL);
973                     strappend(exclude_file, exc);
974                     amfree(qname);
975                 }
976             } else {
977                 qname = quote_string(dp->exclude_file->last->name);
978                 exc = newvstralloc(exc, "exclude-file=", qname, ";", NULL);
979                 strappend(exclude_file, exc);
980                 if(fdout) {
981                     fprintf(fdout,
982                        "WARNING: %s:%s does not support multiple exclude\n",
983                        dp->host->hostname, qdpname);
984                 }
985                 amfree(qname);
986             }
987         } else if(fdout) {
988             fprintf(fdout, "WARNING: %s:%s does not support exclude file\n",
989                     dp->host->hostname, qdpname);
990         }
991     }
992     exclude_list = stralloc("");
993     if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
994         if(am_has_feature(their_features, fe_options_exclude_list)) {
995             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
996                (dp->exclude_list->nb_element == 1 && nb_exclude_file == 0)) {
997                 for(excl = dp->exclude_list->first; excl != NULL;
998                                                     excl = excl->next) {
999                     qname = quote_string(excl->name);
1000                     exc = newvstralloc( exc, "exclude-list=", qname, ";", NULL);
1001                     strappend(exclude_list, exc);
1002                     amfree(qname);
1003                 }
1004             } else {
1005                 qname = quote_string(dp->exclude_list->last->name);
1006                 exc = newvstralloc(exc, "exclude-list=", qname, ";", NULL);
1007                 strappend(exclude_list, exc);
1008                 if(fdout) {
1009                         fprintf(fdout,
1010                          "WARNING: %s:%s does not support multiple exclude\n",
1011                          dp->host->hostname, qdpname);
1012                 }
1013                 amfree(qname);
1014             }
1015         } else if(fdout) {
1016             fprintf(fdout, "WARNING: %s:%s does not support exclude list\n",
1017                     dp->host->hostname, qdpname);
1018         }
1019     }
1020
1021     include_file = stralloc("");
1022     nb_include_file = 0;
1023     if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1024         nb_include_file = dp->include_file->nb_element;
1025         if(am_has_feature(their_features, fe_options_include_file)) {
1026             if(am_has_feature(their_features, fe_options_multiple_include) ||
1027                dp->include_file->nb_element == 1) {
1028                 for(excl = dp->include_file->first; excl != NULL;
1029                                                     excl = excl->next) {
1030                     qname = quote_string(excl->name);
1031                     exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1032                     strappend(include_file, exc);
1033                     amfree(qname);
1034                 }
1035             } else {
1036                 qname = quote_string(dp->include_file->last->name);
1037                 exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1038                 strappend(include_file, exc);
1039                 if(fdout) {
1040                     fprintf(fdout,
1041                          "WARNING: %s:%s does not support multiple include\n",
1042                          dp->host->hostname, qdpname);
1043                 }
1044                 amfree(qname);
1045             }
1046         } else if(fdout) {
1047             fprintf(fdout, "WARNING: %s:%s does not support include file\n",
1048                     dp->host->hostname, qdpname);
1049         }
1050     }
1051     include_list = stralloc("");
1052     if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1053         if(am_has_feature(their_features, fe_options_include_list)) {
1054             if(am_has_feature(their_features, fe_options_multiple_include) ||
1055                (dp->include_list->nb_element == 1 && nb_include_file == 0)) {
1056                 for(excl = dp->include_list->first; excl != NULL;
1057                                                     excl = excl->next) {
1058                     qname = quote_string(excl->name);
1059                     exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1060                     strappend(include_list, exc);
1061                     amfree(qname);
1062                 }
1063             } else {
1064                 qname = quote_string(dp->include_list->last->name);
1065                 exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1066                 strappend(include_list, exc);
1067                 if(fdout) {
1068                         fprintf(fdout,
1069                          "WARNING: %s:%s does not support multiple include\n",
1070                          dp->host->hostname, qdpname);
1071                 }
1072                 amfree(qname);
1073             }
1074         } else if(fdout) {
1075             fprintf(fdout, "WARNING: %s:%s does not support include list\n",
1076                     dp->host->hostname, qdpname);
1077         }
1078     }
1079
1080     if(dp->exclude_optional) {
1081         if(am_has_feature(their_features, fe_options_optional_exclude)) {
1082             excl_opt = "exclude-optional;";
1083         }
1084         else if(fdout) {
1085             fprintf(fdout,
1086                     "WARNING: %s:%s does not support optional exclude\n",
1087                     dp->host->hostname, qdpname);
1088         }
1089     }
1090     if(dp->include_optional) {
1091         if(am_has_feature(their_features, fe_options_optional_include)) {
1092            incl_opt = "include-optional;";
1093         }
1094         else if(fdout) {
1095             fprintf(fdout,
1096                     "WARNING: %s:%s does not support optional include\n",
1097                     dp->host->hostname, qdpname);
1098         }
1099     }
1100
1101     result = vstralloc(";",
1102                        auth_opt,
1103                        kencrypt_opt,
1104                        compress_opt,
1105                        encrypt_opt,
1106                        decrypt_opt,
1107                        record_opt,
1108                        index_opt,
1109                        exclude_file,
1110                        exclude_list,
1111                        include_file,
1112                        include_list,
1113                        excl_opt,
1114                        incl_opt,
1115                        NULL);
1116     amfree(qdpname);
1117     amfree(auth_opt);
1118     amfree(exclude_list);
1119     amfree(exclude_file);
1120     amfree(include_file);
1121     amfree(include_list);
1122     amfree(exc);
1123     amfree(decrypt_opt);
1124     amfree(encrypt_opt);
1125
1126     /* result contains at least 'auth=...' */
1127     if ( err ) {
1128         amfree(result);
1129         return NULL;
1130     } else {
1131         return result;
1132     }
1133 }
1134
1135  
1136 char *
1137 match_disklist(
1138     disklist_t *origqp,
1139     int         sargc,
1140     char **     sargv)
1141 {
1142     char *prevhost = NULL;
1143     char *errstr = NULL;
1144     int i;
1145     int match_a_host;
1146     int match_a_disk;
1147     int prev_match;
1148     disk_t *dp;
1149
1150     if(sargc <= 0)
1151         return NULL;
1152
1153     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1154         if(dp->todo == 1)
1155             dp->todo = -1;
1156     }
1157
1158     prev_match = 0;
1159     for(i=0;i<sargc;i++) {
1160         match_a_host = 0;
1161         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1162             if(match_host(sargv[i], dp->host->hostname))
1163                 match_a_host = 1;
1164         }
1165         match_a_disk = 0;
1166         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1167             if(prevhost != NULL &&
1168                match_host(prevhost, dp->host->hostname) &&
1169                (match_disk(sargv[i], dp->name) ||
1170                 (dp->device && match_disk(sargv[i], dp->device)))) {
1171                 if(match_a_host) {
1172                     error("Argument %s match a host and a disk",sargv[i]);
1173                     /*NOTREACHED*/
1174                 }
1175                 else {
1176                     if(dp->todo == -1) {
1177                         dp->todo = 1;
1178                         match_a_disk = 1;
1179                         prev_match = 0;
1180                     }
1181                 }
1182             }
1183         }
1184         if(!match_a_disk) {
1185             if(match_a_host == 1) {
1186                 if(prev_match == 1) { /* all disk of the previous host */
1187                     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1188                         if(match_host(prevhost,dp->host->hostname))
1189                             if(dp->todo == -1)
1190                                 dp->todo = 1;
1191                     }
1192                 }
1193                 prevhost = sargv[i];
1194                 prev_match = 1;
1195             }
1196             else {
1197                 vstrextend(&errstr, "Argument '", sargv[i], "' match neither a host nor a disk.\n", NULL);
1198             }
1199         }
1200     }
1201
1202     if(prev_match == 1) { /* all disk of the previous host */
1203         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1204             if(match_host(prevhost,dp->host->hostname))
1205                 if(dp->todo == -1)
1206                     dp->todo = 1;
1207         }
1208     }
1209
1210     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1211         if(dp->todo == -1)
1212             dp->todo = 0;
1213     }
1214
1215     return errstr;
1216 }
1217
1218
1219 #ifdef TEST
1220
1221 static void dump_disk(const disk_t *);
1222 static void dump_disklist(const disklist_t *);
1223 int main(int, char *[]);
1224
1225 static void
1226 dump_disk(
1227     const disk_t *      dp)
1228 {
1229     printf("  DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n",
1230            dp->name, dp->host->hostname, dp->line, dp->dtype_name,
1231            dp->name == NULL? "(null)": dp->name,
1232            dp->spindle);
1233 }
1234
1235 static void
1236 dump_disklist(
1237     const disklist_t *  lst)
1238 {
1239     const disk_t *dp, *prev;
1240     const am_host_t *hp;
1241
1242     if(hostlist == NULL) {
1243         printf("DISKLIST not read in\n");
1244         return;
1245     }
1246
1247     printf("DISKLIST BY HOSTNAME:\n");
1248
1249     for(hp = hostlist; hp != NULL; hp = hp->next) {
1250         printf("HOST %s INTERFACE %s\n",
1251                hp->hostname,
1252                (hp->netif == NULL||hp->netif->name == NULL) ? "(null)"
1253                                                             : hp->netif->name);
1254         for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
1255             dump_disk(dp);
1256         putchar('\n');
1257     }
1258
1259
1260     printf("DISKLIST IN FILE ORDER:\n");
1261
1262     prev = NULL;
1263     for(dp = lst->head; dp != NULL; prev = dp, dp = dp->next) {
1264         dump_disk(dp);
1265         /* check pointers */
1266         if(dp->prev != prev) printf("*** prev pointer mismatch!\n");
1267         if(dp->next == NULL && lst->tail != dp) printf("tail mismatch!\n");
1268     }
1269 }
1270
1271 int
1272 main(
1273     int         argc,
1274     char **     argv)
1275 {
1276   char *conffile;
1277   char *conf_diskfile;
1278   disklist_t lst;
1279   int result;
1280   unsigned long malloc_hist_1, malloc_size_1;
1281   unsigned long malloc_hist_2, malloc_size_2;
1282
1283   safe_fd(-1, 0);
1284
1285   set_pname("diskfile");
1286
1287   dbopen(DBG_SUBDIR_SERVER);
1288
1289   /* Don't die when child closes pipe */
1290   signal(SIGPIPE, SIG_IGN);
1291
1292   malloc_size_1 = malloc_inuse(&malloc_hist_1);
1293
1294   if (argc>1) {
1295     config_name = argv[1];
1296     if (strchr(config_name, '/') != NULL) {
1297       config_dir = stralloc2(argv[1], "/");
1298       config_name = strrchr(config_name, '/') + 1;
1299     } else {
1300       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1301     }
1302   } else {
1303     config_dir = stralloc("");
1304   }
1305   conffile = stralloc2(config_dir, CONFFILE_NAME);
1306   if((result = read_conffile(conffile)) == 0) {
1307     conf_diskfile = getconf_str(CNF_DISKFILE);
1308     if (*conf_diskfile == '/') {
1309       conf_diskfile = stralloc(conf_diskfile);
1310     } else {
1311       conf_diskfile = stralloc2(config_dir, conf_diskfile);
1312     }
1313     result = read_diskfile(conf_diskfile, &lst);
1314     if(result == 0) {
1315       dump_disklist(&lst);
1316     }
1317     amfree(conf_diskfile);
1318   }
1319   amfree(conffile);
1320   amfree(config_dir);
1321
1322   malloc_size_2 = malloc_inuse(&malloc_hist_2);
1323
1324   if(malloc_size_1 != malloc_size_2) {
1325     malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1326   }
1327
1328   return result;
1329 }
1330 #endif /* TEST */