Imported Upstream version 2.5.1p3
[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.2.12 2007/01/26 14:33:24 martinea Exp $
29  *
30  * read disklist file
31  */
32 #include "amanda.h"
33 #include "arglist.h"
34 #include "conffile.h"
35 #include "diskfile.h"
36 #include "util.h"
37
38 static am_host_t *hostlist;
39
40 /* local functions */
41 static char *upcase(char *st);
42 static int parse_diskline(disklist_t *, const char *, FILE *, int *, char **);
43 static void disk_parserror(const char *, int, const char *, ...)
44     __attribute__ ((format (printf, 3, 4)));
45
46
47 int
48 read_diskfile(
49     const char *filename,
50     disklist_t *lst)
51 {
52     FILE *diskf;
53     int line_num;
54     char *line;
55
56     /* initialize */
57     hostlist = NULL;
58     lst->head = lst->tail = NULL;
59     line_num = 0;
60
61     if ((diskf = fopen(filename, "r")) == NULL) {
62         return -1;
63         /*NOTREACHED*/
64     }
65
66     while ((line = agets(diskf)) != NULL) {
67         line_num++;
68         if (line[0] != '\0') {
69             if (parse_diskline(lst, filename, diskf, &line_num, &line) < 0) {
70                 amfree(line);
71                 afclose(diskf);
72                 return (-1);
73             }
74         }
75         amfree(line);
76     }
77
78     afclose(diskf);
79     return (0);
80 }
81
82 am_host_t *
83 lookup_host(
84     const char *hostname)
85 {
86     am_host_t *p;
87
88     for (p = hostlist; p != NULL; p = p->next) {
89         if(strcasecmp(p->hostname, hostname) == 0) return p;
90     }
91     return (NULL);
92 }
93
94 disk_t *
95 lookup_disk(
96     const char *hostname,
97     const char *diskname)
98 {
99     am_host_t *host;
100     disk_t *disk;
101
102     host = lookup_host(hostname);
103     if (host == NULL)
104         return (NULL);
105
106     for (disk = host->disks; disk != NULL; disk = disk->hostnext) {
107         if (strcmp(disk->name, diskname) == 0)
108             return (disk);
109     }
110     return (NULL);
111 }
112
113
114 /*
115  * put disk on end of queue
116  */
117
118 void
119 enqueue_disk(
120     disklist_t *list,
121     disk_t *    disk)
122 {
123     if(list->tail == NULL) list->head = disk;
124     else list->tail->next = disk;
125     disk->prev = list->tail;
126
127     list->tail = disk;
128     disk->next = NULL;
129 }
130
131
132 /*
133  * put disk on head of queue
134  */
135
136 void
137 headqueue_disk(
138     disklist_t *list,
139     disk_t *    disk)
140 {
141     if(list->head == NULL) list->tail = disk;
142     else list->head->prev = disk;
143     disk->next = list->head;
144
145     list->head = disk;
146     disk->prev = NULL;
147 }
148
149
150 /*
151  * insert in sorted order
152  */
153
154 void
155 insert_disk(
156     disklist_t *list,
157     disk_t *    disk,
158     int         (*cmp)(disk_t *a, disk_t *b))
159 {
160     disk_t *prev, *ptr;
161
162     prev = NULL;
163     ptr = list->head;
164
165     while(ptr != NULL) {
166         if(cmp(disk, ptr) < 0) break;
167         prev = ptr;
168         ptr = ptr->next;
169     }
170     disk->next = ptr;
171     disk->prev = prev;
172
173     if(prev == NULL) list->head = disk;
174     else prev->next = disk;
175     if(ptr == NULL) list->tail = disk;
176     else ptr->prev = disk;
177 }
178
179 disk_t *
180 add_disk(
181     disklist_t *list,
182     char *      hostname,
183     char *      diskname)
184 {
185     disk_t *disk;
186     am_host_t *host;
187
188     disk = alloc(SIZEOF(disk_t));
189     disk->line = 0;
190     disk->tape_splitsize = (off_t)0;
191     disk->split_diskbuffer = NULL;
192     disk->fallback_splitsize = (off_t)0;
193     disk->name = stralloc(diskname);
194     disk->device = stralloc(diskname);
195     disk->spindle = -1;
196     disk->up = NULL;
197     disk->compress = COMP_NONE;
198     disk->encrypt  = ENCRYPT_NONE;
199     disk->start_t = 0;
200     disk->todo = 1;
201     disk->index = 1;
202     disk->exclude_list = NULL;
203     disk->exclude_file = NULL;
204     disk->include_list = NULL;
205     disk->include_file = NULL;
206
207     host = lookup_host(hostname);
208     if(host == NULL) {
209         host = alloc(SIZEOF(am_host_t));
210         host->next = hostlist;
211         hostlist = host;
212
213         host->hostname = stralloc(hostname);
214         host->disks = NULL;
215         host->inprogress = 0;
216         host->maxdumps = 1;
217         host->netif = NULL;
218         host->start_t = 0;
219         host->up = NULL;
220         host->features = NULL;
221     }
222     enqueue_disk(list, disk);
223
224     disk->host = host;
225     disk->hostnext = host->disks;
226     host->disks = disk;
227
228     return disk;
229 }
230
231
232 /*
233  * check if disk is present in list. Return true if so, false otherwise.
234  */
235
236 int
237 find_disk(
238     disklist_t *list,
239     disk_t *    disk)
240 {
241     disk_t *t;
242
243     t = list->head;
244     while ((t != NULL) && (t != disk)) {
245         t = t->next;
246     }
247     return (t == disk);
248 }
249
250
251 /*
252  * sort a whole queue
253  */
254
255 void
256 sort_disk(
257     disklist_t *in,
258     disklist_t *out,
259     int         (*cmp)(disk_t *a, disk_t *b))
260 {
261     disklist_t *tmp;
262     disk_t *disk;
263
264     tmp = in;           /* just in case in == out */
265
266     out->head = (disk_t *)0;
267     out->tail = (disk_t *)0;
268
269     while((disk = dequeue_disk(tmp)))
270         insert_disk(out, disk, cmp);
271 }
272
273
274 /*
275  * remove disk from front of queue
276  */
277
278 disk_t *
279 dequeue_disk(
280     disklist_t *list)
281 {
282     disk_t *disk;
283
284     if(list->head == NULL) return NULL;
285
286     disk = list->head;
287     list->head = disk->next;
288
289     if(list->head == NULL) list->tail = NULL;
290     else list->head->prev = NULL;
291
292     disk->prev = disk->next = NULL;     /* for debugging */
293     return disk;
294 }
295
296 void
297 remove_disk(
298     disklist_t *list,
299     disk_t *    disk)
300 {
301     if(disk->prev == NULL) list->head = disk->next;
302     else disk->prev->next = disk->next;
303
304     if(disk->next == NULL) list->tail = disk->prev;
305     else disk->next->prev = disk->prev;
306
307     disk->prev = disk->next = NULL;
308 }
309
310 void
311 free_disklist(
312     disklist_t* dl)
313 {
314     disk_t    *dp;
315     am_host_t *host, *hostnext;
316
317     while (dl->head != NULL) {
318         dp = dequeue_disk(dl);
319         amfree(dp->name);
320         free_sl(dp->exclude_file);
321         free_sl(dp->exclude_list);
322         free_sl(dp->include_file);
323         free_sl(dp->include_list);
324         free(dp);
325     }
326
327     for(host=hostlist; host != NULL; host = hostnext) {
328         amfree(host->hostname);
329         am_release_feature_set(host->features);
330         host->features = NULL;
331         hostnext = host->next;
332         amfree(host);
333     }
334     hostlist=NULL;
335 }
336
337 static char *
338 upcase(
339     char *st)
340 {
341     char *s = st;
342
343     while(*s) {
344         if(islower((int)*s)) *s = (char)toupper((int)*s);
345         s++;
346     }
347     return st;
348 }
349
350
351 /* return  0 on success */
352 /* return -1 on error   */
353 static int
354 parse_diskline(
355     disklist_t *lst,
356     const char *filename,
357     FILE *      diskf,
358     int *       line_num_p,
359     /*@keep@*/ char **  line_p)
360 {
361     am_host_t *host;
362     disk_t *disk;
363     dumptype_t *dtype;
364     interface_t *netif = 0;
365     char *hostname = NULL;
366     char *diskname, *diskdevice;
367     char *dumptype;
368     char *s, *fp;
369     int ch, dup = 0;
370     char *line = *line_p;
371     int line_num = *line_num_p;
372     struct tm *stm;
373     time_t st;
374     char *shost, *sdisk;
375     am_host_t *p;
376     disk_t *dp;
377
378     assert(filename != NULL);
379     assert(line_num > 0);
380     assert(line != NULL);
381
382     s = line;
383     ch = *s++;
384     skip_whitespace(s, ch);
385     if(ch == '\0' || ch == '#')
386         return (0);
387
388     fp = s - 1;
389     skip_non_whitespace(s, ch);
390     s[-1] = '\0';
391     host = lookup_host(fp);
392     if (host == NULL) {
393         hostname = stralloc(fp);
394         malloc_mark(hostname);
395     } else {
396         hostname = stralloc(host->hostname);
397         if (strcmp(host->hostname, fp) != 0) {
398             disk_parserror(filename, line_num, "Same host with different case: \"%s\" and \"%s\".", host->hostname, fp);
399             return -1;
400         }
401     }
402
403     shost = sanitise_filename(hostname);
404     for (p = hostlist; p != NULL; p = p->next) {
405         char *shostp = sanitise_filename(p->hostname);
406         if (!strcmp(hostname, p->hostname) &&
407              strcmp(shost, shostp)) {
408             disk_parserror(filename, line_num, "Two host are mapping to the same name: \"%s\" and \"%s\"", p->hostname, hostname);
409             return(-1);
410         }
411         else if (strcasecmp(hostname, p->hostname) &&
412                  match_host(hostname, p->hostname) &&
413                  match_host(p->hostname, hostname)) {
414             disk_parserror(filename, line_num, "Duplicate host name: \"%s\" and \"%s\"", p->hostname, hostname);
415             return(-1);
416         }
417         amfree(shostp);
418     }
419     amfree(shost);
420
421     skip_whitespace(s, ch);
422     if(ch == '\0' || ch == '#') {
423         disk_parserror(filename, line_num, "disk device name expected");
424         amfree(hostname);
425         return (-1);
426     }
427
428     fp = s - 1;
429     skip_quoted_string(s, ch);
430     s[-1] = '\0';
431     diskname = unquote_string(fp);
432
433     skip_whitespace(s, ch);
434     if(ch == '\0' || ch == '#') {
435         disk_parserror(filename, line_num, "disk dumptype expected");
436         amfree(hostname);
437         amfree(diskname);
438         return (-1);
439     }
440     fp = s - 1;
441     skip_quoted_string(s, ch);
442     s[-1] = '\0';
443
444     /* diskdevice */
445     dumptype = NULL;
446     diskdevice = NULL;
447     if(fp[0] != '{') {
448         dumptype = unquote_string(fp);
449         if ((dtype = lookup_dumptype(dumptype)) == NULL) {
450             diskdevice = dumptype;
451             skip_whitespace(s, ch);
452             if(ch == '\0' || ch == '#') {
453                 disk_parserror(filename, line_num,
454                         "disk dumptype '%s' not found", dumptype);
455                 amfree(hostname);
456                 amfree(diskdevice);
457                 amfree(diskname);
458                 return (-1);
459             }
460
461             fp = s - 1;
462             skip_quoted_string(s, ch);
463             s[-1] = '\0';
464             if (fp[0] != '{') {
465                 dumptype = unquote_string(fp);
466             }
467         }
468     }
469
470     /* check for duplicate disk */
471     disk = NULL;
472     if (host) {
473         if ((disk = lookup_disk(hostname, diskname)) != NULL) {
474             dup = 1;
475         } else {
476             disk = host->disks;
477             do {
478                 if (match_disk(diskname, disk->name) &&
479                     match_disk(disk->name, diskname)) {
480                     dup = 1;
481                 } else {
482                     disk = disk->hostnext;
483                 }
484             }
485             while (dup == 0 && disk != NULL);
486         }
487         if (dup == 1) {
488             disk_parserror(filename, line_num,
489                            "duplicate disk record, previous on line %d",
490                            disk->line);
491         }
492     }
493     if (!disk) {
494         disk = alloc(SIZEOF(disk_t));
495         malloc_mark(disk);
496         disk->line = line_num;
497         disk->name = diskname;
498         disk->device = diskdevice;
499         malloc_mark(disk->name);
500         disk->spindle = -1;
501         disk->up = NULL;
502         disk->inprogress = 0;
503     }
504
505     if (host) {
506         sdisk = sanitise_filename(diskname);
507         for (dp = host->disks; dp != NULL; dp = dp->next) {
508             char *sdiskp = sanitise_filename(dp->name);
509             if (strcmp(diskname, dp->name) != 0 &&
510                  strcmp(sdisk, sdiskp) == 0) {
511                 disk_parserror(filename, line_num,
512                  "Two disk are mapping to the same name: \"%s\" and \"%s\""
513                  ", you must use different diskname",
514                  dp->name, diskname);
515             return(-1);
516             }
517             amfree(sdiskp);
518         }
519         amfree(sdisk);
520     }
521
522     if (fp[0] == '{') {
523         s[-1] = (char)ch;
524         s = fp+2;
525         skip_whitespace(s, ch);
526         if (ch != '\0' && ch != '#') {
527             disk_parserror(filename, line_num,
528                       "expected line break after `{\', ignoring rest of line");
529         }
530
531         if (strchr(s-1, '}') &&
532             (strchr(s-1, '#') == NULL ||
533              strchr(s-1, '}') < strchr(s-1, '#'))) {
534             disk_parserror(filename, line_num,"'}' on same line than '{'");
535             amfree(hostname);
536             if(!dup) {
537                 amfree(disk->device);
538                 amfree(disk->name);
539                 amfree(disk);
540             } else {
541                 amfree(diskdevice);
542                 amfree(diskname);
543             }
544             return (-1);
545         }
546         amfree(line);
547
548         dtype = read_dumptype(vstralloc("custom(", hostname,
549                                         ":", disk->name, ")", NULL),
550                               diskf, (char*)filename, line_num_p);
551         if (dtype == NULL || dup) {
552             disk_parserror(filename, line_num,
553                            "read of custom dumptype failed");
554             amfree(hostname);
555             if(!dup) {
556                 amfree(disk->device);
557                 amfree(disk->name);
558                 amfree(disk);
559             } else {
560                 amfree(diskdevice);
561                 amfree(diskname);
562             }
563             return (-1);
564         }
565
566         *line_p = line = agets(diskf);
567         line_num = *line_num_p; /* no incr, read_dumptype did it already */
568
569         if (line == NULL)
570             *line_p = line = stralloc("");
571         s = line;
572         ch = *s++;
573     } else {
574         if((dtype = lookup_dumptype(dumptype)) == NULL) {
575             char *qdt = quote_string(dumptype);
576
577             disk_parserror(filename, line_num, "undefined dumptype `%s'", qdt);
578             amfree(qdt);
579             amfree(dumptype);
580             amfree(hostname);
581             if (!dup) {
582                 amfree(disk->device);
583                 amfree(disk->name);
584                 amfree(disk);
585             } else {
586                 amfree(diskdevice);
587                 amfree(diskname);
588             }
589             return (-1);
590         }
591     }
592
593     if (dup) {
594         amfree(hostname);
595         amfree(diskdevice);
596         amfree(diskname);
597         return (-1);
598     }
599
600     disk->dtype_name         = dtype->name;
601     disk->program            = dumptype_get_program(dtype);
602     disk->exclude_list     = duplicate_sl(dumptype_get_exclude(dtype).sl_list);
603     disk->exclude_file     = duplicate_sl(dumptype_get_exclude(dtype).sl_file);
604     disk->exclude_optional   = dumptype_get_exclude(dtype).optional;
605     disk->include_list     = duplicate_sl(dumptype_get_include(dtype).sl_list);
606     disk->include_file     = duplicate_sl(dumptype_get_include(dtype).sl_file);
607     disk->include_optional   = dumptype_get_include(dtype).optional;
608     disk->priority           = dumptype_get_priority(dtype);
609     disk->dumpcycle          = dumptype_get_dumpcycle(dtype);
610 /*    disk->frequency        = dumptype_get_frequency(dtype);*/
611     disk->security_driver    = dumptype_get_security_driver(dtype);
612     disk->maxdumps           = dumptype_get_maxdumps(dtype);
613     disk->tape_splitsize     = dumptype_get_tape_splitsize(dtype);
614     disk->split_diskbuffer   = dumptype_get_split_diskbuffer(dtype);
615     disk->fallback_splitsize = dumptype_get_fallback_splitsize(dtype);
616     disk->maxpromoteday      = dumptype_get_maxpromoteday(dtype);
617     disk->bumppercent        = dumptype_get_bumppercent(dtype);
618     disk->bumpsize           = dumptype_get_bumpsize(dtype);
619     disk->bumpdays           = dumptype_get_bumpdays(dtype);
620     disk->bumpmult           = dumptype_get_bumpmult(dtype);
621     disk->starttime          = dumptype_get_starttime(dtype);
622     disk->start_t = 0;
623     if (disk->starttime > 0) {
624         st = time(NULL);
625         disk->start_t = st;
626         stm = localtime(&st);
627         disk->start_t -= stm->tm_sec + 60 * stm->tm_min + 3600 * stm->tm_hour;
628         disk->start_t += disk->starttime / 100 * 3600 +
629                          disk->starttime % 100 * 60;
630         if ((disk->start_t - st) < -43200)
631             disk->start_t += 86400;
632     }
633     disk->strategy           = dumptype_get_strategy(dtype);
634     disk->estimate           = dumptype_get_estimate(dtype);
635     disk->compress           = dumptype_get_compress(dtype);
636     disk->srvcompprog        = dumptype_get_srvcompprog(dtype);
637     disk->clntcompprog       = dumptype_get_clntcompprog(dtype);
638     disk->encrypt            = dumptype_get_encrypt(dtype);
639     disk->srv_decrypt_opt    = dumptype_get_srv_decrypt_opt(dtype);
640     disk->clnt_decrypt_opt   = dumptype_get_clnt_decrypt_opt(dtype);
641     disk->srv_encrypt        = dumptype_get_srv_encrypt(dtype);
642     disk->clnt_encrypt       = dumptype_get_clnt_encrypt(dtype);
643     disk->amandad_path       = dumptype_get_amandad_path(dtype);
644     disk->client_username    = dumptype_get_client_username(dtype);
645     disk->ssh_keys           = dumptype_get_ssh_keys(dtype);
646     disk->comprate[0]        = dumptype_get_comprate(dtype)[0];
647     disk->comprate[1]        = dumptype_get_comprate(dtype)[1];
648
649     /*
650      * Boolean parameters with no value (Appears here as value 2) defaults
651      * to TRUE for backward compatibility and for logical consistency.
652      */
653     disk->record             = dumptype_get_record(dtype) != 0;
654     disk->skip_incr          = dumptype_get_skip_incr(dtype) != 0;
655     disk->skip_full          = dumptype_get_skip_full(dtype) != 0;
656     disk->to_holdingdisk     = dumptype_get_to_holdingdisk(dtype);
657     disk->kencrypt           = dumptype_get_kencrypt(dtype) != 0;
658     disk->index              = dumptype_get_index(dtype) != 0; 
659
660     disk->todo               = 1;
661
662     skip_whitespace(s, ch);
663     fp = s - 1;
664     if(ch && ch != '#') {               /* get optional spindle number */
665         char *fp1;
666         int is_digit=1;
667
668         skip_non_whitespace(s, ch);
669         s[-1] = '\0';
670         fp1=fp;
671         if (*fp1 == '-') fp1++;
672         for(;*fp1!='\0';fp1++) {
673             if(!isdigit((int)*fp1)) {
674                 is_digit = 0;
675             }
676         }
677         if(is_digit == 0) {
678             disk_parserror(filename, line_num, "non-integer spindle `%s'", fp);
679             amfree(hostname);
680             amfree(disk->name);
681             amfree(disk);
682             return (-1);
683         }
684         disk->spindle = atoi(fp);
685         skip_integer(s, ch);
686     }
687
688     skip_whitespace(s, ch);
689     fp = s - 1;
690     if(ch && ch != '#') {               /* get optional network interface */
691         skip_non_whitespace(s, ch);
692         s[-1] = '\0';
693         if((netif = lookup_interface(upcase(fp))) == NULL) {
694             disk_parserror(filename, line_num,
695                 "undefined network interface `%s'", fp);
696             amfree(hostname);
697             amfree(disk->name);
698             amfree(disk);
699             return (-1);
700         }
701     } else {
702         netif = lookup_interface("default");
703     }
704
705     skip_whitespace(s, ch);
706     if(ch && ch != '#') {               /* now we have garbage, ignore it */
707         disk_parserror(filename, line_num, "end of line expected");
708     }
709
710     if(dumptype_get_ignore(dtype) || dumptype_get_strategy(dtype) == DS_SKIP) {
711         amfree(hostname);
712         amfree(disk->name);
713         amfree(disk);
714         return (0);
715     }
716
717     /* success, add disk to lists */
718
719     if(host == NULL) {                  /* new host */
720         host = alloc(SIZEOF(am_host_t));
721         malloc_mark(host);
722         host->next = hostlist;
723         hostlist = host;
724
725         host->hostname = hostname;
726         hostname = NULL;
727         host->disks = NULL;
728         host->inprogress = 0;
729         host->maxdumps = 1;             /* will be overwritten */
730         host->netif = NULL;
731         host->start_t = 0;
732         host->up = NULL;
733         host->features = NULL;
734     } else {
735         amfree(hostname);
736     }
737
738     host->netif = netif;
739
740     enqueue_disk(lst, disk);
741
742     disk->host = host;
743     disk->hostnext = host->disks;
744     host->disks = disk;
745     host->maxdumps = disk->maxdumps;
746
747     return (0);
748 }
749
750
751 printf_arglist_function2(void disk_parserror, const char *, filename,
752     int, line_num, const char *, format)
753 {
754     va_list argp;
755
756     /* print error message */
757
758     fprintf(stderr, "\"%s\", line %d: ", filename, line_num);
759     arglist_start(argp, format);
760     vfprintf(stderr, format, argp);
761     arglist_end(argp);
762     fputc('\n', stderr);
763 }
764
765
766 void
767 dump_queue(
768     char *      st,
769     disklist_t  q,
770     int         npr,    /* we print first npr disks on queue, plus last two */
771     FILE *      f)
772 {
773     disk_t *d,*p;
774     int pos;
775     char *qname;
776
777     if(empty(q)) {
778         fprintf(f, "%s QUEUE: empty\n", st);
779         return;
780     }
781     fprintf(f, "%s QUEUE:\n", st);
782     for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
783         qname = quote_string(d->name);
784         if(pos < npr) fprintf(f, "%3d: %-10s %-4s\n",
785                               pos, d->host->hostname, qname);
786         amfree(qname);
787     }
788     if(pos > npr) {
789         if(pos > npr+2) fprintf(f, "  ...\n");
790         if(pos > npr+1) {
791             d = p->prev;
792             fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
793         }
794         d = p;
795         fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
796     }
797 }
798
799 char *
800 optionstr(
801     disk_t *            dp,
802     am_feature_t *      their_features,
803     FILE *              fdout)
804 {
805     char *auth_opt = NULL;
806     char *kencrypt_opt = "";
807     char *compress_opt = "";
808     char *encrypt_opt = stralloc("");
809     char *decrypt_opt = stralloc("");
810     char *record_opt = "";
811     char *index_opt = "";
812     char *exclude_file = NULL;
813     char *exclude_list = NULL;
814     char *include_file = NULL;
815     char *include_list = NULL;
816     char *excl_opt = "";
817     char *incl_opt = "";
818     char *exc = NULL;
819     char *result = NULL;
820     sle_t *excl;
821     int nb_exclude_file;
822     int nb_include_file;
823     char *qdpname;
824     char *qname;
825     int err=0;
826
827     assert(dp != NULL);
828     assert(dp->host != NULL);
829
830     qdpname = quote_string(dp->name);
831     if(am_has_feature(dp->host->features, fe_options_auth)) {
832         auth_opt = vstralloc("auth=", dp->security_driver, ";", NULL);
833     } else if(strcasecmp(dp->security_driver, "bsd") == 0) {
834         if(am_has_feature(dp->host->features, fe_options_bsd_auth))
835             auth_opt = stralloc("bsd-auth;");
836         else if(fdout) {
837             fprintf(fdout,
838                     "WARNING: %s:%s does not support auth or bsd-auth\n",
839                     dp->host->hostname, qdpname);
840         }
841     } else if(strcasecmp(dp->security_driver, "krb4") == 0) {
842         if(am_has_feature(dp->host->features, fe_options_krb4_auth))
843             auth_opt = stralloc("krb4-auth;");
844         else if(fdout) {
845             fprintf(fdout,
846                     "WARNING: %s:%s does not support auth or krb4-auth\n",
847                     dp->host->hostname, qdpname);
848         }
849         if(dp->kencrypt) {
850             if(am_has_feature(dp->host->features, fe_options_kencrypt)) {
851                 kencrypt_opt = "kencrypt;";
852             }
853             else if(fdout) {
854             fprintf(fdout,
855                     "WARNING: %s:%s does not support kencrypt\n",
856                     dp->host->hostname, qdpname);
857             }
858         }
859     }
860
861     switch(dp->compress) {
862     case COMP_FAST:
863         if(am_has_feature(their_features, fe_options_compress_fast)) {
864             compress_opt = "compress-fast;";
865         }
866         else if(fdout) {
867             fprintf(fdout,
868                     "WARNING: %s:%s does not support fast compression\n",
869                     dp->host->hostname, qdpname);
870         }
871         break;
872     case COMP_BEST:
873         if(am_has_feature(their_features, fe_options_compress_best)) {
874             compress_opt = "compress-best;";
875         }
876         else if(fdout) {
877             fprintf(fdout,
878                     "WARNING: %s:%s does not support best compression\n",
879                     dp->host->hostname, qdpname);
880         }
881         break;
882     case COMP_CUST:
883         if(am_has_feature(their_features, fe_options_compress_cust)) {
884           compress_opt = vstralloc("comp-cust=", dp->clntcompprog, ";", NULL);
885           if (BSTRNCMP(compress_opt, "comp-cust=;") == 0){
886             if(fdout) {
887               fprintf(fdout,
888                       "ERROR: %s:%s client custom compression with no compression program specified\n",
889                       dp->host->hostname, qdpname);
890             }
891             err++;
892           }
893         }
894         else if(fdout) {
895             fprintf(fdout,
896                     "WARNING: %s:%s does not support client custom compression\n",
897                     dp->host->hostname, qdpname);
898         }
899         break;
900     case COMP_SERV_FAST:
901         if(am_has_feature(their_features, fe_options_srvcomp_fast)) {
902             compress_opt = "srvcomp-fast;";
903         }
904         break;
905     case COMP_SERV_BEST:
906         if(am_has_feature(their_features, fe_options_srvcomp_best)) {
907             compress_opt = "srvcomp-best;";
908         }
909         break;
910     case COMP_SERV_CUST:
911         if(am_has_feature(their_features, fe_options_srvcomp_cust)) {
912           compress_opt = vstralloc("srvcomp-cust=", dp->srvcompprog, ";", NULL);
913           if (BSTRNCMP(compress_opt, "srvcomp-cust=;") == 0){
914             if(fdout) {
915               fprintf(fdout,
916                       "ERROR: %s:%s server custom compression with no compression program specified\n",
917                       dp->host->hostname, qdpname);
918             }
919             err++;
920           }
921         }
922         else if(fdout) {
923           fprintf(fdout,
924                   "WARNING: %s:%s does not support server custom compression\n",
925                   dp->host->hostname, qdpname);
926         }
927         break;
928     }
929
930     switch(dp->encrypt) {
931     case ENCRYPT_CUST:
932       if(am_has_feature(their_features, fe_options_encrypt_cust)) {
933          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-cust=",
934                                     dp->clnt_encrypt, ";", NULL);
935          if (BSTRNCMP(encrypt_opt, "encrypt-cust=;") == 0) {
936             if(fdout) {
937               fprintf(fdout,
938                       "ERROR: %s:%s encrypt client with no encryption program specified\n",
939                       dp->host->hostname, qdpname);
940             }
941             err++;
942           }
943          if ( dp->compress == COMP_SERV_FAST || 
944               dp->compress == COMP_SERV_BEST ||
945               dp->compress == COMP_SERV_CUST ) {
946            if(fdout) {
947               fprintf(fdout,
948                       "ERROR: %s:Client encryption with server compression is not supported. See amanda.conf(5) for detail.\n", dp->host->hostname);
949             }
950             err++;
951           }
952          if(dp->clnt_decrypt_opt) {
953            if(am_has_feature(their_features, fe_options_client_decrypt_option)) {
954              decrypt_opt = newvstralloc(decrypt_opt, "client-decrypt-option=",
955                                         dp->clnt_decrypt_opt, ";", NULL);
956            }
957            else if(fdout) {
958             fprintf(fdout,
959                     "WARNING: %s:%s does not support client decrypt option\n",
960                     dp->host->hostname, qdpname);
961            }
962          }
963       }
964       else if(fdout) {
965             fprintf(fdout,
966                     "WARNING: %s:%s does not support client data encryption\n",
967                     dp->host->hostname, qdpname);
968      }
969          break;
970     case ENCRYPT_SERV_CUST:
971       if(am_has_feature(their_features, fe_options_encrypt_serv_cust)) {
972          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-serv-cust=",
973                                     dp->srv_encrypt, ";", NULL);
974          if (BSTRNCMP(encrypt_opt, "encrypt-serv-cust=;") == 0){
975             if(fdout) {
976               fprintf(fdout,
977                       "ERROR: %s:%s encrypt server with no encryption program specified\n",
978                       dp->host->hostname, qdpname);
979             }
980             err++;
981           }
982          if(dp->srv_decrypt_opt) {
983            if(am_has_feature(their_features, fe_options_server_decrypt_option)) {
984              decrypt_opt = newvstralloc(decrypt_opt, "server-decrypt-option=",
985                                         dp->srv_decrypt_opt, ";", NULL);
986            }
987            else if(fdout) {
988             fprintf(fdout,
989                     "WARNING: %s:%s does not support server decrypt option\n",
990                     dp->host->hostname, qdpname);
991            }
992          }
993       }
994       else if(fdout) {
995             fprintf(fdout,
996                     "WARNING: %s:%s does not support server data encryption\n",
997                     dp->host->hostname, qdpname);
998       }
999          break;
1000     }
1001     
1002     if(!dp->record) {
1003         if(am_has_feature(their_features, fe_options_no_record)) {
1004             record_opt = "no-record;";
1005         }
1006         else if(fdout) {
1007             fprintf(fdout, "WARNING: %s:%s does not support no record\n",
1008                     dp->host->hostname, qdpname);
1009         }
1010     }
1011
1012     if(dp->index) {
1013         if(am_has_feature(their_features, fe_options_index)) {
1014             index_opt = "index;";
1015         }
1016         else if(fdout) {
1017             fprintf(fdout, "WARNING: %s:%s does not support index\n",
1018                     dp->host->hostname, qdpname);
1019         }
1020     }
1021
1022     if(dp->kencrypt) kencrypt_opt = "kencrypt;";
1023
1024
1025     exclude_file = stralloc("");
1026     nb_exclude_file = 0;
1027     if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1028         nb_exclude_file = dp->exclude_file->nb_element;
1029         if(am_has_feature(their_features, fe_options_exclude_file)) {
1030             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
1031                dp->exclude_file->nb_element == 1) {
1032                 for(excl = dp->exclude_file->first; excl != NULL;
1033                                                     excl = excl->next) {
1034                     qname = quote_string(excl->name);
1035                     exc = newvstralloc( exc, "exclude-file=", qname, ";", NULL);
1036                     strappend(exclude_file, exc);
1037                     amfree(qname);
1038                 }
1039             } else {
1040                 qname = quote_string(dp->exclude_file->last->name);
1041                 exc = newvstralloc(exc, "exclude-file=", qname, ";", NULL);
1042                 strappend(exclude_file, exc);
1043                 if(fdout) {
1044                     fprintf(fdout,
1045                        "WARNING: %s:%s does not support multiple exclude\n",
1046                        dp->host->hostname, qdpname);
1047                 }
1048                 amfree(qname);
1049             }
1050         } else if(fdout) {
1051             fprintf(fdout, "WARNING: %s:%s does not support exclude file\n",
1052                     dp->host->hostname, qdpname);
1053         }
1054     }
1055     exclude_list = stralloc("");
1056     if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1057         if(am_has_feature(their_features, fe_options_exclude_list)) {
1058             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
1059                (dp->exclude_list->nb_element == 1 && nb_exclude_file == 0)) {
1060                 for(excl = dp->exclude_list->first; excl != NULL;
1061                                                     excl = excl->next) {
1062                     qname = quote_string(excl->name);
1063                     exc = newvstralloc( exc, "exclude-list=", qname, ";", NULL);
1064                     strappend(exclude_list, exc);
1065                     amfree(qname);
1066                 }
1067             } else {
1068                 qname = quote_string(dp->exclude_list->last->name);
1069                 exc = newvstralloc(exc, "exclude-list=", qname, ";", NULL);
1070                 strappend(exclude_list, exc);
1071                 if(fdout) {
1072                         fprintf(fdout,
1073                          "WARNING: %s:%s does not support multiple exclude\n",
1074                          dp->host->hostname, qdpname);
1075                 }
1076                 amfree(qname);
1077             }
1078         } else if(fdout) {
1079             fprintf(fdout, "WARNING: %s:%s does not support exclude list\n",
1080                     dp->host->hostname, qdpname);
1081         }
1082     }
1083
1084     include_file = stralloc("");
1085     nb_include_file = 0;
1086     if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1087         nb_include_file = dp->include_file->nb_element;
1088         if(am_has_feature(their_features, fe_options_include_file)) {
1089             if(am_has_feature(their_features, fe_options_multiple_include) ||
1090                dp->include_file->nb_element == 1) {
1091                 for(excl = dp->include_file->first; excl != NULL;
1092                                                     excl = excl->next) {
1093                     qname = quote_string(excl->name);
1094                     exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1095                     strappend(include_file, exc);
1096                     amfree(qname);
1097                 }
1098             } else {
1099                 qname = quote_string(dp->include_file->last->name);
1100                 exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1101                 strappend(include_file, exc);
1102                 if(fdout) {
1103                     fprintf(fdout,
1104                          "WARNING: %s:%s does not support multiple include\n",
1105                          dp->host->hostname, qdpname);
1106                 }
1107                 amfree(qname);
1108             }
1109         } else if(fdout) {
1110             fprintf(fdout, "WARNING: %s:%s does not support include file\n",
1111                     dp->host->hostname, qdpname);
1112         }
1113     }
1114     include_list = stralloc("");
1115     if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1116         if(am_has_feature(their_features, fe_options_include_list)) {
1117             if(am_has_feature(their_features, fe_options_multiple_include) ||
1118                (dp->include_list->nb_element == 1 && nb_include_file == 0)) {
1119                 for(excl = dp->include_list->first; excl != NULL;
1120                                                     excl = excl->next) {
1121                     qname = quote_string(excl->name);
1122                     exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1123                     strappend(include_list, exc);
1124                     amfree(qname);
1125                 }
1126             } else {
1127                 qname = quote_string(dp->include_list->last->name);
1128                 exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1129                 strappend(include_list, exc);
1130                 if(fdout) {
1131                         fprintf(fdout,
1132                          "WARNING: %s:%s does not support multiple include\n",
1133                          dp->host->hostname, qdpname);
1134                 }
1135                 amfree(qname);
1136             }
1137         } else if(fdout) {
1138             fprintf(fdout, "WARNING: %s:%s does not support include list\n",
1139                     dp->host->hostname, qdpname);
1140         }
1141     }
1142
1143     if(dp->exclude_optional) {
1144         if(am_has_feature(their_features, fe_options_optional_exclude)) {
1145             excl_opt = "exclude-optional;";
1146         }
1147         else if(fdout) {
1148             fprintf(fdout,
1149                     "WARNING: %s:%s does not support optional exclude\n",
1150                     dp->host->hostname, qdpname);
1151         }
1152     }
1153     if(dp->include_optional) {
1154         if(am_has_feature(their_features, fe_options_optional_include)) {
1155            incl_opt = "include-optional;";
1156         }
1157         else if(fdout) {
1158             fprintf(fdout,
1159                     "WARNING: %s:%s does not support optional include\n",
1160                     dp->host->hostname, qdpname);
1161         }
1162     }
1163
1164     result = vstralloc(";",
1165                        auth_opt,
1166                        kencrypt_opt,
1167                        compress_opt,
1168                        encrypt_opt,
1169                        decrypt_opt,
1170                        record_opt,
1171                        index_opt,
1172                        exclude_file,
1173                        exclude_list,
1174                        include_file,
1175                        include_list,
1176                        excl_opt,
1177                        incl_opt,
1178                        NULL);
1179     amfree(qdpname);
1180     amfree(auth_opt);
1181     amfree(exclude_list);
1182     amfree(exclude_file);
1183     amfree(include_file);
1184     amfree(include_list);
1185     amfree(exc);
1186     amfree(decrypt_opt);
1187     amfree(encrypt_opt);
1188
1189     /* result contains at least 'auth=...' */
1190     if ( err ) {
1191         amfree(result);
1192         return NULL;
1193     } else {
1194         return result;
1195     }
1196 }
1197
1198  
1199 char *
1200 match_disklist(
1201     disklist_t *origqp,
1202     int         sargc,
1203     char **     sargv)
1204 {
1205     char *prevhost = NULL;
1206     char *errstr = NULL;
1207     int i;
1208     int match_a_host;
1209     int match_a_disk;
1210     int prev_match;
1211     disk_t *dp;
1212
1213     if(sargc <= 0)
1214         return NULL;
1215
1216     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1217         if(dp->todo == 1)
1218             dp->todo = -1;
1219     }
1220
1221     prev_match = 0;
1222     for(i=0;i<sargc;i++) {
1223         match_a_host = 0;
1224         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1225             if(match_host(sargv[i], dp->host->hostname))
1226                 match_a_host = 1;
1227         }
1228         match_a_disk = 0;
1229         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1230             if(prevhost != NULL &&
1231                match_host(prevhost, dp->host->hostname) &&
1232                (match_disk(sargv[i], dp->name) ||
1233                 (dp->device && match_disk(sargv[i], dp->device)))) {
1234                 if(match_a_host) {
1235                     error("Argument %s match a host and a disk",sargv[i]);
1236                     /*NOTREACHED*/
1237                 }
1238                 else {
1239                     if(dp->todo == -1) {
1240                         dp->todo = 1;
1241                         match_a_disk = 1;
1242                         prev_match = 0;
1243                     }
1244                 }
1245             }
1246         }
1247         if(!match_a_disk) {
1248             if(match_a_host == 1) {
1249                 if(prev_match == 1) { /* all disk of the previous host */
1250                     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1251                         if(match_host(prevhost,dp->host->hostname))
1252                             if(dp->todo == -1)
1253                                 dp->todo = 1;
1254                     }
1255                 }
1256                 prevhost = sargv[i];
1257                 prev_match = 1;
1258             }
1259             else {
1260                 vstrextend(&errstr, "Argument '", sargv[i], "' match neither a host nor a disk.\n", NULL);
1261             }
1262         }
1263     }
1264
1265     if(prev_match == 1) { /* all disk of the previous host */
1266         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1267             if(match_host(prevhost,dp->host->hostname))
1268                 if(dp->todo == -1)
1269                     dp->todo = 1;
1270         }
1271     }
1272
1273     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1274         if(dp->todo == -1)
1275             dp->todo = 0;
1276     }
1277
1278     return errstr;
1279 }
1280
1281
1282 #ifdef TEST
1283
1284 static void dump_disk(const disk_t *);
1285 static void dump_disklist(const disklist_t *);
1286 int main(int, char *[]);
1287
1288 static void
1289 dump_disk(
1290     const disk_t *      dp)
1291 {
1292     printf("  DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n",
1293            dp->name, dp->host->hostname, dp->line, dp->dtype_name,
1294            dp->name == NULL? "(null)": dp->name,
1295            dp->spindle);
1296 }
1297
1298 static void
1299 dump_disklist(
1300     const disklist_t *  lst)
1301 {
1302     const disk_t *dp, *prev;
1303     const am_host_t *hp;
1304
1305     if(hostlist == NULL) {
1306         printf("DISKLIST not read in\n");
1307         return;
1308     }
1309
1310     printf("DISKLIST BY HOSTNAME:\n");
1311
1312     for(hp = hostlist; hp != NULL; hp = hp->next) {
1313         printf("HOST %s INTERFACE %s\n",
1314                hp->hostname,
1315                (hp->netif == NULL||hp->netif->name == NULL) ? "(null)"
1316                                                             : hp->netif->name);
1317         for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
1318             dump_disk(dp);
1319         putchar('\n');
1320     }
1321
1322
1323     printf("DISKLIST IN FILE ORDER:\n");
1324
1325     prev = NULL;
1326     for(dp = lst->head; dp != NULL; prev = dp, dp = dp->next) {
1327         dump_disk(dp);
1328         /* check pointers */
1329         if(dp->prev != prev) printf("*** prev pointer mismatch!\n");
1330         if(dp->next == NULL && lst->tail != dp) printf("tail mismatch!\n");
1331     }
1332 }
1333
1334 int
1335 main(
1336     int         argc,
1337     char **     argv)
1338 {
1339   char *conffile;
1340   char *conf_diskfile;
1341   disklist_t lst;
1342   int result;
1343   unsigned long malloc_hist_1, malloc_size_1;
1344   unsigned long malloc_hist_2, malloc_size_2;
1345
1346   safe_fd(-1, 0);
1347
1348   set_pname("diskfile");
1349
1350   dbopen(DBG_SUBDIR_SERVER);
1351
1352   /* Don't die when child closes pipe */
1353   signal(SIGPIPE, SIG_IGN);
1354
1355   malloc_size_1 = malloc_inuse(&malloc_hist_1);
1356
1357   if (argc>1) {
1358     config_name = argv[1];
1359     if (strchr(config_name, '/') != NULL) {
1360       config_dir = stralloc2(argv[1], "/");
1361       config_name = strrchr(config_name, '/') + 1;
1362     } else {
1363       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1364     }
1365   } else {
1366     config_dir = stralloc("");
1367   }
1368   conffile = stralloc2(config_dir, CONFFILE_NAME);
1369   if((result = read_conffile(conffile)) == 0) {
1370     conf_diskfile = getconf_str(CNF_DISKFILE);
1371     if (*conf_diskfile == '/') {
1372       conf_diskfile = stralloc(conf_diskfile);
1373     } else {
1374       conf_diskfile = stralloc2(config_dir, conf_diskfile);
1375     }
1376     result = read_diskfile(conf_diskfile, &lst);
1377     if(result == 0) {
1378       dump_disklist(&lst);
1379     }
1380     amfree(conf_diskfile);
1381   }
1382   amfree(conffile);
1383   amfree(config_dir);
1384
1385   malloc_size_2 = malloc_inuse(&malloc_hist_2);
1386
1387   if(malloc_size_1 != malloc_size_2) {
1388     malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
1389   }
1390
1391   return result;
1392 }
1393 #endif /* TEST */