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