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