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