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