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