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