Imported Upstream version 2.4.4p3
[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 2003/12/16 22:36:45 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 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 host_t *lookup_host(hostname)
76 char *hostname;
77 {
78     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     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     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(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     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->start_t       = dtype->start_t;
425     disk->strategy      = dtype->strategy;
426     disk->compress      = dtype->compress;
427     disk->comprate[0]   = dtype->comprate[0];
428     disk->comprate[1]   = dtype->comprate[1];
429     disk->record        = dtype->record;
430     disk->skip_incr     = dtype->skip_incr;
431     disk->skip_full     = dtype->skip_full;
432     disk->no_hold       = dtype->no_hold;
433     disk->kencrypt      = dtype->kencrypt;
434     disk->index         = dtype->index;
435     disk->todo          = 1;
436
437     skip_whitespace(s, ch);
438     fp = s - 1;
439     if(ch && ch != '#') {               /* get optional spindle number */
440         disk->spindle = atoi(fp);
441         skip_integer(s, ch);
442     }
443
444     skip_whitespace(s, ch);
445     fp = s - 1;
446     if(ch && ch != '#') {               /* get optional network interface */
447         skip_non_whitespace(s, ch);
448         s[-1] = '\0';
449         if((netif = lookup_interface(upcase(fp))) == NULL) {
450             parserror("undefined network interface `%s'", fp);
451             if(host == NULL) amfree(hostname);
452             amfree(disk->name);
453             amfree(disk);
454             return 1;
455         }
456     } else {
457         netif = lookup_interface("");
458     }
459
460     skip_whitespace(s, ch);
461     if(ch && ch != '#') {               /* now we have garbage, ignore it */
462         parserror("end of line expected");
463     }
464
465     if(dtype->ignore || dtype->strategy == DS_SKIP) {
466         amfree(diskname);
467         free_sl(disk->exclude_file);
468         free_sl(disk->exclude_list);
469         free_sl(disk->include_file);
470         free_sl(disk->include_list);
471         amfree(disk);
472         return 1;
473     }
474
475     /* success, add disk to lists */
476
477     if(host == NULL) {                  /* new host */
478         host = alloc(sizeof(host_t));
479         malloc_mark(host);
480         host->next = hostlist;
481         hostlist = host;
482
483         host->hostname = hostname;
484         hostname = NULL;
485         host->disks = NULL;
486         host->inprogress = 0;
487         host->maxdumps = 1;             /* will be overwritten */
488         host->netif = NULL;
489         host->start_t = 0;
490         host->up = NULL;
491         host->features = NULL;
492     }
493
494     host->netif = netif;
495
496     enqueue_disk(&lst, disk);
497
498     disk->host = host;
499     disk->hostnext = host->disks;
500     host->disks = disk;
501     host->maxdumps = disk->maxdumps;
502
503     return 1;
504 }
505
506
507 printf_arglist_function(static void parserror, char *, format)
508 {
509     va_list argp;
510
511     /* print error message */
512
513     fprintf(stderr, "\"%s\", line %d: ", diskfname, line_num);
514     arglist_start(argp, format);
515     vfprintf(stderr, format, argp);
516     arglist_end(argp);
517     fputc('\n', stderr);
518
519     got_parserror = 1;
520 }
521
522
523 void dump_queue(st, q, npr, f)
524 char *st;
525 disklist_t q;
526 int npr;        /* we print first npr disks on queue, plus last two */
527 FILE *f;
528 {
529     disk_t *d,*p;
530     int pos;
531
532     if(empty(q)) {
533         fprintf(f, "%s QUEUE: empty\n", st);
534         return;
535     }
536     fprintf(f, "%s QUEUE:\n", st);
537     for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
538         if(pos < npr) fprintf(f, "%3d: %-10s %-4s\n",
539                               pos, d->host->hostname, d->name);
540     }
541     if(pos > npr) {
542         if(pos > npr+2) fprintf(f, "  ...\n");
543         if(pos > npr+1) {
544             d = p->prev;
545             fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
546         }
547         d = p;
548         fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
549     }
550 }
551
552 char *optionstr(dp, their_features, fdout)
553 disk_t *dp;
554 am_feature_t * their_features;
555 FILE *fdout;
556 {
557     char *auth_opt = NULL;
558     char *kencrypt_opt = "";
559     char *compress_opt = "";
560     char *record_opt = "";
561     char *index_opt = "";
562     char *exclude_file = NULL;
563     char *exclude_list = NULL;
564     char *include_file = NULL;
565     char *include_list = NULL;
566     char *excl_opt = "";
567     char *incl_opt = "";
568     char *exc = NULL;
569     char *result = NULL;
570     sle_t *excl;
571     int nb_exclude_file;
572     int nb_include_file;
573
574     /* modification by BIS@BBN 4/25/2003:
575      * The first "if" statement has a number of problems, so I removed the
576      * am_has_feature(dp->host->features, fe_options_auth)
577      * condition and caused the first case to always fail.
578      * 1) If dp->host is a NULL pointer, subsequent tests which look
579      *    at dp->host->features or dp->host->hostname will cause a dump.
580      *    It appears that there should be an assertion that dp->host
581      *    is NOT NULL.
582      * 2) The code which checks for the kencrypt feature is only executed
583      *    in the case where dp->auth == AUTH_KRB4.  If we enable Kerberos IV
584      *    in the first case, then the code checking for kencrypt will never
585      *    be executed.
586      * 3) The option processing code in server-src/dumper.c and
587      *    client-src/sendbackup.c do not yet handle the "auth=krb4"
588      *    option style of specifying Kerberos IV.
589      * Getting rid of the whole first case seems to take care of problems
590      * 2 and 3, but not problem 1.
591      */
592     if(dp->host && 0)
593        /* && am_has_feature(dp->host->features, fe_options_auth)) */ {
594         auth_opt = stralloc("auth=");
595         if(dp->auth == AUTH_BSD) {
596             strappend(auth_opt, "bsd");
597         } else if(dp->auth == AUTH_KRB4) {
598             strappend(auth_opt, "krb4");
599         } else {
600             strappend(auth_opt, "unknown");
601         }
602         strappend(auth_opt, ";");
603     } else if(dp->auth == AUTH_BSD) {
604         if(am_has_feature(dp->host->features, fe_options_bsd_auth)) {
605             auth_opt = stralloc("bsd-auth;");
606         }
607         else if(fdout) {
608             fprintf(fdout, "WARNING: %s:%s does not support bsd-auth\n",
609                     dp->host->hostname, dp->name);
610         }
611     } else if(dp->auth == AUTH_KRB4) {
612         if(am_has_feature(dp->host->features, fe_options_krb4_auth)) {
613             auth_opt = stralloc("krb4-auth;");
614         }
615         else if(fdout) {
616             fprintf(fdout, "WARNING: %s:%s does not support krb4-auth\n",
617                     dp->host->hostname, dp->name);
618         }
619         if(dp->kencrypt) {
620             if(am_has_feature(dp->host->features, fe_options_kencrypt)) {
621                 kencrypt_opt = "kencrypt;";
622             }
623             else if(fdout) {
624                 fprintf(fdout,
625                         "WARNING: %s:%s does not support kencrypt\n",
626                         dp->host->hostname, dp->name);
627             }
628         }
629     }
630
631     switch(dp->compress) {
632     case COMP_FAST:
633         if(am_has_feature(their_features, fe_options_compress_fast)) {
634             compress_opt = "compress-fast;";
635         }
636         else if(fdout) {
637             fprintf(fdout,
638                     "WARNING: %s:%s does not support fast compression\n",
639                     dp->host->hostname, dp->name);
640         }
641         break;
642     case COMP_BEST:
643         if(am_has_feature(their_features, fe_options_compress_best)) {
644             compress_opt = "compress-best;";
645         }
646         else if(fdout) {
647             fprintf(fdout,
648                     "WARNING: %s:%s does not support best compression\n",
649                     dp->host->hostname, dp->name);
650         }
651         break;
652     case COMP_SERV_FAST:
653         if(am_has_feature(their_features, fe_options_srvcomp_fast)) {
654             compress_opt = "srvcomp-fast;";
655         }
656         break;
657     case COMP_SERV_BEST:
658         if(am_has_feature(their_features, fe_options_srvcomp_best)) {
659             compress_opt = "srvcomp-best;";
660         }
661         break;
662     }
663
664     if(!dp->record) {
665         if(am_has_feature(their_features, fe_options_no_record)) {
666             record_opt = "no-record;";
667         }
668         else if(fdout) {
669             fprintf(fdout, "WARNING: %s:%s does not support no record\n",
670                     dp->host->hostname, dp->name);
671         }
672     }
673
674     if(dp->index) {
675         if(am_has_feature(their_features, fe_options_index)) {
676             index_opt = "index;";
677         }
678         else if(fdout) {
679             fprintf(fdout, "WARNING: %s:%s does not support index\n",
680                     dp->host->hostname, dp->name);
681         }
682     }
683
684     exclude_file = stralloc("");
685     nb_exclude_file = 0;
686     if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
687         nb_exclude_file = dp->exclude_file->nb_element;
688         if(am_has_feature(their_features, fe_options_exclude_file)) {
689             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
690                dp->exclude_file->nb_element == 1) {
691                 for(excl = dp->exclude_file->first; excl != NULL;
692                                                     excl = excl->next) {
693                     exc = newvstralloc( exc, "exclude-file=", excl->name,
694                                         ";", NULL);
695                     strappend(exclude_file, exc);
696                 }
697             } else {
698                 exc = newvstralloc(exc, "exclude-file=",
699                                    dp->exclude_file->last->name, ";", NULL);
700                 strappend(exclude_file, exc);
701                 if(fdout) {
702                     fprintf(fdout,
703                        "WARNING: %s:%s does not support multiple exclude\n",
704                        dp->host->hostname, dp->name);
705                 }
706             }
707         } else if(fdout) {
708             fprintf(fdout, "WARNING: %s:%s does not support exclude file\n",
709                     dp->host->hostname, dp->name);
710         }
711     }
712     exclude_list = stralloc("");
713     if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
714         if(am_has_feature(their_features, fe_options_exclude_list)) {
715             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
716                (dp->exclude_list->nb_element == 1 && nb_exclude_file == 0)) {
717                 for(excl = dp->exclude_list->first; excl != NULL;
718                                                     excl = excl->next) {
719                     exc = newvstralloc( exc, "exclude-list=", excl->name,
720                                         ";", NULL);
721                     strappend(exclude_list, exc);
722                 }
723             } else {
724                 exc = newvstralloc(exc, "exclude-list=",
725                                    dp->exclude_list->last->name, ";", NULL);
726                 strappend(exclude_list, exc);
727                 if(fdout) {
728                         fprintf(fdout,
729                          "WARNING: %s:%s does not support multiple exclude\n",
730                          dp->host->hostname, dp->name);
731                 }
732             }
733         } else if(fdout) {
734             fprintf(fdout, "WARNING: %s:%s does not support exclude list\n",
735                     dp->host->hostname, dp->name);
736         }
737     }
738
739     include_file = stralloc("");
740     nb_include_file = 0;
741     if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
742         nb_include_file = dp->include_file->nb_element;
743         if(am_has_feature(their_features, fe_options_include_file)) {
744             if(am_has_feature(their_features, fe_options_multiple_include) ||
745                dp->include_file->nb_element == 1) {
746                 for(excl = dp->include_file->first; excl != NULL;
747                                                     excl = excl->next) {
748                     exc = newvstralloc( exc, "include-file=", excl->name,
749                                         ";", NULL);
750                     strappend(include_file, exc);
751                 }
752             } else {
753                 exc = newvstralloc(exc, "include-file=",
754                                    dp->include_file->last->name, ";", NULL);
755                 strappend(include_file, exc);
756                 if(fdout) {
757                     fprintf(fdout,
758                          "WARNING: %s:%s does not support multiple include\n",
759                          dp->host->hostname, dp->name);
760                 }
761             }
762         } else if(fdout) {
763             fprintf(fdout, "WARNING: %s:%s does not support include file\n",
764                     dp->host->hostname, dp->name);
765         }
766     }
767     include_list = stralloc("");
768     if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
769         if(am_has_feature(their_features, fe_options_include_list)) {
770             if(am_has_feature(their_features, fe_options_multiple_include) ||
771                (dp->include_list->nb_element == 1 && nb_include_file == 0)) {
772                 for(excl = dp->include_list->first; excl != NULL;
773                                                     excl = excl->next) {
774                     exc = newvstralloc( exc, "include-list=", excl->name,
775                                         ";", NULL);
776                     strappend(include_list, exc);
777                 }
778             } else {
779                 exc = newvstralloc(exc, "include-list=",
780                                    dp->include_list->last->name, ";", NULL);
781                 strappend(include_list, exc);
782                 if(fdout) {
783                         fprintf(fdout,
784                          "WARNING: %s:%s does not support multiple include\n",
785                          dp->host->hostname, dp->name);
786                 }
787             }
788         } else if(fdout) {
789             fprintf(fdout, "WARNING: %s:%s does not support include list\n",
790                     dp->host->hostname, dp->name);
791         }
792     }
793
794     if(dp->exclude_optional) {
795         if(am_has_feature(their_features, fe_options_optional_exclude)) {
796             excl_opt = "exclude-optional;";
797         }
798         else if(fdout) {
799             fprintf(fdout,
800                     "WARNING: %s:%s does not support optional exclude\n",
801                     dp->host->hostname, dp->name);
802         }
803     }
804     if(dp->include_optional) {
805         if(am_has_feature(their_features, fe_options_optional_include)) {
806            incl_opt = "include-optional;";
807         }
808         else if(fdout) {
809             fprintf(fdout,
810                     "WARNING: %s:%s does not support optional include\n",
811                     dp->host->hostname, dp->name);
812         }
813     }
814
815     result = vstralloc(";",
816                        auth_opt,
817                        kencrypt_opt,
818                        compress_opt,
819                        record_opt,
820                        index_opt,
821                        exclude_file,
822                        exclude_list,
823                        include_file,
824                        include_list,
825                        excl_opt,
826                        incl_opt,
827                        NULL);
828     amfree(auth_opt);
829     amfree(exclude_file);
830     amfree(exclude_list);
831     amfree(include_file);
832     amfree(include_list);
833     amfree(exc);
834
835     return result;
836 }
837
838  
839 void match_disklist(disklist_t *origqp, int sargc, char **sargv)
840 {
841     char *prevhost = NULL;
842     int i;
843     int match_a_host;
844     int match_a_disk;
845     int prev_match;
846     disk_t *dp;
847
848     if(sargc <= 0)
849         return;
850
851     for(dp = origqp->head; dp != NULL; dp = dp->next) {
852         if(dp->todo == 1)
853             dp->todo = -1;
854     }
855
856     prev_match = 0;
857     for(i=0;i<sargc;i++) {
858         match_a_host = 0;
859         for(dp = origqp->head; dp != NULL; dp = dp->next) {
860             if(match_host(sargv[i], dp->host->hostname))
861                 match_a_host = 1;
862         }
863         match_a_disk = 0;
864         for(dp = origqp->head; dp != NULL; dp = dp->next) {
865             if(prevhost != NULL &&
866                match_host(prevhost, dp->host->hostname) &&
867                (match_disk(sargv[i], dp->name) ||
868                 (dp->device && match_disk(sargv[i], dp->device)))) {
869                 if(match_a_host) {
870                     error("Argument %s match a host and a disk",sargv[i]);
871                 }
872                 else {
873                     if(dp->todo == -1) {
874                         dp->todo = 1;
875                         match_a_disk = 1;
876                         prev_match = 0;
877                     }
878                 }
879             }
880         }
881         if(!match_a_disk) {
882             if(match_a_host == 1) {
883                 if(prev_match == 1) { /* all disk of the previous host */
884                     for(dp = origqp->head; dp != NULL; dp = dp->next) {
885                         if(match_host(prevhost,dp->host->hostname))
886                             if(dp->todo == -1)
887                                 dp->todo = 1;
888                     }
889                 }
890                 prevhost = sargv[i];
891                 prev_match = 1;
892             }
893             else {
894                 prev_match = 0;
895                 /*error("%s match nothing",sargv[i]);*/
896             }
897         }
898     }
899
900     if(prev_match == 1) { /* all disk of the previous host */
901         for(dp = origqp->head; dp != NULL; dp = dp->next) {
902             if(match_host(prevhost,dp->host->hostname))
903                 if(dp->todo == -1)
904                     dp->todo = 1;
905         }
906     }
907
908     for(dp = origqp->head; dp != NULL; dp = dp->next) {
909         if(dp->todo == -1)
910             dp->todo = 0;
911     }
912 }
913
914  
915 #ifdef TEST
916
917 void
918 dump_disk(dp)
919 disk_t *dp;
920 {
921     printf("  DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n",
922            dp->name, dp->host->hostname, dp->line, dp->dtype_name,
923            dp->name == NULL? "(null)": dp->name,
924            dp->spindle);
925 }
926
927 void
928 dump_disklist()
929 {
930     disk_t *dp, *prev;
931     host_t *hp;
932
933     if(hostlist == NULL) {
934         printf("DISKLIST not read in\n");
935         return;
936     }
937
938     printf("DISKLIST BY HOSTNAME:\n");
939
940     for(hp = hostlist; hp != NULL; hp = hp->next) {
941         printf("HOST %s INTERFACE %s\n",
942                hp->hostname,
943                (hp->netif == NULL||hp->netif->name == NULL) ? "(null)"
944                                                             : hp->netif->name);
945         for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
946             dump_disk(dp);
947         putchar('\n');
948     }
949
950
951     printf("DISKLIST IN FILE ORDER:\n");
952
953     prev = NULL;
954     for(dp = lst.head; dp != NULL; prev = dp, dp = dp->next) {
955         dump_disk(dp);
956         /* check pointers */
957         if(dp->prev != prev) printf("*** prev pointer mismatch!\n");
958         if(dp->next == NULL && lst.tail != dp) printf("tail mismatch!\n");
959     }
960 }
961
962 int
963 main(argc, argv)
964 int argc;
965 char *argv[];
966 {
967   char *conffile;
968   char *conf_diskfile;
969   int result;
970   int fd;
971   unsigned long malloc_hist_1, malloc_size_1;
972   unsigned long malloc_hist_2, malloc_size_2;
973
974   for(fd = 3; fd < FD_SETSIZE; fd++) {
975     /*
976      * Make sure nobody spoofs us with a lot of extra open files
977      * that would cause an open we do to get a very high file
978      * descriptor, which in turn might be used as an index into
979      * an array (e.g. an fd_set).
980      */
981     close(fd);
982   }
983
984   set_pname("diskfile");
985
986   malloc_size_1 = malloc_inuse(&malloc_hist_1);
987
988   if (argc>1) {
989     config_name = argv[1];
990     if (strchr(config_name, '/') != NULL) {
991       config_dir = stralloc2(argv[1], "/");
992       config_name = strrchr(config_name, '/') + 1;
993     } else {
994       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
995     }
996   } else {
997     config_dir = stralloc("");
998   }
999   conffile = stralloc2(config_dir, CONFFILE_NAME);
1000   if((result = read_conffile(conffile)) == 0) {
1001     conf_diskfile = getconf_str(CNF_DISKFILE);
1002     if (*conf_diskfile == '/') {
1003       conf_diskfile = stralloc(conf_diskfile);
1004     } else {
1005       conf_diskfile = stralloc2(config_dir, conf_diskfile);
1006     }
1007     if((result = (read_diskfile(conf_diskfile) == NULL)) == 0) {
1008       dump_disklist();
1009     }
1010     amfree(conf_diskfile);
1011   }
1012   amfree(conffile);
1013   amfree(config_dir);
1014
1015   malloc_size_2 = malloc_inuse(&malloc_hist_2);
1016
1017   if(malloc_size_1 != malloc_size_2) {
1018     malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1019   }
1020
1021   return result;
1022 }
1023
1024 #endif /* TEST */