Imported Upstream version 3.3.1
[debian/amanda] / server-src / diskfile.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: diskfile.c,v 1.95 2006/07/26 15:17:37 martinea Exp $
29  *
30  * read disklist file
31  */
32 #include "amanda.h"
33 #include "match.h"
34 #include "arglist.h"
35 #include "conffile.h"
36 #include "diskfile.h"
37 #include "util.h"
38 #include "amxml.h"
39
40 static am_host_t *hostlist;
41 static netif_t *all_netifs;
42
43 /* local functions */
44 static char *upcase(char *st);
45 static int parse_diskline(disklist_t *, const char *, FILE *, int *, char **);
46 static void disk_parserror(const char *, int, const char *, ...)
47                             G_GNUC_PRINTF(3, 4);
48
49
50 cfgerr_level_t
51 read_diskfile(
52     const char *filename,
53     disklist_t *lst)
54 {
55     FILE *diskf;
56     int line_num;
57     char *line = NULL;
58
59     /* initialize */
60     hostlist = NULL;
61     lst->head = lst->tail = NULL;
62     line_num = 0;
63
64     /* if we already have config errors, then don't bother */
65     if (config_errors(NULL) >= CFGERR_ERRORS) {
66         return config_errors(NULL);
67     }
68
69     if ((diskf = fopen(filename, "r")) == NULL) {
70         config_add_error(CFGERR_ERRORS,
71             vstrallocf(_("Could not open '%s': %s"), filename, strerror(errno)));
72         goto end;
73         /*NOTREACHED*/
74     }
75
76     while ((line = agets(diskf)) != NULL) {
77         line_num++;
78         if (line[0] != '\0') {
79             if (parse_diskline(lst, filename, diskf, &line_num, &line) < 0) {
80                 goto end;
81             }
82         }
83         amfree(line);
84     }
85
86 end:
87     amfree(line);
88     afclose(diskf);
89     return config_errors(NULL);
90 }
91
92 am_host_t *
93 get_hostlist(void)
94 {
95     return hostlist;
96 }
97
98 am_host_t *
99 lookup_host(
100     const char *hostname)
101 {
102     am_host_t *p;
103
104     for (p = hostlist; p != NULL; p = p->next) {
105         if(strcasecmp(p->hostname, hostname) == 0) return p;
106     }
107     return (NULL);
108 }
109
110 disk_t *
111 lookup_disk(
112     const char *hostname,
113     const char *diskname)
114 {
115     am_host_t *host;
116     disk_t *disk;
117
118     host = lookup_host(hostname);
119     if (host == NULL)
120         return (NULL);
121
122     for (disk = host->disks; disk != NULL; disk = disk->hostnext) {
123         if (strcmp(disk->name, diskname) == 0)
124             return (disk);
125     }
126     return (NULL);
127 }
128
129
130 /*
131  * put disk on end of queue
132  */
133
134 void
135 enqueue_disk(
136     disklist_t *list,
137     disk_t *    disk)
138 {
139     if(list->tail == NULL) list->head = disk;
140     else list->tail->next = disk;
141     disk->prev = list->tail;
142
143     list->tail = disk;
144     disk->next = NULL;
145 }
146
147
148 /*
149  * put disk on head of queue
150  */
151
152 void
153 headqueue_disk(
154     disklist_t *list,
155     disk_t *    disk)
156 {
157     if(list->head == NULL) list->tail = disk;
158     else list->head->prev = disk;
159     disk->next = list->head;
160
161     list->head = disk;
162     disk->prev = NULL;
163 }
164
165
166 /*
167  * insert in sorted order
168  */
169
170 void
171 insert_disk(
172     disklist_t *list,
173     disk_t *    disk,
174     int         (*cmp)(disk_t *a, disk_t *b))
175 {
176     disk_t *prev, *ptr;
177
178     prev = NULL;
179     ptr = list->head;
180
181     while(ptr != NULL) {
182         if(cmp(disk, ptr) < 0) break;
183         prev = ptr;
184         ptr = ptr->next;
185     }
186     disk->next = ptr;
187     disk->prev = prev;
188
189     if(prev == NULL) list->head = disk;
190     else prev->next = disk;
191     if(ptr == NULL) list->tail = disk;
192     else ptr->prev = disk;
193 }
194
195 disk_t *
196 add_disk(
197     disklist_t *list,
198     char *      hostname,
199     char *      diskname)
200 {
201     disk_t *disk;
202     am_host_t *host;
203
204     disk = alloc(SIZEOF(disk_t));
205     bzero(disk, SIZEOF(disk_t));
206     disk->line = 0;
207     disk->allow_split = 0;
208     disk->splitsize = (off_t)0;
209     disk->tape_splitsize = (off_t)0;
210     disk->split_diskbuffer = NULL;
211     disk->fallback_splitsize = (off_t)0;
212     disk->hostname = stralloc(hostname);
213     disk->name = stralloc(diskname);
214     disk->device = stralloc(diskname);
215     disk->spindle = -1;
216     disk->up = NULL;
217     disk->compress = COMP_NONE;
218     disk->encrypt  = ENCRYPT_NONE;
219     disk->start_t = 0;
220     disk->todo = 1;
221     disk->index = 1;
222     disk->exclude_list = NULL;
223     disk->exclude_file = NULL;
224     disk->include_list = NULL;
225     disk->include_file = NULL;
226     disk->application = NULL;
227     disk->pp_scriptlist = NULL;
228
229     host = lookup_host(hostname);
230     if(host == NULL) {
231         host = alloc(SIZEOF(am_host_t));
232         host->next = hostlist;
233         hostlist = host;
234
235         host->hostname = stralloc(hostname);
236         host->disks = NULL;
237         host->inprogress = 0;
238         host->maxdumps = 1;
239         host->netif = NULL;
240         host->start_t = 0;
241         host->up = NULL;
242         host->features = NULL;
243         host->pre_script = 0;
244         host->post_script = 0;
245     }
246     enqueue_disk(list, disk);
247
248     disk->host = host;
249     disk->hostnext = host->disks;
250     host->disks = disk;
251
252     return disk;
253 }
254
255
256 /*
257  * check if disk is present in list. Return true if so, false otherwise.
258  */
259
260 int
261 find_disk(
262     disklist_t *list,
263     disk_t *    disk)
264 {
265     disk_t *t;
266
267     t = list->head;
268     while ((t != NULL) && (t != disk)) {
269         t = t->next;
270     }
271     return (t == disk);
272 }
273
274
275 /*
276  * sort a whole queue
277  */
278
279 void
280 sort_disk(
281     disklist_t *in,
282     disklist_t *out,
283     int         (*cmp)(disk_t *a, disk_t *b))
284 {
285     disklist_t *tmp;
286     disk_t *disk;
287
288     tmp = in;           /* just in case in == out */
289
290     out->head = (disk_t *)0;
291     out->tail = (disk_t *)0;
292
293     while((disk = dequeue_disk(tmp)))
294         insert_disk(out, disk, cmp);
295 }
296
297
298 /*
299  * remove disk from front of queue
300  */
301
302 disk_t *
303 dequeue_disk(
304     disklist_t *list)
305 {
306     disk_t *disk;
307
308     if(list->head == NULL) return NULL;
309
310     disk = list->head;
311     list->head = disk->next;
312
313     if(list->head == NULL) list->tail = NULL;
314     else list->head->prev = NULL;
315
316     disk->prev = disk->next = NULL;     /* for debugging */
317     return disk;
318 }
319
320 void
321 remove_disk(
322     disklist_t *list,
323     disk_t *    disk)
324 {
325     if(disk->prev == NULL) list->head = disk->next;
326     else disk->prev->next = disk->next;
327
328     if(disk->next == NULL) list->tail = disk->prev;
329     else disk->next->prev = disk->prev;
330
331     disk->prev = disk->next = NULL;
332 }
333
334 void
335 free_disklist(
336     disklist_t* dl)
337 {
338     disk_t    *dp;
339     am_host_t *host, *hostnext;
340     netif_t *netif, *next_if;
341
342     while (dl->head != NULL) {
343         dp = dequeue_disk(dl);
344         amfree(dp->filename);
345         amfree(dp->name);
346         amfree(dp->hostname);
347         amfree(dp->device);
348         free_sl(dp->exclude_file);
349         free_sl(dp->exclude_list);
350         free_sl(dp->include_file);
351         free_sl(dp->include_list);
352         free(dp);
353     }
354
355     for(host=hostlist; host != NULL; host = hostnext) {
356         amfree(host->hostname);
357         am_release_feature_set(host->features);
358         host->features = NULL;
359         hostnext = host->next;
360         amfree(host);
361     }
362     hostlist=NULL;
363
364     for (netif = all_netifs; netif != NULL; netif = next_if) {
365         next_if = netif->next;
366         amfree(netif);
367     }
368     all_netifs = NULL;
369 }
370
371 static char *
372 upcase(
373     char *st)
374 {
375     char *s = st;
376
377     while(*s) {
378         if(islower((int)*s)) *s = (char)toupper((int)*s);
379         s++;
380     }
381     return st;
382 }
383
384
385 /* return  0 on success */
386 /* return -1 on error   */
387 static int
388 parse_diskline(
389     disklist_t *lst,
390     const char *filename,
391     FILE *      diskf,
392     int *       line_num_p,
393     /*@keep@*/ char **  line_p)
394 {
395     am_host_t *host;
396     disk_t *disk;
397     dumptype_t *dtype;
398     netif_t *netif = NULL;
399     interface_t *cfg_if = NULL;
400     char *hostname = NULL;
401     char *diskname, *diskdevice;
402     char *dumptype;
403     char *s, *fp;
404     int ch, dup = 0;
405     char *line = *line_p;
406     int line_num = *line_num_p;
407     struct tm *stm;
408     time_t st;
409     char *shost, *sdisk;
410     am_host_t *p;
411     disk_t *dp;
412     identlist_t pp_iter;
413
414     assert(filename != NULL);
415     assert(line_num > 0);
416     assert(line != NULL);
417
418     s = line;
419     ch = *s++;
420     skip_whitespace(s, ch);
421     if(ch == '\0' || ch == '#')
422         return (0);
423
424     fp = s - 1;
425     skip_non_whitespace(s, ch);
426     s[-1] = '\0';
427     if (g_str_equal(fp, "includefile")) {
428         char *include_name;
429         char *include_filename;
430         skip_whitespace(s, ch);
431         if (ch == '\0' || ch == '#') {
432             disk_parserror(filename, line_num, _("include filename name expected"));
433             return (-1);
434         }
435         fp = s - 1;
436         skip_quoted_string(s, ch);
437         s[-1] = '\0';
438         include_name = unquote_string(fp);
439         include_filename = config_dir_relative(include_name);
440         read_diskfile(include_filename, lst);
441         g_free(include_filename);
442         g_free(include_name);
443         if (config_errors(NULL) >= CFGERR_WARNINGS) {
444             return -1;
445         } else {
446             return 0;
447         }
448     }
449     host = lookup_host(fp);
450     if (host == NULL) {
451         hostname = stralloc(fp);
452     } else {
453         hostname = stralloc(host->hostname);
454         if (strcmp(host->hostname, fp) != 0) {
455             disk_parserror(filename, line_num, _("Same host with different case: \"%s\" and \"%s\"."), host->hostname, fp);
456             return -1;
457         }
458     }
459
460     shost = sanitise_filename(hostname);
461     for (p = hostlist; p != NULL; p = p->next) {
462         char *shostp = sanitise_filename(p->hostname);
463         if (strcmp(hostname, p->hostname) &&
464             !strcmp(shost, shostp)) {
465             disk_parserror(filename, line_num, _("Two hosts are mapping to the same name: \"%s\" and \"%s\""), p->hostname, hostname);
466             return(-1);
467         }
468         else if (strcasecmp(hostname, p->hostname) &&
469                  match_host(hostname, p->hostname) &&
470                  match_host(p->hostname, hostname)) {
471             disk_parserror(filename, line_num, _("Duplicate host name: \"%s\" and \"%s\""), p->hostname, hostname);
472             return(-1);
473         }
474         amfree(shostp);
475     }
476     amfree(shost);
477
478     skip_whitespace(s, ch);
479     if(ch == '\0' || ch == '#') {
480         disk_parserror(filename, line_num, _("disk device name expected"));
481         amfree(hostname);
482         return (-1);
483     }
484
485     fp = s - 1;
486     skip_quoted_string(s, ch);
487     s[-1] = '\0';
488     diskname = unquote_string(fp);
489     if (strlen(diskname) == 0) {
490         disk_parserror(filename, line_num, _("invalid empty diskname"));
491         amfree(hostname);
492         return (-1);
493     }
494     skip_whitespace(s, ch);
495     if(ch == '\0' || ch == '#') {
496         disk_parserror(filename, line_num, _("disk dumptype expected"));
497         amfree(hostname);
498         amfree(diskname);
499         return (-1);
500     }
501     fp = s - 1;
502     skip_quoted_string(s, ch);
503     s[-1] = '\0';
504
505     /* diskdevice */
506     dumptype = NULL;
507     diskdevice = NULL;
508     if(fp[0] != '{') {
509         dumptype = unquote_string(fp);
510         if (strlen(dumptype) == 0) {
511             disk_parserror(filename, line_num, _("invalid empty diskdevice"));
512             amfree(hostname);
513             return (-1);
514         }
515         if ((dtype = lookup_dumptype(dumptype)) == NULL) {
516             diskdevice = dumptype;
517             skip_whitespace(s, ch);
518             if(ch == '\0' || ch == '#') {
519                 disk_parserror(filename, line_num,
520                         _("disk dumptype '%s' not found"), dumptype);
521                 amfree(hostname);
522                 amfree(diskdevice);
523                 amfree(diskname);
524                 return (-1);
525             }
526
527             fp = s - 1;
528             skip_quoted_string(s, ch);
529             s[-1] = '\0';
530             if (fp[0] != '{') {
531                 dumptype = unquote_string(fp);
532             }
533         }
534     }
535
536     /* check for duplicate disk */
537     disk = NULL;
538     if (host) {
539         if ((disk = lookup_disk(hostname, diskname)) != NULL) {
540             dup = 1;
541         } else {
542             disk = host->disks;
543             do {
544                 char *a1, *a2;
545                 a1 = clean_regex(diskname, 1);
546                 a2 = clean_regex(disk->name, 1);
547
548                 if (match_disk(a1, disk->name) && match_disk(a2, diskname)) {
549                     dup = 1;
550                 } else {
551                     disk = disk->hostnext;
552                 }
553                 amfree(a1);
554                 amfree(a2);
555             }
556             while (dup == 0 && disk != NULL);
557         }
558         if (dup == 1) {
559             disk_parserror(filename, line_num,
560                            _("duplicate disk record, previous on line %d"),
561                            disk->line);
562         }
563     }
564     if (!disk) {
565         disk = alloc(SIZEOF(disk_t));
566         disk->filename = g_strdup(filename);
567         disk->line = line_num;
568         disk->hostname = hostname;
569         disk->name = diskname;
570         disk->device = diskdevice;
571         disk->spindle = -1;
572         disk->up = NULL;
573         disk->inprogress = 0;
574         disk->application = NULL;
575         disk->pp_scriptlist = NULL;
576         disk->dataport_list = NULL;
577     }
578
579     if (host) {
580         sdisk = sanitise_filename(diskname);
581         for (dp = host->disks; dp != NULL; dp = dp->hostnext) {
582             char *sdiskp = sanitise_filename(dp->name);
583             if (strcmp(diskname, dp->name) != 0 &&
584                  strcmp(sdisk, sdiskp) == 0) {
585                 disk_parserror(filename, line_num,
586                  _("Two disks are mapping to the same name: \"%s\" and \"%s\"; you must use different diskname"),
587                  dp->name, diskname);
588             return(-1);
589             }
590             amfree(sdiskp);
591         }
592         amfree(sdisk);
593     }
594
595     if (fp[0] == '{') {
596         s[-1] = (char)ch;
597         s = fp+2;
598         skip_whitespace(s, ch);
599         if (ch != '\0' && ch != '#') {
600             disk_parserror(filename, line_num,
601                       _("expected line break after `{\', ignoring rest of line"));
602         }
603
604         if (strchr(s-1, '}') &&
605             (strchr(s-1, '#') == NULL ||
606              strchr(s-1, '}') < strchr(s-1, '#'))) {
607             disk_parserror(filename, line_num,_("'}' on same line than '{'"));
608             amfree(hostname);
609             if(!dup) {
610                 amfree(disk->device);
611                 amfree(disk->name);
612                 amfree(disk);
613             } else {
614                 amfree(diskdevice);
615                 amfree(diskname);
616             }
617             return (-1);
618         }
619         dtype = read_dumptype(vstralloc("custom(", hostname,
620                                         ":", disk->name, ")",
621                                         ".", anonymous_value(), NULL),
622                               diskf, (char*)filename, line_num_p);
623         if (dtype == NULL || dup) {
624             disk_parserror(filename, line_num,
625                            _("read of custom dumptype failed"));
626             amfree(hostname);
627             if(!dup) {
628                 amfree(disk->device);
629                 amfree(disk->name);
630                 amfree(disk);
631             } else {
632                 amfree(diskdevice);
633                 amfree(diskname);
634             }
635             return (-1);
636         }
637         amfree(line);
638
639         *line_p = line = agets(diskf);
640         line_num = *line_num_p; /* no incr, read_dumptype did it already */
641
642         if (line == NULL)
643             *line_p = line = stralloc("");
644         s = line;
645         ch = *s++;
646     } else {
647         if((dtype = lookup_dumptype(dumptype)) == NULL) {
648             char *qdt = quote_string(dumptype);
649
650             disk_parserror(filename, line_num, _("undefined dumptype `%s'"), qdt);
651             amfree(qdt);
652             amfree(hostname);
653             if (!dup) {
654                 amfree(disk->device);
655                 amfree(disk->name);
656                 amfree(disk);
657             } else {
658                 amfree(diskdevice);
659                 amfree(diskname);
660             }
661             return (-1);
662         }
663         amfree(dumptype);
664     }
665
666     if (dup) {
667         /* disk_parserror already called, above */
668         g_assert(config_errors(NULL) != CFGERR_OK);
669         amfree(hostname);
670         amfree(diskdevice);
671         amfree(diskname);
672         return (-1);
673     }
674
675     disk->dtype_name         = dumptype_name(dtype);
676     disk->config             = dtype;
677     disk->program            = dumptype_get_program(dtype);
678     disk->exclude_list     = duplicate_sl(dumptype_get_exclude(dtype).sl_list);
679     disk->exclude_file     = duplicate_sl(dumptype_get_exclude(dtype).sl_file);
680     disk->exclude_optional   = dumptype_get_exclude(dtype).optional;
681     disk->include_list     = duplicate_sl(dumptype_get_include(dtype).sl_list);
682     disk->include_file     = duplicate_sl(dumptype_get_include(dtype).sl_file);
683     disk->include_optional   = dumptype_get_include(dtype).optional;
684     disk->priority           = dumptype_get_priority(dtype);
685     disk->dumpcycle          = dumptype_get_dumpcycle(dtype);
686 /*    disk->frequency        = dumptype_get_frequency(dtype);*/
687     disk->auth               = dumptype_get_auth(dtype);
688     disk->maxdumps           = dumptype_get_maxdumps(dtype);
689     disk->allow_split        = dumptype_get_allow_split(dtype);
690     disk->tape_splitsize     = dumptype_get_tape_splitsize(dtype);
691     disk->split_diskbuffer   = dumptype_get_split_diskbuffer(dtype);
692     disk->fallback_splitsize = dumptype_get_fallback_splitsize(dtype);
693     if (disk->allow_split) {
694         tapetype_t *tapetype = lookup_tapetype(getconf_str(CNF_TAPETYPE));
695         disk->splitsize = tapetype_get_part_size(tapetype);
696     } else {
697         disk->splitsize = disk->tape_splitsize;
698     }
699     disk->maxpromoteday      = dumptype_get_maxpromoteday(dtype);
700     disk->bumppercent        = dumptype_get_bumppercent(dtype);
701     disk->bumpsize           = dumptype_get_bumpsize(dtype);
702     disk->bumpdays           = dumptype_get_bumpdays(dtype);
703     disk->bumpmult           = dumptype_get_bumpmult(dtype);
704     disk->starttime          = dumptype_get_starttime(dtype);
705     disk->application        = dumptype_get_application(dtype);
706     disk->pp_scriptlist      = dumptype_get_scriptlist(dtype);
707     disk->start_t = 0;
708     if (disk->starttime > 0) {
709         st = time(NULL);
710         disk->start_t = st;
711         stm = localtime(&st);
712         disk->start_t -= stm->tm_sec + 60 * stm->tm_min + 3600 * stm->tm_hour;
713         disk->start_t += disk->starttime / 100 * 3600 +
714                          disk->starttime % 100 * 60;
715         if ((disk->start_t - st) < -43200)
716             disk->start_t += 86400;
717     }
718     disk->strategy           = dumptype_get_strategy(dtype);
719     disk->ignore             = dumptype_get_ignore(dtype);
720     disk->estimatelist       = dumptype_get_estimatelist(dtype);
721     disk->compress           = dumptype_get_compress(dtype);
722     disk->srvcompprog        = dumptype_get_srvcompprog(dtype);
723     disk->clntcompprog       = dumptype_get_clntcompprog(dtype);
724     disk->encrypt            = dumptype_get_encrypt(dtype);
725     disk->srv_decrypt_opt    = dumptype_get_srv_decrypt_opt(dtype);
726     disk->clnt_decrypt_opt   = dumptype_get_clnt_decrypt_opt(dtype);
727     disk->srv_encrypt        = dumptype_get_srv_encrypt(dtype);
728     disk->clnt_encrypt       = dumptype_get_clnt_encrypt(dtype);
729     disk->amandad_path       = dumptype_get_amandad_path(dtype);
730     disk->client_username    = dumptype_get_client_username(dtype);
731     disk->client_port        = dumptype_get_client_port(dtype);
732     disk->ssh_keys           = dumptype_get_ssh_keys(dtype);
733     disk->comprate[0]        = dumptype_get_comprate(dtype)[0];
734     disk->comprate[1]        = dumptype_get_comprate(dtype)[1];
735     disk->data_path          = dumptype_get_data_path(dtype);
736     disk->dump_limit         = dumptype_get_dump_limit(dtype);
737
738     /*
739      * Boolean parameters with no value (Appears here as value 2) defaults
740      * to TRUE for backward compatibility and for logical consistency.
741      */
742     disk->record             = dumptype_get_record(dtype) != 0;
743     disk->skip_incr          = dumptype_get_skip_incr(dtype) != 0;
744     disk->skip_full          = dumptype_get_skip_full(dtype) != 0;
745     disk->to_holdingdisk     = dumptype_get_to_holdingdisk(dtype);
746     disk->kencrypt           = dumptype_get_kencrypt(dtype) != 0;
747     disk->index              = dumptype_get_index(dtype) != 0; 
748
749     disk->todo               = 1;
750
751     skip_whitespace(s, ch);
752     fp = s - 1;
753     if(ch && ch != '#') {               /* get optional spindle number */
754         char *fp1;
755         int is_digit=1;
756
757         skip_non_whitespace(s, ch);
758         s[-1] = '\0';
759         fp1=fp;
760         if (*fp1 == '-') fp1++;
761         for(;*fp1!='\0';fp1++) {
762             if(!isdigit((int)*fp1)) {
763                 is_digit = 0;
764             }
765         }
766         if(is_digit == 0) {
767             disk_parserror(filename, line_num, _("non-integer spindle `%s'"), fp);
768             amfree(hostname);
769             amfree(disk->name);
770             amfree(disk);
771             return (-1);
772         }
773         disk->spindle = atoi(fp);
774         skip_integer(s, ch);
775     }
776
777     skip_whitespace(s, ch);
778     fp = s - 1;
779     if(ch && ch != '#') {               /* get optional network interface */
780         skip_non_whitespace(s, ch);
781         s[-1] = '\0';
782         if((cfg_if = lookup_interface(upcase(fp))) == NULL) {
783             disk_parserror(filename, line_num,
784                 _("undefined network interface `%s'"), fp);
785             amfree(hostname);
786             amfree(disk->name);
787             amfree(disk);
788             return (-1);
789         }
790     } else {
791         cfg_if = lookup_interface("default");
792     }
793
794     /* see if we already have a netif_t for this interface */
795     for (netif = all_netifs; netif != NULL; netif = netif->next) {
796         if (netif->config == cfg_if)
797             break;
798     }
799
800     /* nope; make up a new one */
801     if (!netif) {
802         netif = alloc(sizeof(*netif));
803         netif->next = all_netifs;
804         all_netifs = netif;
805         netif->config = cfg_if;
806         netif->curusage = 0;
807     }
808
809     skip_whitespace(s, ch);
810     if(ch && ch != '#') {               /* now we have garbage, ignore it */
811         disk_parserror(filename, line_num, _("end of line expected"));
812     }
813
814     if (disk->program && disk->application &&
815         strcmp(disk->program,"APPLICATION")) {
816         disk_parserror(filename, line_num,
817                        _("Both program and application set"));
818     }
819
820     if (disk->program && strcmp(disk->program,"APPLICATION")==0 &&
821         !disk->application) {
822         disk_parserror(filename, line_num,
823                        _("program set to APPLICATION but no application set"));
824     }
825
826     if (disk->application) {
827         application_t *application;
828         char          *plugin;
829
830         application = lookup_application(disk->application);
831         g_assert(application != NULL);
832         plugin = application_get_plugin(application);
833         if (!plugin || strlen(plugin) == 0) {
834             disk_parserror(filename, line_num,
835                            _("plugin not set for application"));
836         }
837     }
838
839     for (pp_iter = disk->pp_scriptlist; pp_iter != NULL;
840          pp_iter = pp_iter->next) {
841         pp_script_t *pp_script;
842         char        *plugin;
843         char        *pp_script_name;
844
845         pp_script_name = (char*)pp_iter->data;
846         pp_script = lookup_pp_script(pp_script_name);
847         g_assert(pp_script != NULL);
848         plugin = pp_script_get_plugin(pp_script);
849         if (!plugin || strlen(plugin) == 0) {
850             disk_parserror(filename, line_num, _("plugin not set for script"));
851         }
852     }
853
854     /* success, add disk to lists */
855
856     if(host == NULL) {                  /* new host */
857         host = alloc(SIZEOF(am_host_t));
858         host->next = hostlist;
859         hostlist = host;
860
861         host->hostname = stralloc(hostname);
862         hostname = NULL;
863         host->disks = NULL;
864         host->inprogress = 0;
865         host->maxdumps = 1;             /* will be overwritten */
866         host->netif = NULL;
867         host->start_t = 0;
868         host->up = NULL;
869         host->features = NULL;
870         host->pre_script = 0;
871         host->post_script = 0;
872     }
873
874     host->netif = netif;
875
876     enqueue_disk(lst, disk);
877
878     disk->host = host;
879     disk->hostnext = host->disks;
880     host->disks = disk;
881     host->maxdumps = disk->maxdumps;
882
883     return (0);
884 }
885
886
887 printf_arglist_function2(void disk_parserror, const char *, filename,
888     int, line_num, const char *, format)
889 {
890     va_list argp;
891     char * msg;
892     char * errstr;
893
894     /* format the error message and hand it off to conffile */
895
896     arglist_start(argp, format);
897     msg = g_strdup_vprintf(format, argp);
898     errstr = g_strdup_printf("\"%s\", line %d: %s", filename, line_num, msg);
899     amfree(msg);
900     arglist_end(argp);
901
902     config_add_error(CFGERR_ERRORS, errstr);
903 }
904
905
906 void
907 dump_queue(
908     char *      st,
909     disklist_t  q,
910     int         npr,    /* we print first npr disks on queue, plus last two */
911     FILE *      f)
912 {
913     disk_t *d,*p;
914     int pos;
915     char *qname;
916
917     if(empty(q)) {
918         g_fprintf(f, _("%s QUEUE: empty\n"), st);
919         return;
920     }
921     g_fprintf(f, _("%s QUEUE:\n"), st);
922     for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
923         qname = quote_string(d->name);
924         if(pos < npr) g_fprintf(f, "%3d: %-10s %-4s\n",
925                               pos, d->host->hostname, qname);
926         amfree(qname);
927     }
928     if(pos > npr) {
929         if(pos > npr+2) g_fprintf(f, "  ...\n");
930         if(pos > npr+1) {
931             d = p->prev;
932             g_fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
933         }
934         d = p;
935         g_fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
936     }
937 }
938
939 GPtrArray *
940 validate_optionstr(
941     disk_t       *dp)
942 {
943     GPtrArray *errarray;
944     int        nb_exclude;
945     int        nb_include;
946     am_feature_t *their_features = dp->host->features;
947
948     assert(dp != NULL);
949     assert(dp->host != NULL);
950
951     errarray = g_ptr_array_new();
952
953     if (!am_has_feature(their_features, fe_options_auth)) {
954         if (strcasecmp(dp->auth, "bsd") == 0)
955             if (!am_has_feature(their_features, fe_options_bsd_auth))
956                 g_ptr_array_add(errarray, _("does not support auth"));
957     }
958
959     switch(dp->compress) {
960     case COMP_FAST:
961         if (!am_has_feature(their_features, fe_options_compress_fast)) {
962             g_ptr_array_add(errarray, _("does not support fast compression"));
963         }
964         break;
965     case COMP_BEST:
966         if (!am_has_feature(their_features, fe_options_compress_best)) {
967             g_ptr_array_add(errarray, _("does not support best compression"));
968         }
969         break;
970     case COMP_CUST:
971         if (am_has_feature(their_features, fe_options_compress_cust)) {
972             if (dp->clntcompprog == NULL || strlen(dp->clntcompprog) == 0) {
973                 g_ptr_array_add(errarray, _("client custom compression with no compression program specified"));
974             }
975         } else {
976             g_ptr_array_add(errarray, _("does not support client custom compression"));
977         }
978         break;
979     case COMP_SERVER_FAST:
980         break;
981     case COMP_SERVER_BEST:
982         break;
983     case COMP_SERVER_CUST:
984         if (dp->srvcompprog == NULL || strlen(dp->srvcompprog) == 0) {
985             g_ptr_array_add(errarray, _("server custom compression with no compression program specified"));
986         }
987         break;
988     }
989
990     switch(dp->encrypt) {
991     case ENCRYPT_CUST:
992         if (am_has_feature(their_features, fe_options_encrypt_cust)) {
993             if (dp->clnt_decrypt_opt) {
994                 if (!am_has_feature(their_features, fe_options_client_decrypt_option)) {
995                     g_ptr_array_add(errarray, _("does not support client decrypt option"));
996                 }
997             }
998             if (dp->clnt_encrypt == NULL || strlen(dp->clnt_encrypt) == 0) {
999                 g_ptr_array_add(errarray, _("encrypt client with no encryption program specified"));
1000             }
1001             if (dp->compress == COMP_SERVER_FAST ||
1002                 dp->compress == COMP_SERVER_BEST ||
1003                 dp->compress == COMP_SERVER_CUST ) {
1004                 g_ptr_array_add(errarray, _("Client encryption with server compression is not supported. See amanda.conf(5) for detail"));
1005             }
1006         } else {
1007             g_ptr_array_add(errarray, _("does not support client data encryption"));
1008         }
1009         break;
1010     case ENCRYPT_SERV_CUST:
1011         if (dp->srv_encrypt == NULL || strlen(dp->srv_encrypt) == 0) {
1012             g_ptr_array_add(errarray, _("No encryption program specified in dumptypes, Change the dumptype in the disklist or mention the encryption program to use in the dumptypes file"));
1013         }
1014         break;
1015     }
1016
1017     if (!dp->record) {
1018         if (!am_has_feature(their_features, fe_options_no_record)) {
1019             g_ptr_array_add(errarray, _("does not support no record"));
1020         }
1021     }
1022
1023     if (dp->index) {
1024         if (!am_has_feature(their_features, fe_options_index)) {
1025             g_ptr_array_add(errarray, _("does not support index"));
1026         }
1027     }
1028
1029     if (dp->kencrypt) {
1030         if (!am_has_feature(their_features, fe_options_kencrypt)) {
1031             g_ptr_array_add(errarray, _("does not support kencrypt"));
1032         }
1033     }
1034
1035     nb_exclude = 0;
1036     if (dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1037         nb_exclude = dp->exclude_file->nb_element;
1038         if (!am_has_feature(their_features, fe_options_exclude_file)) {
1039             g_ptr_array_add(errarray, _("does not support exclude file"));
1040         }
1041     }
1042
1043     if (dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1044         nb_exclude += dp->exclude_list->nb_element;
1045         if (!am_has_feature(their_features, fe_options_exclude_list)) {
1046             g_ptr_array_add(errarray, _("does not support exclude list"));
1047         }
1048     }
1049
1050     if (nb_exclude > 1 &&
1051         !am_has_feature(their_features, fe_options_multiple_exclude)) {
1052         g_ptr_array_add(errarray, _("does not support multiple exclude"));
1053     }
1054
1055     nb_include = 0;
1056     if (dp->include_file != NULL && dp->include_file->nb_element > 0) {
1057         nb_include = dp->include_file->nb_element;
1058         if (!am_has_feature(their_features, fe_options_include_file)) {
1059             g_ptr_array_add(errarray, ("does not support include file"));
1060         }
1061     }
1062
1063     if (dp->include_list != NULL && dp->include_list->nb_element > 0) {
1064         nb_include += dp->include_list->nb_element;
1065         if (!am_has_feature(their_features, fe_options_include_list)) {
1066             g_ptr_array_add(errarray, _("does not support include list"));
1067         }
1068     }
1069
1070     if (nb_include > 1 &&
1071         !am_has_feature(their_features, fe_options_multiple_exclude)) {
1072         g_ptr_array_add(errarray, _("does not support multiple include"));
1073     }
1074
1075     if (dp->exclude_optional) {
1076         if (!am_has_feature(their_features, fe_options_optional_exclude)) {
1077             g_ptr_array_add(errarray, _("does not support optional exclude"));
1078         }
1079     }
1080     if (dp->include_optional) {
1081         if (!am_has_feature(their_features, fe_options_optional_include)) {
1082             g_ptr_array_add(errarray, _("does not support optional include"));
1083         }
1084     }
1085
1086     return errarray;
1087 }
1088
1089 char *
1090 optionstr(
1091     disk_t *    dp)
1092 {
1093     char *auth_opt = NULL;
1094     char *kencrypt_opt = "";
1095     char *compress_opt = "";
1096     char *encrypt_opt = stralloc("");
1097     char *decrypt_opt = stralloc("");
1098     char *record_opt = "";
1099     char *index_opt = "";
1100     char *exclude_file = NULL;
1101     char *exclude_list = NULL;
1102     char *include_file = NULL;
1103     char *include_list = NULL;
1104     char *excl_opt = "";
1105     char *incl_opt = "";
1106     char *exc = NULL;
1107     char *result = NULL;
1108     sle_t *excl;
1109     char *qdpname;
1110     char *qname;
1111     am_feature_t *their_features = dp->host->features;
1112
1113     assert(dp != NULL);
1114     assert(dp->host != NULL);
1115
1116     qdpname = quote_string(dp->name);
1117     if (am_has_feature(their_features, fe_options_auth)) {
1118         auth_opt = vstralloc("auth=", dp->auth, ";", NULL);
1119     } else if(strcasecmp(dp->auth, "bsd") == 0) {
1120         if(am_has_feature(their_features, fe_options_bsd_auth))
1121             auth_opt = stralloc("bsd-auth;");
1122     }
1123
1124     switch(dp->compress) {
1125     case COMP_FAST:
1126         compress_opt = "compress-fast;";
1127         break;
1128     case COMP_BEST:
1129         compress_opt = "compress-best;";
1130         break;
1131     case COMP_CUST:
1132         compress_opt = vstralloc("comp-cust=", dp->clntcompprog, ";", NULL);
1133         break;
1134     case COMP_SERVER_FAST:
1135         compress_opt = "srvcomp-fast;";
1136         break;
1137     case COMP_SERVER_BEST:
1138         compress_opt = "srvcomp-best;";
1139         break;
1140     case COMP_SERVER_CUST:
1141         compress_opt = vstralloc("srvcomp-cust=", dp->srvcompprog, ";", NULL);
1142         break;
1143     }
1144
1145     switch(dp->encrypt) {
1146     case ENCRYPT_CUST:
1147         encrypt_opt = newvstralloc(encrypt_opt, "encrypt-cust=",
1148                                    dp->clnt_encrypt, ";", NULL);
1149         if (dp->clnt_decrypt_opt) {
1150              decrypt_opt = newvstralloc(decrypt_opt, "client-decrypt-option=",
1151                                         dp->clnt_decrypt_opt, ";", NULL);
1152         }
1153         break;
1154     case ENCRYPT_SERV_CUST:
1155         encrypt_opt = newvstralloc(encrypt_opt, "encrypt-serv-cust=",
1156                                    dp->srv_encrypt, ";", NULL);
1157         if (dp->srv_decrypt_opt) {
1158             decrypt_opt = newvstralloc(decrypt_opt, "server-decrypt-option=",
1159                                        dp->srv_decrypt_opt, ";", NULL);
1160          }
1161          break;
1162     }
1163
1164     if (!dp->record) {
1165         record_opt = "no-record;";
1166     }
1167
1168     if (dp->index) {
1169         index_opt = "index;";
1170     }
1171
1172     if (dp->kencrypt) {
1173         kencrypt_opt = "kencrypt;";
1174     }
1175
1176     exclude_file = stralloc("");
1177     if (dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1178         for(excl = dp->exclude_file->first; excl != NULL;
1179                                             excl = excl->next) {
1180             qname = quote_string(excl->name);
1181             exc = newvstralloc( exc, "exclude-file=", qname, ";", NULL);
1182             strappend(exclude_file, exc);
1183             amfree(qname);
1184         }
1185     }
1186     exclude_list = stralloc("");
1187     if (dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1188         for(excl = dp->exclude_list->first; excl != NULL;
1189                                             excl = excl->next) {
1190             qname = quote_string(excl->name);
1191             exc = newvstralloc( exc, "exclude-list=", qname, ";", NULL);
1192             strappend(exclude_list, exc);
1193             amfree(qname);
1194         }
1195     }
1196
1197     include_file = stralloc("");
1198     if (dp->include_file != NULL && dp->include_file->nb_element > 0) {
1199         for(excl = dp->include_file->first; excl != NULL;
1200                                             excl = excl->next) {
1201             qname = quote_string(excl->name);
1202             exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1203             strappend(include_file, exc);
1204             amfree(qname);
1205         }
1206     }
1207     include_list = stralloc("");
1208     if (dp->include_list != NULL && dp->include_list->nb_element > 0) {
1209         for(excl = dp->include_list->first; excl != NULL;
1210                                             excl = excl->next) {
1211             qname = quote_string(excl->name);
1212             exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1213             strappend(include_list, exc);
1214             amfree(qname);
1215         }
1216     }
1217
1218     if (dp->exclude_optional) {
1219         excl_opt = "exclude-optional;";
1220     }
1221     if (dp->include_optional) {
1222         incl_opt = "include-optional;";
1223     }
1224
1225     result = vstralloc(";",
1226                        auth_opt,
1227                        kencrypt_opt,
1228                        compress_opt,
1229                        encrypt_opt,
1230                        decrypt_opt,
1231                        record_opt,
1232                        index_opt,
1233                        exclude_file,
1234                        exclude_list,
1235                        include_file,
1236                        include_list,
1237                        excl_opt,
1238                        incl_opt,
1239                        NULL);
1240     amfree(qdpname);
1241     amfree(auth_opt);
1242     amfree(exclude_list);
1243     amfree(exclude_file);
1244     amfree(include_file);
1245     amfree(include_list);
1246     amfree(exc);
1247     amfree(decrypt_opt);
1248     amfree(encrypt_opt);
1249
1250     /* result contains at least 'auth=...' */
1251     return result;
1252 }
1253
1254 typedef struct {
1255     am_feature_t  *features;
1256     char          *result;
1257 } xml_app_t;
1258
1259 /* A GHFunc (callback for g_hash_table_foreach) */
1260 static void xml_property(
1261     gpointer key_p,
1262     gpointer value_p,
1263     gpointer user_data_p)
1264 {
1265     char       *tmp;
1266     property_t *property = value_p;
1267     xml_app_t  *xml_app = user_data_p;
1268     GSList     *value;
1269     GString    *strbuf;
1270
1271     strbuf = g_string_new(xml_app->result);
1272
1273     tmp = amxml_format_tag("name", (char *)key_p);
1274     g_string_append_printf(strbuf, "    <property>\n      %s\n", tmp);
1275     g_free(tmp);
1276
1277     // TODO if client have fe_xml_property_priority
1278     if (property->priority
1279         && am_has_feature(xml_app->features, fe_xml_property_priority))
1280         g_string_append(strbuf, "      <priority>yes</priority>\n");
1281
1282     for (value = property->values; value != NULL; value = value->next) {
1283         tmp = amxml_format_tag("value", value->data);
1284         g_string_append_printf(strbuf, "      %s", tmp);
1285         g_free(tmp);
1286     }
1287     g_string_append_printf(strbuf, "\n    </property>\n");
1288
1289     g_free(xml_app->result);
1290     xml_app->result = g_string_free(strbuf, FALSE);
1291 }
1292
1293 char *
1294 xml_optionstr(
1295     disk_t *            dp,
1296     int                 to_server)
1297 {
1298     char *auth_opt;
1299     char *kencrypt_opt;
1300     char *compress_opt;
1301     char *encrypt_opt = stralloc("");
1302     char *decrypt_opt = stralloc("");
1303     char *record_opt;
1304     char *index_opt;
1305     char *data_path_opt = stralloc("");
1306     char *exclude = stralloc("");
1307     char *exclude_file = NULL;
1308     char *exclude_list = NULL;
1309     char *include = stralloc("");
1310     char *include_file = NULL;
1311     char *include_list = NULL;
1312     char *excl_opt = "";
1313     char *incl_opt = "";
1314     char *exc = NULL;
1315     char *script_opt;
1316     char *result = NULL;
1317     sle_t *excl;
1318     char *qdpname;
1319     char *q64name;
1320     am_feature_t *their_features = dp->host->features;
1321
1322     assert(dp != NULL);
1323     assert(dp->host != NULL);
1324
1325     qdpname = quote_string(dp->name);
1326     if (am_has_feature(their_features, fe_options_auth)) {
1327         auth_opt = vstralloc("  <auth>", dp->auth, "</auth>\n", NULL);
1328     } else {
1329         auth_opt = stralloc("");
1330     }
1331
1332     switch(dp->compress) {
1333     case COMP_FAST:
1334         compress_opt = stralloc("  <compress>FAST</compress>\n");
1335         break;
1336     case COMP_BEST:
1337         compress_opt = stralloc("  <compress>BEST</compress>\n");
1338         break;
1339     case COMP_CUST:
1340         compress_opt = vstralloc("  <compress>CUSTOM"
1341                                  "<custom-compress-program>",
1342                                  dp->clntcompprog,
1343                                  "</custom-compress-program>\n"
1344                                  "  </compress>\n", NULL);
1345         break;
1346     case COMP_SERVER_FAST:
1347         compress_opt = stralloc("  <compress>SERVER-FAST</compress>\n");
1348         break;
1349     case COMP_SERVER_BEST:
1350         compress_opt = stralloc("  <compress>SERVER-BEST</compress>\n");
1351         break;
1352     case COMP_SERVER_CUST:
1353         compress_opt = vstralloc("  <compress>SERVER-CUSTOM"
1354                                  "<custom-compress-program>",
1355                                  dp->srvcompprog,
1356                                  "</custom-compress-program>\n"
1357                                  "  </compress>\n", NULL);
1358         break;
1359     default:
1360         compress_opt = stralloc("");
1361     }
1362
1363     switch(dp->encrypt) {
1364     case ENCRYPT_CUST:
1365         if (dp->clnt_decrypt_opt) {
1366             decrypt_opt = newvstralloc(decrypt_opt,
1367                                        "    <decrypt-option>",
1368                                        dp->clnt_decrypt_opt,
1369                                        "</decrypt-option>\n", NULL);
1370             }
1371         if (decrypt_opt) {
1372              encrypt_opt = newvstralloc(encrypt_opt,
1373                                         "  <encrypt>CUSTOM"
1374                                         "<custom-encrypt-program>",
1375                                         dp->clnt_encrypt,
1376                                         "</custom-encrypt-program>\n",
1377                                         decrypt_opt,
1378                                         "  </encrypt>\n", NULL);
1379         }
1380         break;
1381     case ENCRYPT_SERV_CUST:
1382         if (to_server) {
1383             decrypt_opt =  newvstralloc(decrypt_opt,
1384                                         "    <decrypt-option>",
1385                                         dp->srv_decrypt_opt, 
1386                                         "</decrypt-option>\n", NULL);
1387             encrypt_opt = newvstralloc(encrypt_opt,
1388                                        "  <encrypt>SERVER-CUSTOM"
1389                                        "<custom-encrypt-program>",
1390                                        dp->srv_encrypt,
1391                                        "</custom-encrypt-program>\n",
1392                                        decrypt_opt,
1393                                        "  </encrypt>\n", NULL);
1394         }
1395         break;
1396     }
1397     
1398     if (!dp->record) {
1399         record_opt = "  <record>NO</record>\n";
1400     } else {
1401         record_opt = "  <record>YES</record>\n";
1402     }
1403
1404     if(dp->index) {
1405         index_opt = "  <index>YES</index>\n";
1406     } else {
1407         index_opt = "";
1408     }
1409
1410     if (dp->kencrypt) {
1411         kencrypt_opt = "  <kencrypt>YES</kencrypt>\n";
1412     } else {
1413         kencrypt_opt = "";
1414     }
1415
1416     if (am_has_feature(their_features, fe_xml_data_path)) {
1417         switch(dp->data_path) {
1418         case DATA_PATH_AMANDA:
1419             amfree(data_path_opt);
1420             data_path_opt = stralloc("  <datapath>AMANDA</datapath>\n");
1421             break;
1422         case DATA_PATH_DIRECTTCP:
1423           { /* dp->dataport_list is not set for selfcheck/sendsize */
1424             if (am_has_feature(their_features, fe_xml_directtcp_list)) {
1425                 char *s, *sc;
1426                 char *value, *b64value;
1427
1428                 amfree(data_path_opt);
1429                 data_path_opt = stralloc("  <datapath>DIRECTTCP");
1430                 if (dp->dataport_list) {
1431                     s = sc = stralloc(dp->dataport_list);
1432                     do {
1433                         value = s;
1434                         s = strchr(s, ';');
1435                         if (s) {
1436                             *s++ = '\0';
1437                         }
1438
1439                         b64value = amxml_format_tag("directtcp", value);
1440                         vstrextend(&data_path_opt, "\n    ", b64value, NULL);
1441                         amfree(b64value);
1442                     } while (s);
1443                     amfree(sc);
1444                     strappend(data_path_opt, "\n  ");
1445                 }
1446                 strappend(data_path_opt, "</datapath>\n");
1447             }
1448           }
1449           break;
1450         }
1451     }
1452
1453     exclude_file = stralloc("");
1454     if (dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1455         for(excl = dp->exclude_file->first; excl != NULL;
1456                                             excl = excl->next) {
1457             q64name = amxml_format_tag("file", excl->name);
1458             exc = newvstralloc( exc, "    ", q64name, "\n", NULL);
1459             strappend(exclude_file, exc);
1460             amfree(q64name);
1461         }
1462     }
1463     exclude_list = stralloc("");
1464     if (dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1465         for(excl = dp->exclude_list->first; excl != NULL;
1466                                             excl = excl->next) {
1467             q64name = amxml_format_tag("list", excl->name);
1468             exc = newvstralloc(exc, "    ", q64name, "\n", NULL);
1469             strappend(exclude_list, exc);
1470             amfree(q64name);
1471         }
1472     }
1473
1474     include_file = stralloc("");
1475     if (dp->include_file != NULL && dp->include_file->nb_element > 0) {
1476         for(excl = dp->include_file->first; excl != NULL;
1477                                             excl = excl->next) {
1478             q64name = amxml_format_tag("file", excl->name);
1479             exc = newvstralloc( exc, "    ", q64name, "\n", NULL);
1480             strappend(include_file, exc);
1481             amfree(q64name);
1482         }
1483     }
1484     include_list = stralloc("");
1485     if (dp->include_list != NULL && dp->include_list->nb_element > 0) {
1486         for(excl = dp->include_list->first; excl != NULL;
1487                                             excl = excl->next) {
1488             q64name = amxml_format_tag("list", excl->name);
1489             exc = newvstralloc( exc, "    ", q64name, "\n", NULL);
1490             strappend(include_list, exc);
1491             amfree(q64name);
1492         }
1493     }
1494
1495     if (dp->exclude_optional) {
1496         excl_opt = "    <optional>YES</optional>\n";
1497     }
1498     if (dp->include_optional) {
1499         incl_opt = "    <optional>YES</optional>\n";
1500     }
1501
1502     if (dp->exclude_file || dp->exclude_list)
1503         exclude = newvstralloc(exclude,
1504                                "  <exclude>\n",
1505                                exclude_file,
1506                                exclude_list,
1507                                excl_opt,
1508                                "  </exclude>\n", NULL);
1509     if (dp->include_file || dp->include_list)
1510         include = newvstralloc(include,
1511                                "  <include>\n",
1512                                include_file,
1513                                include_list,
1514                                incl_opt,
1515                                "  </include>\n", NULL);
1516     script_opt = xml_scripts(dp->pp_scriptlist, their_features);
1517     result = vstralloc(auth_opt,
1518                        kencrypt_opt,
1519                        compress_opt,
1520                        encrypt_opt,
1521                        record_opt,
1522                        index_opt,
1523                        data_path_opt,
1524                        exclude,
1525                        include,
1526                        script_opt,
1527                        NULL);
1528
1529     amfree(qdpname);
1530     amfree(auth_opt);
1531     amfree(data_path_opt);
1532     amfree(compress_opt);
1533     amfree(exclude);
1534     amfree(exclude_list);
1535     amfree(exclude_file);
1536     amfree(include);
1537     amfree(include_file);
1538     amfree(include_list);
1539     amfree(exc);
1540     amfree(decrypt_opt);
1541     amfree(encrypt_opt);
1542     amfree(script_opt);
1543
1544     /* result contains at least 'auth=...' */
1545     return result;
1546 }
1547
1548 char *
1549 xml_dumptype_properties(
1550     disk_t *dp)
1551 {
1552     xml_app_t xml_dumptype;
1553
1554     xml_dumptype.result = g_strdup("");
1555     xml_dumptype.features = NULL;
1556     if (dp && dp->config) {
1557         g_hash_table_foreach(dumptype_get_property(dp->config), xml_property,
1558                              &xml_dumptype);
1559     }
1560     return xml_dumptype.result;
1561 }
1562
1563 char *
1564 xml_estimate(
1565     estimatelist_t estimatelist,
1566     am_feature_t *their_features)
1567 {
1568     estimatelist_t el;
1569     char *l = NULL;
1570
1571     if (am_has_feature(their_features, fe_xml_estimatelist)) {
1572         vstrextend(&l, "  <estimate>", NULL);
1573         for (el=estimatelist; el != NULL; el = el->next) {
1574             switch (GPOINTER_TO_INT(el->data)) {
1575             case ES_CLIENT  : vstrextend(&l, "CLIENT ", NULL); break;
1576             case ES_SERVER  : vstrextend(&l, "SERVER ", NULL); break;
1577             case ES_CALCSIZE: vstrextend(&l, "CALCSIZE ", NULL); break;
1578             }
1579         }
1580         vstrextend(&l, "</estimate>", NULL);
1581     } else { /* add the first estimate only */
1582         if (am_has_feature(their_features, fe_xml_estimate)) {
1583             vstrextend(&l, "  <estimate>", NULL);
1584             switch (GPOINTER_TO_INT(estimatelist->data)) {
1585             case ES_CLIENT  : vstrextend(&l, "CLIENT", NULL); break;
1586             case ES_SERVER  : vstrextend(&l, "SERVER", NULL); break;
1587             case ES_CALCSIZE: vstrextend(&l, "CALCSIZE", NULL); break;
1588             }
1589         }
1590         vstrextend(&l, "</estimate>", NULL);
1591         if (GPOINTER_TO_INT(estimatelist->data) == ES_CALCSIZE) {
1592             vstrextend(&l, "  <calcsize>YES</calcsize>", NULL);
1593         }
1594     }
1595
1596     return l;
1597 }
1598
1599 char *
1600 clean_dle_str_for_client(
1601     char *dle_str,
1602     am_feature_t *their_features)
1603 {
1604     char *rval_dle_str;
1605     char *hack1, *hack2;
1606     char *pend, *pscript, *pproperty, *eproperty;
1607     int len;
1608
1609     if (!dle_str)
1610         return NULL;
1611
1612     rval_dle_str = stralloc(dle_str);
1613
1614     /* Remove everything between "  <encrypt>SERVER-CUSTOM" and "</encrypt>\n"
1615      */
1616 #define SC "</encrypt>\n"
1617 #define SC_LEN strlen(SC)
1618     hack1 = strstr(rval_dle_str, "  <encrypt>SERVER-CUSTOM");
1619     if (hack1) {
1620         hack2 = strstr(hack1, SC);
1621         /* +1 is to also move the trailing '\0' */
1622         memmove(hack1, hack2 + SC_LEN, strlen(hack2 + SC_LEN) + 1);
1623     }
1624 #undef SC
1625 #undef SC_LEN
1626
1627     if (!am_has_feature(their_features, fe_dumptype_property)) {
1628 #define SC "</property>\n"
1629 #define SC_LEN strlen(SC)
1630         /* remove all dle properties, they are before backup-program or script
1631           properties */
1632         hack1 = rval_dle_str;
1633         pend = strstr(rval_dle_str, "<backup-program>");
1634         pscript = strstr(rval_dle_str, "<script>");
1635         if (pscript && pscript < pend)
1636             pend = pscript;
1637         if (!pend) /* the complete string */
1638             pend = rval_dle_str + strlen(rval_dle_str);
1639         while (hack1) {
1640             pproperty = strstr(hack1, "    <property>");
1641             if (pproperty && pproperty < pend) { /* remove it */
1642                 eproperty = strstr(pproperty, SC);
1643                 len = eproperty + SC_LEN - pproperty;
1644                 memmove(pproperty, eproperty + SC_LEN, strlen(eproperty + SC_LEN) + 1);
1645                 pend  -= len;
1646                 hack1 = pproperty;
1647             } else {
1648                 hack1 = NULL;
1649             }
1650         }
1651     } else {
1652     }
1653 #undef SC
1654 #undef SC_LEN
1655
1656     return rval_dle_str;
1657 }
1658
1659
1660 char *
1661 xml_application(
1662     disk_t        *dp G_GNUC_UNUSED,
1663     application_t *application,
1664     am_feature_t  *their_features)
1665 {
1666     char       *plugin;
1667     char       *b64plugin;
1668     char       *client_name;
1669     xml_app_t   xml_app;
1670     proplist_t  proplist;
1671
1672     xml_app.features = their_features;
1673     xml_app.result   = NULL;
1674     plugin = application_get_plugin(application);
1675     b64plugin = amxml_format_tag("plugin", plugin);
1676     xml_app.result = vstralloc("  <backup-program>\n",
1677                         "    ", b64plugin, "\n",
1678                         NULL);
1679     proplist = application_get_property(application);
1680     g_hash_table_foreach(proplist, xml_property, &xml_app);
1681
1682     client_name = application_get_client_name(application);
1683     if (client_name && strlen(client_name) > 0 &&
1684         am_has_feature(their_features, fe_application_client_name)) {
1685         char *b64client_name = amxml_format_tag("client_name", client_name);
1686         vstrextend(&xml_app.result, "    ", b64client_name, "\n", NULL);
1687     }
1688
1689     vstrextend(&xml_app.result, "  </backup-program>\n", NULL);
1690
1691     amfree(b64plugin);
1692
1693     return xml_app.result;
1694 }
1695
1696
1697 char *
1698 xml_scripts(
1699     identlist_t pp_scriptlist,
1700     am_feature_t  *their_features)
1701 {
1702     char       *plugin;
1703     char       *b64plugin;
1704     char       *client_name;
1705     char       *xml_scr;
1706     char       *xml_scr1;
1707     char       *str = "";
1708     char       *sep;
1709     char       *eo_str;
1710     execute_on_t execute_on;
1711     int          execute_where;
1712     proplist_t  proplist;
1713     identlist_t pp_iter;
1714     pp_script_t *pp_script;
1715     xml_app_t   xml_app;
1716
1717     xml_app.features = their_features;
1718
1719     xml_scr = stralloc("");
1720     for (pp_iter = pp_scriptlist; pp_iter != NULL;
1721          pp_iter = pp_iter->next) {
1722         char *pp_script_name = pp_iter->data;
1723         pp_script = lookup_pp_script(pp_script_name);
1724         g_assert(pp_script != NULL);
1725         plugin = pp_script_get_plugin(pp_script);
1726         b64plugin = amxml_format_tag("plugin", plugin);
1727         xml_scr1 = vstralloc("  <script>\n",
1728                              "    ", b64plugin, "\n",
1729                              NULL);
1730
1731         execute_where = pp_script_get_execute_where(pp_script);
1732         switch (execute_where) {
1733             case ES_CLIENT: str = "CLIENT"; break;
1734             case ES_SERVER: str = "SERVER"; break;
1735         }
1736         xml_scr1 = vstrextend(&xml_scr1, "    <execute_where>",
1737                               str, "</execute_where>\n", NULL);
1738
1739         execute_on = pp_script_get_execute_on(pp_script);
1740         sep = "";
1741         eo_str = stralloc("");
1742         if (execute_on & EXECUTE_ON_PRE_DLE_AMCHECK) {
1743             eo_str = vstrextend(&eo_str, sep, "PRE-DLE-AMCHECK", NULL);
1744             sep = ",";
1745         }
1746         if (execute_on & EXECUTE_ON_PRE_HOST_AMCHECK) {
1747             eo_str = vstrextend(&eo_str, sep, "PRE-HOST-AMCHECK", NULL);
1748             sep = ",";
1749         }
1750         if (execute_on & EXECUTE_ON_POST_DLE_AMCHECK) {
1751             eo_str = vstrextend(&eo_str, sep, "POST-DLE-AMCHECK", NULL);
1752             sep = ",";
1753         }
1754         if (execute_on & EXECUTE_ON_POST_HOST_AMCHECK) {
1755             eo_str = vstrextend(&eo_str, sep, "POST-HOST-AMCHECK", NULL);
1756             sep = ",";
1757         }
1758         if (execute_on & EXECUTE_ON_PRE_DLE_ESTIMATE) {
1759             eo_str = vstrextend(&eo_str, sep, "PRE-DLE-ESTIMATE", NULL);
1760             sep = ",";
1761         }
1762         if (execute_on & EXECUTE_ON_PRE_HOST_ESTIMATE) {
1763             eo_str = vstrextend(&eo_str, sep, "PRE-HOST-ESTIMATE", NULL);
1764             sep = ",";
1765         }
1766         if (execute_on & EXECUTE_ON_POST_DLE_ESTIMATE) {
1767             eo_str = vstrextend(&eo_str, sep, "POST-DLE-ESTIMATE", NULL);
1768             sep = ",";
1769         }
1770         if (execute_on & EXECUTE_ON_POST_HOST_ESTIMATE) {
1771             eo_str = vstrextend(&eo_str, sep, "POST-HOST-ESTIMATE", NULL);
1772             sep = ",";
1773         }
1774         if (execute_on & EXECUTE_ON_PRE_DLE_BACKUP) {
1775             eo_str = vstrextend(&eo_str, sep, "PRE-DLE-BACKUP", NULL);
1776             sep = ",";
1777         }
1778         if (execute_on & EXECUTE_ON_PRE_HOST_BACKUP) {
1779             eo_str = vstrextend(&eo_str, sep, "PRE-HOST-BACKUP", NULL);
1780             sep = ",";
1781         }
1782         if (execute_on & EXECUTE_ON_POST_DLE_BACKUP) {
1783             eo_str = vstrextend(&eo_str, sep, "POST-DLE-BACKUP", NULL);
1784             sep = ",";
1785         }
1786         if (execute_on & EXECUTE_ON_POST_HOST_BACKUP) {
1787             eo_str = vstrextend(&eo_str, sep, "POST-HOST-BACKUP", NULL);
1788             sep = ",";
1789         }
1790         if (execute_on & EXECUTE_ON_PRE_RECOVER) {
1791             eo_str = vstrextend(&eo_str, sep, "PRE-RECOVER", NULL);
1792             sep = ",";
1793         }
1794         if (execute_on & EXECUTE_ON_POST_RECOVER) {
1795             eo_str = vstrextend(&eo_str, sep, "POST-RECOVER", NULL);
1796             sep = ",";
1797         }
1798         if (execute_on & EXECUTE_ON_PRE_LEVEL_RECOVER) {
1799             eo_str = vstrextend(&eo_str, sep, "PRE-LEVEL-RECOVER", NULL);
1800             sep = ",";
1801         }
1802         if (execute_on & EXECUTE_ON_POST_LEVEL_RECOVER) {
1803             eo_str = vstrextend(&eo_str, sep, "POST-LEVEL-RECOVER", NULL);
1804             sep = ",";
1805         }
1806         if (execute_on & EXECUTE_ON_INTER_LEVEL_RECOVER) {
1807             eo_str = vstrextend(&eo_str, sep, "INTER-LEVEL-RECOVER", NULL);
1808             sep = ",";
1809         }
1810         if (execute_on != 0)
1811             xml_scr1 = vstrextend(&xml_scr1,
1812                                   "    <execute_on>", eo_str,
1813                                   "</execute_on>\n", NULL);
1814         amfree(eo_str);
1815         proplist = pp_script_get_property(pp_script);
1816         xml_app.result   = stralloc("");
1817         g_hash_table_foreach(proplist, xml_property, &xml_app);
1818
1819         client_name = pp_script_get_client_name(pp_script);
1820         if (client_name && strlen(client_name) > 0 &&
1821             am_has_feature(their_features, fe_script_client_name)) {
1822             char *b64client_name = amxml_format_tag("client_name",
1823                                                     client_name);
1824             vstrextend(&xml_app.result, "    ", b64client_name, "\n", NULL);
1825         }
1826
1827         xml_scr = vstrextend(&xml_scr, xml_scr1, xml_app.result, "  </script>\n", NULL);
1828         amfree(b64plugin);
1829         amfree(xml_app.result);
1830         amfree(xml_scr1);
1831     }
1832     return xml_scr;
1833 }
1834
1835
1836 void
1837 disable_skip_disk(
1838     disklist_t *origqp)
1839 {
1840     disk_t *dp;
1841
1842     for (dp = origqp->head; dp != NULL; dp = dp->next) {
1843         if (dp->ignore || dp->strategy == DS_SKIP)
1844             dp->todo = 0;
1845     }
1846 }
1847
1848
1849 char *
1850 match_disklist(
1851     disklist_t *origqp,
1852     int         sargc,
1853     char **     sargv)
1854 {
1855     char *prevhost = NULL;
1856     char *errstr = NULL;
1857     int i;
1858     int match_a_host;
1859     int match_a_disk;
1860     int prev_match;
1861     disk_t *dp_skip;
1862     disk_t *dp;
1863
1864     if(sargc <= 0)
1865         return NULL;
1866
1867     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1868         if(dp->todo == 1)
1869             dp->todo = -1;
1870     }
1871
1872     prev_match = 0;
1873     for(i=0;i<sargc;i++) {
1874         match_a_host = 0;
1875         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1876             if(match_host(sargv[i], dp->host->hostname))
1877                 match_a_host = 1;
1878         }
1879         match_a_disk = 0;
1880         dp_skip = NULL;
1881         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1882             if(prevhost != NULL &&
1883                match_host(prevhost, dp->host->hostname) &&
1884                (match_disk(sargv[i], dp->name) ||
1885                 (dp->device && match_disk(sargv[i], dp->device)))) {
1886                 if(match_a_host) {
1887                     error(_("Argument %s cannot be both a host and a disk"),sargv[i]);
1888                     /*NOTREACHED*/
1889                 }
1890                 else {
1891                     if(dp->todo == -1) {
1892                         dp->todo = 1;
1893                         match_a_disk = 1;
1894                         prev_match = 0;
1895                     } else if (dp->todo == 0) {
1896                         match_a_disk = 1;
1897                         prev_match = 0;
1898                         dp_skip = dp;
1899                     } else { /* dp->todo == 1 */
1900                         match_a_disk = 1;
1901                         prev_match = 0;
1902                     }
1903                 }
1904             }
1905         }
1906         if(!match_a_disk) {
1907             if(match_a_host == 1) {
1908                 if(prev_match == 1) { /* all disk of the previous host */
1909                     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1910                         if(match_host(prevhost,dp->host->hostname))
1911                             if(dp->todo == -1) {
1912                                 dp->todo = 1;
1913                                 match_a_disk = 1;
1914                             }
1915                     }
1916                     if (!match_a_disk) {
1917                         char *errstr1;
1918                         errstr1 = vstrallocf(_("All disks on host '%s' are ignored or have strategy \"skip\".\n"), prevhost);
1919                         vstrextend(&errstr, errstr1, NULL);
1920                         amfree(errstr1);
1921                     }
1922                 }
1923                 prevhost = sargv[i];
1924                 prev_match = 1;
1925             }
1926             else {
1927                 char *errstr1;
1928                 if (strchr(sargv[i], (int)'\\')) {
1929                     errstr1 = vstrallocf(_("Argument '%s' matches neither a host nor a disk; quoting may not be correct.\n"), sargv[i]);
1930                 } else {
1931                     errstr1 = vstrallocf(_("Argument '%s' matches neither a host nor a disk.\n"), sargv[i]);
1932                 }
1933                 vstrextend(&errstr, errstr1, NULL);
1934                 amfree(errstr1);
1935                 prev_match = 0;
1936             }
1937         } else if (dp_skip) {
1938                 char *errstr1;
1939                 if (dp_skip->strategy == DS_SKIP) {
1940                     errstr1 = vstrallocf(_("Argument '%s' matches a disk with strategy \"skip\".\n"), sargv[i]);
1941                 } else {
1942                     errstr1 = vstrallocf(_("Argument '%s' matches a disk marked \"ignore\".\n"), sargv[i]);
1943                 }
1944                 vstrextend(&errstr, errstr1, NULL);
1945                 amfree(errstr1);
1946                 prev_match = 0;
1947         }
1948     }
1949
1950     if(prev_match == 1) { /* all disk of the previous host */
1951         match_a_disk = 0;
1952         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1953             if(match_host(prevhost,dp->host->hostname))
1954                 if(dp->todo == -1) {
1955                     dp->todo = 1;
1956                     match_a_disk = 1;
1957                 }
1958         }
1959         if (!match_a_disk) {
1960             char *errstr1;
1961             errstr1 = vstrallocf(_("All disks on host '%s' are ignored or have strategy \"skip\".\n"), prevhost);
1962             vstrextend(&errstr, errstr1, NULL);
1963             amfree(errstr1);
1964         }
1965     }
1966
1967     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1968         if(dp->todo == -1)
1969             dp->todo = 0;
1970     }
1971
1972     return errstr;
1973 }
1974
1975 gboolean
1976 match_dumpfile(
1977     dumpfile_t  *file,
1978     int         sargc,
1979     char **     sargv)
1980 {
1981     disk_t d;
1982     am_host_t h;
1983     disklist_t dl;
1984
1985     /* rather than try to reproduce the adaptive matching logic in
1986      * match_disklist, this simply creates a new, fake disklist with one
1987      * element in it, and calls match_disklist directly */
1988
1989     bzero(&h, sizeof(h));
1990     h.hostname = file->name;
1991     h.disks = &d;
1992
1993     bzero(&d, sizeof(d));
1994     d.host = &h;
1995     d.hostname = file->name;
1996     d.name = file->disk;
1997     d.device = file->disk;
1998     d.todo = 1;
1999
2000     dl.head = dl.tail = &d;
2001
2002     (void)match_disklist(&dl, sargc, sargv);
2003     return d.todo;
2004 }
2005
2006 netif_t *
2007 disklist_netifs(void)
2008 {
2009     return all_netifs;
2010 }
2011
2012 #ifdef TEST
2013
2014 static void dump_disk(const disk_t *);
2015 static void dump_disklist(const disklist_t *);
2016 int main(int, char *[]);
2017
2018 static void
2019 dump_disk(
2020     const disk_t *      dp)
2021 {
2022     g_printf(_("  DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n"),
2023            dp->name, dp->host->hostname, dp->line, dp->dtype_name,
2024            dp->name == NULL? "(null)": dp->name,
2025            dp->spindle);
2026 }
2027
2028 static void
2029 dump_disklist(
2030     const disklist_t *  lst)
2031 {
2032     const disk_t *dp, *prev;
2033     const am_host_t *hp;
2034
2035     if(hostlist == NULL) {
2036         g_printf(_("DISKLIST not read in\n"));
2037         return;
2038     }
2039
2040     g_printf(_("DISKLIST BY HOSTNAME:\n"));
2041
2042     for(hp = hostlist; hp != NULL; hp = hp->next) {
2043         char *if_name = NULL;
2044         if (hp->netif && hp->netif->config)
2045             if_name = interface_name(hp->netif->config);
2046
2047         g_printf(_("HOST %s INTERFACE %s\n"),
2048                hp->hostname,
2049                if_name ? _("(null)") : if_name);
2050         for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
2051             dump_disk(dp);
2052         putchar('\n');
2053     }
2054
2055
2056     g_printf(_("DISKLIST IN FILE ORDER:\n"));
2057
2058     prev = NULL;
2059     for(dp = lst->head; dp != NULL; prev = dp, dp = dp->next) {
2060         dump_disk(dp);
2061         /* check pointers */
2062         if(dp->prev != prev) g_printf(_("*** prev pointer mismatch!\n"));
2063         if(dp->next == NULL && lst->tail != dp) g_printf(_("tail mismatch!\n"));
2064     }
2065 }
2066
2067 int
2068 main(
2069     int         argc,
2070     char **     argv)
2071 {
2072   char *conffile;
2073   char *conf_diskfile;
2074   disklist_t lst;
2075   int result;
2076
2077   /*
2078    * Configure program for internationalization:
2079    *   1) Only set the message locale for now.
2080    *   2) Set textdomain for all amanda related programs to "amanda"
2081    *      We don't want to be forced to support dozens of message catalogs.
2082    */  
2083   setlocale(LC_MESSAGES, "C");
2084   textdomain("amanda"); 
2085
2086   safe_fd(-1, 0);
2087
2088   set_pname("diskfile");
2089
2090   dbopen(DBG_SUBDIR_SERVER);
2091
2092   /* Don't die when child closes pipe */
2093   signal(SIGPIPE, SIG_IGN);
2094
2095   if (argc>1) {
2096     config_init(CONFIG_INIT_EXPLICIT_NAME, argv[1]);
2097   } else {
2098     config_init(CONFIG_INIT_USE_CWD, NULL)
2099   }
2100
2101   if (config_errors(NULL) >= CFGERR_WARNINGS) {
2102     config_print_errors();
2103     if (config_errors(NULL) >= CFGERR_ERRORS) {
2104       g_critical(_("errors processing config file"));
2105     }
2106   }
2107
2108   conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
2109   result = read_diskfile(conf_diskfile, &lst);
2110   if(result == CFGERR_OK) {
2111     dump_disklist(&lst);
2112   } else {
2113     config_print_errors();
2114   }
2115   amfree(conf_diskfile);
2116   amfree(conffile);
2117   amfree(config_dir);
2118
2119   return result;
2120 }
2121 #endif /* TEST */