b909b0dcc97ba032920b6fbb2f3c87246dd21960
[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 static netif_t *all_netifs;
40
41 /* local functions */
42 static char *upcase(char *st);
43 static int parse_diskline(disklist_t *, const char *, FILE *, int *, char **);
44 static void disk_parserror(const char *, int, const char *, ...)
45                             G_GNUC_PRINTF(3, 4);
46
47
48 int
49 read_diskfile(
50     const char *filename,
51     disklist_t *lst)
52 {
53     FILE *diskf;
54     int line_num;
55     char *line;
56
57     /* initialize */
58     hostlist = NULL;
59     lst->head = lst->tail = NULL;
60     line_num = 0;
61
62     if ((diskf = fopen(filename, "r")) == NULL) {
63         return -1;
64         /*NOTREACHED*/
65     }
66
67     while ((line = agets(diskf)) != NULL) {
68         line_num++;
69         if (line[0] != '\0') {
70             if (parse_diskline(lst, filename, diskf, &line_num, &line) < 0) {
71                 amfree(line);
72                 afclose(diskf);
73                 return (-1);
74             }
75         }
76         amfree(line);
77     }
78
79     afclose(diskf);
80     return (0);
81 }
82
83 am_host_t *
84 lookup_host(
85     const char *hostname)
86 {
87     am_host_t *p;
88
89     for (p = hostlist; p != NULL; p = p->next) {
90         if(strcasecmp(p->hostname, hostname) == 0) return p;
91     }
92     return (NULL);
93 }
94
95 disk_t *
96 lookup_disk(
97     const char *hostname,
98     const char *diskname)
99 {
100     am_host_t *host;
101     disk_t *disk;
102
103     host = lookup_host(hostname);
104     if (host == NULL)
105         return (NULL);
106
107     for (disk = host->disks; disk != NULL; disk = disk->hostnext) {
108         if (strcmp(disk->name, diskname) == 0)
109             return (disk);
110     }
111     return (NULL);
112 }
113
114
115 /*
116  * put disk on end of queue
117  */
118
119 void
120 enqueue_disk(
121     disklist_t *list,
122     disk_t *    disk)
123 {
124     if(list->tail == NULL) list->head = disk;
125     else list->tail->next = disk;
126     disk->prev = list->tail;
127
128     list->tail = disk;
129     disk->next = NULL;
130 }
131
132
133 /*
134  * put disk on head of queue
135  */
136
137 void
138 headqueue_disk(
139     disklist_t *list,
140     disk_t *    disk)
141 {
142     if(list->head == NULL) list->tail = disk;
143     else list->head->prev = disk;
144     disk->next = list->head;
145
146     list->head = disk;
147     disk->prev = NULL;
148 }
149
150
151 /*
152  * insert in sorted order
153  */
154
155 void
156 insert_disk(
157     disklist_t *list,
158     disk_t *    disk,
159     int         (*cmp)(disk_t *a, disk_t *b))
160 {
161     disk_t *prev, *ptr;
162
163     prev = NULL;
164     ptr = list->head;
165
166     while(ptr != NULL) {
167         if(cmp(disk, ptr) < 0) break;
168         prev = ptr;
169         ptr = ptr->next;
170     }
171     disk->next = ptr;
172     disk->prev = prev;
173
174     if(prev == NULL) list->head = disk;
175     else prev->next = disk;
176     if(ptr == NULL) list->tail = disk;
177     else ptr->prev = disk;
178 }
179
180 disk_t *
181 add_disk(
182     disklist_t *list,
183     char *      hostname,
184     char *      diskname)
185 {
186     disk_t *disk;
187     am_host_t *host;
188
189     disk = alloc(SIZEOF(disk_t));
190     disk->line = 0;
191     disk->tape_splitsize = (off_t)0;
192     disk->split_diskbuffer = NULL;
193     disk->fallback_splitsize = (off_t)0;
194     disk->hostname = stralloc(hostname);
195     disk->name = stralloc(diskname);
196     disk->device = stralloc(diskname);
197     disk->spindle = -1;
198     disk->up = NULL;
199     disk->compress = COMP_NONE;
200     disk->encrypt  = ENCRYPT_NONE;
201     disk->start_t = 0;
202     disk->todo = 1;
203     disk->index = 1;
204     disk->exclude_list = NULL;
205     disk->exclude_file = NULL;
206     disk->include_list = NULL;
207     disk->include_file = NULL;
208
209     host = lookup_host(hostname);
210     if(host == NULL) {
211         host = alloc(SIZEOF(am_host_t));
212         host->next = hostlist;
213         hostlist = host;
214
215         host->hostname = stralloc(hostname);
216         host->disks = NULL;
217         host->inprogress = 0;
218         host->maxdumps = 1;
219         host->netif = NULL;
220         host->start_t = 0;
221         host->up = NULL;
222         host->features = NULL;
223     }
224     enqueue_disk(list, disk);
225
226     disk->host = host;
227     disk->hostnext = host->disks;
228     host->disks = disk;
229
230     return disk;
231 }
232
233
234 /*
235  * check if disk is present in list. Return true if so, false otherwise.
236  */
237
238 int
239 find_disk(
240     disklist_t *list,
241     disk_t *    disk)
242 {
243     disk_t *t;
244
245     t = list->head;
246     while ((t != NULL) && (t != disk)) {
247         t = t->next;
248     }
249     return (t == disk);
250 }
251
252
253 /*
254  * sort a whole queue
255  */
256
257 void
258 sort_disk(
259     disklist_t *in,
260     disklist_t *out,
261     int         (*cmp)(disk_t *a, disk_t *b))
262 {
263     disklist_t *tmp;
264     disk_t *disk;
265
266     tmp = in;           /* just in case in == out */
267
268     out->head = (disk_t *)0;
269     out->tail = (disk_t *)0;
270
271     while((disk = dequeue_disk(tmp)))
272         insert_disk(out, disk, cmp);
273 }
274
275
276 /*
277  * remove disk from front of queue
278  */
279
280 disk_t *
281 dequeue_disk(
282     disklist_t *list)
283 {
284     disk_t *disk;
285
286     if(list->head == NULL) return NULL;
287
288     disk = list->head;
289     list->head = disk->next;
290
291     if(list->head == NULL) list->tail = NULL;
292     else list->head->prev = NULL;
293
294     disk->prev = disk->next = NULL;     /* for debugging */
295     return disk;
296 }
297
298 void
299 remove_disk(
300     disklist_t *list,
301     disk_t *    disk)
302 {
303     if(disk->prev == NULL) list->head = disk->next;
304     else disk->prev->next = disk->next;
305
306     if(disk->next == NULL) list->tail = disk->prev;
307     else disk->next->prev = disk->prev;
308
309     disk->prev = disk->next = NULL;
310 }
311
312 void
313 free_disklist(
314     disklist_t* dl)
315 {
316     disk_t    *dp;
317     am_host_t *host, *hostnext;
318
319     while (dl->head != NULL) {
320         dp = dequeue_disk(dl);
321         amfree(dp->name);
322         amfree(dp->hostname);
323         amfree(dp->device);
324         free_sl(dp->exclude_file);
325         free_sl(dp->exclude_list);
326         free_sl(dp->include_file);
327         free_sl(dp->include_list);
328         free(dp);
329     }
330
331     for(host=hostlist; host != NULL; host = hostnext) {
332         amfree(host->hostname);
333         am_release_feature_set(host->features);
334         host->features = NULL;
335         hostnext = host->next;
336         amfree(host);
337     }
338     hostlist=NULL;
339 }
340
341 static char *
342 upcase(
343     char *st)
344 {
345     char *s = st;
346
347     while(*s) {
348         if(islower((int)*s)) *s = (char)toupper((int)*s);
349         s++;
350     }
351     return st;
352 }
353
354
355 /* return  0 on success */
356 /* return -1 on error   */
357 static int
358 parse_diskline(
359     disklist_t *lst,
360     const char *filename,
361     FILE *      diskf,
362     int *       line_num_p,
363     /*@keep@*/ char **  line_p)
364 {
365     am_host_t *host;
366     disk_t *disk;
367     dumptype_t *dtype;
368     netif_t *netif = NULL;
369     interface_t *cfg_if = NULL;
370     char *hostname = NULL;
371     char *diskname, *diskdevice;
372     char *dumptype;
373     char *s, *fp;
374     int ch, dup = 0;
375     char *line = *line_p;
376     int line_num = *line_num_p;
377     struct tm *stm;
378     time_t st;
379     char *shost, *sdisk;
380     am_host_t *p;
381     disk_t *dp;
382
383     assert(filename != NULL);
384     assert(line_num > 0);
385     assert(line != NULL);
386
387     s = line;
388     ch = *s++;
389     skip_whitespace(s, ch);
390     if(ch == '\0' || ch == '#')
391         return (0);
392
393     fp = s - 1;
394     skip_non_whitespace(s, ch);
395     s[-1] = '\0';
396     host = lookup_host(fp);
397     if (host == NULL) {
398         hostname = stralloc(fp);
399     } else {
400         hostname = stralloc(host->hostname);
401         if (strcmp(host->hostname, fp) != 0) {
402             disk_parserror(filename, line_num, "Same host with different case: \"%s\" and \"%s\".", host->hostname, fp);
403             return -1;
404         }
405     }
406
407     shost = sanitise_filename(hostname);
408     for (p = hostlist; p != NULL; p = p->next) {
409         char *shostp = sanitise_filename(p->hostname);
410         if (strcmp(hostname, p->hostname) &&
411             !strcmp(shost, shostp)) {
412             disk_parserror(filename, line_num, "Two hosts are mapping to the same name: \"%s\" and \"%s\"", p->hostname, hostname);
413             return(-1);
414         }
415         else if (strcasecmp(hostname, p->hostname) &&
416                  match_host(hostname, p->hostname) &&
417                  match_host(p->hostname, hostname)) {
418             disk_parserror(filename, line_num, _("Duplicate host name: \"%s\" and \"%s\""), p->hostname, hostname);
419             return(-1);
420         }
421         amfree(shostp);
422     }
423     amfree(shost);
424
425     skip_whitespace(s, ch);
426     if(ch == '\0' || ch == '#') {
427         disk_parserror(filename, line_num, _("disk device name expected"));
428         amfree(hostname);
429         return (-1);
430     }
431
432     fp = s - 1;
433     skip_quoted_string(s, ch);
434     s[-1] = '\0';
435     diskname = unquote_string(fp);
436
437     skip_whitespace(s, ch);
438     if(ch == '\0' || ch == '#') {
439         disk_parserror(filename, line_num, _("disk dumptype expected"));
440         amfree(hostname);
441         amfree(diskname);
442         return (-1);
443     }
444     fp = s - 1;
445     skip_quoted_string(s, ch);
446     s[-1] = '\0';
447
448     /* diskdevice */
449     dumptype = NULL;
450     diskdevice = NULL;
451     if(fp[0] != '{') {
452         dumptype = unquote_string(fp);
453         if ((dtype = lookup_dumptype(dumptype)) == NULL) {
454             diskdevice = dumptype;
455             skip_whitespace(s, ch);
456             if(ch == '\0' || ch == '#') {
457                 disk_parserror(filename, line_num,
458                         _("disk dumptype '%s' not found"), dumptype);
459                 amfree(hostname);
460                 amfree(diskdevice);
461                 amfree(diskname);
462                 return (-1);
463             }
464
465             fp = s - 1;
466             skip_quoted_string(s, ch);
467             s[-1] = '\0';
468             if (fp[0] != '{') {
469                 dumptype = unquote_string(fp);
470             }
471         }
472     }
473
474     /* check for duplicate disk */
475     disk = NULL;
476     if (host) {
477         if ((disk = lookup_disk(hostname, diskname)) != NULL) {
478             dup = 1;
479         } else {
480             disk = host->disks;
481             do {
482                 if (match_disk(diskname, disk->name) &&
483                     match_disk(disk->name, diskname)) {
484                     dup = 1;
485                 } else {
486                     disk = disk->hostnext;
487                 }
488             }
489             while (dup == 0 && disk != NULL);
490         }
491         if (dup == 1) {
492             disk_parserror(filename, line_num,
493                            _("duplicate disk record, previous on line %d"),
494                            disk->line);
495         }
496     }
497     if (!disk) {
498         disk = alloc(SIZEOF(disk_t));
499         disk->line = line_num;
500         disk->hostname = stralloc(hostname);
501         disk->name = diskname;
502         disk->device = diskdevice;
503         disk->spindle = -1;
504         disk->up = NULL;
505         disk->inprogress = 0;
506     }
507
508     if (host) {
509         sdisk = sanitise_filename(diskname);
510         for (dp = host->disks; dp != NULL; dp = dp->next) {
511             char *sdiskp = sanitise_filename(dp->name);
512             if (strcmp(diskname, dp->name) != 0 &&
513                  strcmp(sdisk, sdiskp) == 0) {
514                 disk_parserror(filename, line_num,
515                  _("Two disks are mapping to the same name: \"%s\" and \"%s\"; 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         = dumptype_name(dtype);
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((cfg_if = 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         cfg_if = lookup_interface("default");
705     }
706
707     /* see if we already have a netif_t for this interface */
708     for (netif = all_netifs; netif != NULL; netif = netif->next) {
709         if (netif->config == cfg_if)
710             break;
711     }
712
713     /* nope; make up a new one */
714     if (!netif) {
715         netif = alloc(sizeof(*netif));
716         netif->next = all_netifs;
717         all_netifs = netif;
718         netif->config = cfg_if;
719         netif->curusage = 0;
720     }
721
722     skip_whitespace(s, ch);
723     if(ch && ch != '#') {               /* now we have garbage, ignore it */
724         disk_parserror(filename, line_num, _("end of line expected"));
725     }
726
727     if(dumptype_get_ignore(dtype) || dumptype_get_strategy(dtype) == DS_SKIP) {
728         disk->todo = 0;
729     }
730
731     /* success, add disk to lists */
732
733     if(host == NULL) {                  /* new host */
734         host = alloc(SIZEOF(am_host_t));
735         host->next = hostlist;
736         hostlist = host;
737
738         host->hostname = hostname;
739         hostname = NULL;
740         host->disks = NULL;
741         host->inprogress = 0;
742         host->maxdumps = 1;             /* will be overwritten */
743         host->netif = NULL;
744         host->start_t = 0;
745         host->up = NULL;
746         host->features = NULL;
747     } else {
748         amfree(hostname);
749     }
750
751     host->netif = netif;
752
753     enqueue_disk(lst, disk);
754
755     disk->host = host;
756     disk->hostnext = host->disks;
757     host->disks = disk;
758     host->maxdumps = disk->maxdumps;
759
760     return (0);
761 }
762
763
764 printf_arglist_function2(void disk_parserror, const char *, filename,
765     int, line_num, const char *, format)
766 {
767     va_list argp;
768     const char *xlated_fmt = gettext(format);
769
770     /* print error message */
771
772     g_fprintf(stderr, "\"%s\", line %d: ", filename, line_num);
773     arglist_start(argp, format);
774     g_vfprintf(stderr, xlated_fmt, argp);
775     arglist_end(argp);
776     fputc('\n', stderr);
777 }
778
779
780 void
781 dump_queue(
782     char *      st,
783     disklist_t  q,
784     int         npr,    /* we print first npr disks on queue, plus last two */
785     FILE *      f)
786 {
787     disk_t *d,*p;
788     int pos;
789     char *qname;
790
791     if(empty(q)) {
792         g_fprintf(f, _("%s QUEUE: empty\n"), st);
793         return;
794     }
795     g_fprintf(f, _("%s QUEUE:\n"), st);
796     for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
797         qname = quote_string(d->name);
798         if(pos < npr) g_fprintf(f, "%3d: %-10s %-4s\n",
799                               pos, d->host->hostname, qname);
800         amfree(qname);
801     }
802     if(pos > npr) {
803         if(pos > npr+2) g_fprintf(f, "  ...\n");
804         if(pos > npr+1) {
805             d = p->prev;
806             g_fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
807         }
808         d = p;
809         g_fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
810     }
811 }
812
813 char *
814 optionstr(
815     disk_t *            dp,
816     am_feature_t *      their_features,
817     FILE *              fdout)
818 {
819     char *auth_opt = NULL;
820     char *kencrypt_opt = "";
821     char *compress_opt = "";
822     char *encrypt_opt = stralloc("");
823     char *decrypt_opt = stralloc("");
824     char *record_opt = "";
825     char *index_opt = "";
826     char *exclude_file = NULL;
827     char *exclude_list = NULL;
828     char *include_file = NULL;
829     char *include_list = NULL;
830     char *excl_opt = "";
831     char *incl_opt = "";
832     char *exc = NULL;
833     char *result = NULL;
834     sle_t *excl;
835     int nb_exclude_file;
836     int nb_include_file;
837     char *qdpname;
838     char *qname;
839     int err=0;
840
841     assert(dp != NULL);
842     assert(dp->host != NULL);
843
844     qdpname = quote_string(dp->name);
845     if(am_has_feature(dp->host->features, fe_options_auth)) {
846         auth_opt = vstralloc("auth=", dp->security_driver, ";", NULL);
847     } else if(strcasecmp(dp->security_driver, "bsd") == 0) {
848         if(am_has_feature(dp->host->features, fe_options_bsd_auth))
849             auth_opt = stralloc("bsd-auth;");
850         else if(fdout) {
851             g_fprintf(fdout,
852                     _("WARNING: %s:%s does not support auth or bsd-auth\n"),
853                     dp->host->hostname, qdpname);
854         }
855     } else if(strcasecmp(dp->security_driver, "krb4") == 0) {
856         if(am_has_feature(dp->host->features, fe_options_krb4_auth))
857             auth_opt = stralloc("krb4-auth;");
858         else if(fdout) {
859             g_fprintf(fdout,
860                     _("WARNING: %s:%s does not support auth or krb4-auth\n"),
861                     dp->host->hostname, qdpname);
862         }
863         if(dp->kencrypt) {
864             if(am_has_feature(dp->host->features, fe_options_kencrypt)) {
865                 kencrypt_opt = "kencrypt;";
866             }
867             else if(fdout) {
868             g_fprintf(fdout,
869                     _("WARNING: %s:%s does not support kencrypt\n"),
870                     dp->host->hostname, qdpname);
871             }
872         }
873     }
874
875     switch(dp->compress) {
876     case COMP_FAST:
877         if(am_has_feature(their_features, fe_options_compress_fast)) {
878             compress_opt = "compress-fast;";
879         }
880         else if(fdout) {
881             g_fprintf(fdout,
882                     _("WARNING: %s:%s does not support fast compression\n"),
883                     dp->host->hostname, qdpname);
884         }
885         break;
886     case COMP_BEST:
887         if(am_has_feature(their_features, fe_options_compress_best)) {
888             compress_opt = "compress-best;";
889         }
890         else if(fdout) {
891             g_fprintf(fdout,
892                     _("WARNING: %s:%s does not support best compression\n"),
893                     dp->host->hostname, qdpname);
894         }
895         break;
896     case COMP_CUST:
897         if(am_has_feature(their_features, fe_options_compress_cust)) {
898           compress_opt = vstralloc("comp-cust=", dp->clntcompprog, ";", NULL);
899           if (BSTRNCMP(compress_opt, "comp-cust=;") == 0){
900             if(fdout) {
901               g_fprintf(fdout,
902                       _("ERROR: %s:%s client custom compression with no compression program specified\n"),
903                       dp->host->hostname, qdpname);
904             }
905             err++;
906           }
907         }
908         else if(fdout) {
909             g_fprintf(fdout,
910                     _("WARNING: %s:%s does not support client custom compression\n"),
911                     dp->host->hostname, qdpname);
912         }
913         break;
914     case COMP_SERVER_FAST:
915         if(am_has_feature(their_features, fe_options_srvcomp_fast)) {
916             compress_opt = "srvcomp-fast;";
917         }
918         break;
919     case COMP_SERVER_BEST:
920         if(am_has_feature(their_features, fe_options_srvcomp_best)) {
921             compress_opt = "srvcomp-best;";
922         }
923         break;
924     case COMP_SERVER_CUST:
925         if(am_has_feature(their_features, fe_options_srvcomp_cust)) {
926           compress_opt = vstralloc("srvcomp-cust=", dp->srvcompprog, ";", NULL);
927           if (BSTRNCMP(compress_opt, "srvcomp-cust=;") == 0){
928             if(fdout) {
929               g_fprintf(fdout,
930                       _("ERROR: %s:%s server custom compression with no compression program specified\n"),
931                       dp->host->hostname, qdpname);
932             }
933             err++;
934           }
935         }
936         else if(fdout) {
937           g_fprintf(fdout,
938                   _("WARNING: %s:%s does not support server custom compression\n"),
939                   dp->host->hostname, qdpname);
940         }
941         break;
942     }
943
944     switch(dp->encrypt) {
945     case ENCRYPT_CUST:
946       if(am_has_feature(their_features, fe_options_encrypt_cust)) {
947          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-cust=",
948                                     dp->clnt_encrypt, ";", NULL);
949          if (BSTRNCMP(encrypt_opt, "encrypt-cust=;") == 0) {
950             if(fdout) {
951               g_fprintf(fdout,
952                       _("ERROR: %s:%s encrypt client with no encryption program specified\n"),
953                       dp->host->hostname, qdpname);
954             }
955             err++;
956           }
957          if ( dp->compress == COMP_SERVER_FAST || 
958               dp->compress == COMP_SERVER_BEST ||
959               dp->compress == COMP_SERVER_CUST ) {
960            if(fdout) {
961               g_fprintf(fdout,
962                       _("ERROR: %s:Client encryption with server compression is "
963                         "not supported. See amanda.conf(5) for detail.\n"), 
964                         dp->host->hostname);
965             }
966             err++;
967           }
968          if(dp->clnt_decrypt_opt) {
969            if(am_has_feature(their_features, fe_options_client_decrypt_option)) {
970              decrypt_opt = newvstralloc(decrypt_opt, "client-decrypt-option=",
971                                         dp->clnt_decrypt_opt, ";", NULL);
972            }
973            else if(fdout) {
974             g_fprintf(fdout,
975                     _("WARNING: %s:%s does not support client decrypt option\n"),
976                     dp->host->hostname, qdpname);
977            }
978          }
979       }
980       else if(fdout) {
981             g_fprintf(fdout,
982                     _("WARNING: %s:%s does not support client data encryption\n"),
983                     dp->host->hostname, qdpname);
984      }
985          break;
986     case ENCRYPT_SERV_CUST:
987       if(am_has_feature(their_features, fe_options_encrypt_serv_cust)) {
988          encrypt_opt = newvstralloc(encrypt_opt, "encrypt-serv-cust=",
989                                     dp->srv_encrypt, ";", NULL);
990          if (BSTRNCMP(encrypt_opt, "encrypt-serv-cust=;") == 0){
991             if(fdout) {
992               g_fprintf(fdout,
993                       _("ERROR: %s:%s No encryption program specified in dumptypes\n"),
994                       dp->host->hostname, qdpname);
995               g_fprintf(fdout, _("Change the dumptype in the disklist or mention "
996                                "the ecnryption program to use in the dumptypes file"));
997
998             }
999             err++;
1000           }
1001          if(dp->srv_decrypt_opt) {
1002            if(am_has_feature(their_features, fe_options_server_decrypt_option)) {
1003              decrypt_opt = newvstralloc(decrypt_opt, "server-decrypt-option=",
1004                                         dp->srv_decrypt_opt, ";", NULL);
1005            }
1006            else if(fdout) {
1007             g_fprintf(fdout,
1008                     _("WARNING: %s:%s does not support server decrypt option\n"),
1009                     dp->host->hostname, qdpname);
1010            }
1011          }
1012       }
1013       else if(fdout) {
1014             g_fprintf(fdout,
1015                     _("WARNING: %s:%s does not support server data encryption\n"),
1016                     dp->host->hostname, qdpname);
1017       }
1018          break;
1019     }
1020     
1021     if(!dp->record) {
1022         if(am_has_feature(their_features, fe_options_no_record)) {
1023             record_opt = "no-record;";
1024         }
1025         else if(fdout) {
1026             g_fprintf(fdout, _("WARNING: %s:%s does not support no record\n"),
1027                     dp->host->hostname, qdpname);
1028         }
1029     }
1030
1031     if(dp->index) {
1032         if(am_has_feature(their_features, fe_options_index)) {
1033             index_opt = "index;";
1034         }
1035         else if(fdout) {
1036             g_fprintf(fdout, _("WARNING: %s:%s does not support index\n"),
1037                     dp->host->hostname, qdpname);
1038         }
1039     }
1040
1041     if(dp->kencrypt) kencrypt_opt = "kencrypt;";
1042
1043
1044     exclude_file = stralloc("");
1045     nb_exclude_file = 0;
1046     if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
1047         nb_exclude_file = dp->exclude_file->nb_element;
1048         if(am_has_feature(their_features, fe_options_exclude_file)) {
1049             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
1050                dp->exclude_file->nb_element == 1) {
1051                 for(excl = dp->exclude_file->first; excl != NULL;
1052                                                     excl = excl->next) {
1053                     qname = quote_string(excl->name);
1054                     exc = newvstralloc( exc, "exclude-file=", qname, ";", NULL);
1055                     strappend(exclude_file, exc);
1056                     amfree(qname);
1057                 }
1058             } else {
1059                 qname = quote_string(dp->exclude_file->last->name);
1060                 exc = newvstralloc(exc, "exclude-file=", qname, ";", NULL);
1061                 strappend(exclude_file, exc);
1062                 if(fdout) {
1063                     g_fprintf(fdout,
1064                        _("WARNING: %s:%s does not support multiple exclude\n"),
1065                        dp->host->hostname, qdpname);
1066                 }
1067                 amfree(qname);
1068             }
1069         } else if(fdout) {
1070             g_fprintf(fdout, _("WARNING: %s:%s does not support exclude file\n"),
1071                     dp->host->hostname, qdpname);
1072         }
1073     }
1074     exclude_list = stralloc("");
1075     if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
1076         if(am_has_feature(their_features, fe_options_exclude_list)) {
1077             if(am_has_feature(their_features, fe_options_multiple_exclude) ||
1078                (dp->exclude_list->nb_element == 1 && nb_exclude_file == 0)) {
1079                 for(excl = dp->exclude_list->first; excl != NULL;
1080                                                     excl = excl->next) {
1081                     qname = quote_string(excl->name);
1082                     exc = newvstralloc( exc, "exclude-list=", qname, ";", NULL);
1083                     strappend(exclude_list, exc);
1084                     amfree(qname);
1085                 }
1086             } else {
1087                 qname = quote_string(dp->exclude_list->last->name);
1088                 exc = newvstralloc(exc, "exclude-list=", qname, ";", NULL);
1089                 strappend(exclude_list, exc);
1090                 if(fdout) {
1091                         g_fprintf(fdout,
1092                          _("WARNING: %s:%s does not support multiple exclude\n"),
1093                          dp->host->hostname, qdpname);
1094                 }
1095                 amfree(qname);
1096             }
1097         } else if(fdout) {
1098             g_fprintf(fdout, _("WARNING: %s:%s does not support exclude list\n"),
1099                     dp->host->hostname, qdpname);
1100         }
1101     }
1102
1103     include_file = stralloc("");
1104     nb_include_file = 0;
1105     if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
1106         nb_include_file = dp->include_file->nb_element;
1107         if(am_has_feature(their_features, fe_options_include_file)) {
1108             if(am_has_feature(their_features, fe_options_multiple_include) ||
1109                dp->include_file->nb_element == 1) {
1110                 for(excl = dp->include_file->first; excl != NULL;
1111                                                     excl = excl->next) {
1112                     qname = quote_string(excl->name);
1113                     exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1114                     strappend(include_file, exc);
1115                     amfree(qname);
1116                 }
1117             } else {
1118                 qname = quote_string(dp->include_file->last->name);
1119                 exc = newvstralloc(exc, "include-file=", qname, ";", NULL);
1120                 strappend(include_file, exc);
1121                 if(fdout) {
1122                     g_fprintf(fdout,
1123                          _("WARNING: %s:%s does not support multiple include\n"),
1124                          dp->host->hostname, qdpname);
1125                 }
1126                 amfree(qname);
1127             }
1128         } else if(fdout) {
1129             g_fprintf(fdout, _("WARNING: %s:%s does not support include file\n"),
1130                     dp->host->hostname, qdpname);
1131         }
1132     }
1133     include_list = stralloc("");
1134     if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
1135         if(am_has_feature(their_features, fe_options_include_list)) {
1136             if(am_has_feature(their_features, fe_options_multiple_include) ||
1137                (dp->include_list->nb_element == 1 && nb_include_file == 0)) {
1138                 for(excl = dp->include_list->first; excl != NULL;
1139                                                     excl = excl->next) {
1140                     qname = quote_string(excl->name);
1141                     exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1142                     strappend(include_list, exc);
1143                     amfree(qname);
1144                 }
1145             } else {
1146                 qname = quote_string(dp->include_list->last->name);
1147                 exc = newvstralloc(exc, "include-list=", qname, ";", NULL);
1148                 strappend(include_list, exc);
1149                 if(fdout) {
1150                         g_fprintf(fdout,
1151                          _("WARNING: %s:%s does not support multiple include\n"),
1152                          dp->host->hostname, qdpname);
1153                 }
1154                 amfree(qname);
1155             }
1156         } else if(fdout) {
1157             g_fprintf(fdout, _("WARNING: %s:%s does not support include list\n"),
1158                     dp->host->hostname, qdpname);
1159         }
1160     }
1161
1162     if(dp->exclude_optional) {
1163         if(am_has_feature(their_features, fe_options_optional_exclude)) {
1164             excl_opt = "exclude-optional;";
1165         }
1166         else if(fdout) {
1167             g_fprintf(fdout,
1168                     _("WARNING: %s:%s does not support optional exclude\n"),
1169                     dp->host->hostname, qdpname);
1170         }
1171     }
1172     if(dp->include_optional) {
1173         if(am_has_feature(their_features, fe_options_optional_include)) {
1174            incl_opt = "include-optional;";
1175         }
1176         else if(fdout) {
1177             g_fprintf(fdout,
1178                     _("WARNING: %s:%s does not support optional include\n"),
1179                     dp->host->hostname, qdpname);
1180         }
1181     }
1182
1183     result = vstralloc(";",
1184                        auth_opt,
1185                        kencrypt_opt,
1186                        compress_opt,
1187                        encrypt_opt,
1188                        decrypt_opt,
1189                        record_opt,
1190                        index_opt,
1191                        exclude_file,
1192                        exclude_list,
1193                        include_file,
1194                        include_list,
1195                        excl_opt,
1196                        incl_opt,
1197                        NULL);
1198     amfree(qdpname);
1199     amfree(auth_opt);
1200     amfree(exclude_list);
1201     amfree(exclude_file);
1202     amfree(include_file);
1203     amfree(include_list);
1204     amfree(exc);
1205     amfree(decrypt_opt);
1206     amfree(encrypt_opt);
1207
1208     /* result contains at least 'auth=...' */
1209     if ( err ) {
1210         amfree(result);
1211         return NULL;
1212     } else {
1213         return result;
1214     }
1215 }
1216
1217  
1218 char *
1219 match_disklist(
1220     disklist_t *origqp,
1221     int         sargc,
1222     char **     sargv)
1223 {
1224     char *prevhost = NULL;
1225     char *errstr = NULL;
1226     int i;
1227     int match_a_host;
1228     int match_a_disk;
1229     int prev_match;
1230     disk_t *dp;
1231
1232     if(sargc <= 0)
1233         return NULL;
1234
1235     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1236         if(dp->todo == 1)
1237             dp->todo = -1;
1238     }
1239
1240     prev_match = 0;
1241     for(i=0;i<sargc;i++) {
1242         match_a_host = 0;
1243         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1244             if(match_host(sargv[i], dp->host->hostname))
1245                 match_a_host = 1;
1246         }
1247         match_a_disk = 0;
1248         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1249             if(prevhost != NULL &&
1250                match_host(prevhost, dp->host->hostname) &&
1251                (match_disk(sargv[i], dp->name) ||
1252                 (dp->device && match_disk(sargv[i], dp->device)))) {
1253                 if(match_a_host) {
1254                     error(_("Argument %s cannot be both a host and a disk"),sargv[i]);
1255                     /*NOTREACHED*/
1256                 }
1257                 else {
1258                     if(dp->todo == -1) {
1259                         dp->todo = 1;
1260                         match_a_disk = 1;
1261                         prev_match = 0;
1262                     }
1263                 }
1264             }
1265         }
1266         if(!match_a_disk) {
1267             if(match_a_host == 1) {
1268                 if(prev_match == 1) { /* all disk of the previous host */
1269                     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1270                         if(match_host(prevhost,dp->host->hostname))
1271                             if(dp->todo == -1)
1272                                 dp->todo = 1;
1273                     }
1274                 }
1275                 prevhost = sargv[i];
1276                 prev_match = 1;
1277             }
1278             else {
1279                 char *errstr1 = vstrallocf(_("Argument '%s' matches neither a host nor a disk.\n"), sargv[i]);
1280                 vstrextend(&errstr, errstr1, NULL);
1281                 prev_match = 0;
1282             }
1283         }
1284     }
1285
1286     if(prev_match == 1) { /* all disk of the previous host */
1287         for(dp = origqp->head; dp != NULL; dp = dp->next) {
1288             if(match_host(prevhost,dp->host->hostname))
1289                 if(dp->todo == -1)
1290                     dp->todo = 1;
1291         }
1292     }
1293
1294     for(dp = origqp->head; dp != NULL; dp = dp->next) {
1295         if(dp->todo == -1)
1296             dp->todo = 0;
1297     }
1298
1299     return errstr;
1300 }
1301
1302 netif_t *
1303 disklist_netifs(void)
1304 {
1305     return all_netifs;
1306 }
1307
1308 #ifdef TEST
1309
1310 static void dump_disk(const disk_t *);
1311 static void dump_disklist(const disklist_t *);
1312 int main(int, char *[]);
1313
1314 static void
1315 dump_disk(
1316     const disk_t *      dp)
1317 {
1318     g_printf(_("  DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n"),
1319            dp->name, dp->host->hostname, dp->line, dp->dtype_name,
1320            dp->name == NULL? "(null)": dp->name,
1321            dp->spindle);
1322 }
1323
1324 static void
1325 dump_disklist(
1326     const disklist_t *  lst)
1327 {
1328     const disk_t *dp, *prev;
1329     const am_host_t *hp;
1330
1331     if(hostlist == NULL) {
1332         g_printf(_("DISKLIST not read in\n"));
1333         return;
1334     }
1335
1336     g_printf(_("DISKLIST BY HOSTNAME:\n"));
1337
1338     for(hp = hostlist; hp != NULL; hp = hp->next) {
1339         char *if_name = NULL;
1340         if (hp->netif && hp->netif->config)
1341             if_name = interface_name(hp->netif->config);
1342
1343         g_printf(_("HOST %s INTERFACE %s\n"),
1344                hp->hostname,
1345                if_name ? _("(null)") : if_name);
1346         for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
1347             dump_disk(dp);
1348         putchar('\n');
1349     }
1350
1351
1352     g_printf(_("DISKLIST IN FILE ORDER:\n"));
1353
1354     prev = NULL;
1355     for(dp = lst->head; dp != NULL; prev = dp, dp = dp->next) {
1356         dump_disk(dp);
1357         /* check pointers */
1358         if(dp->prev != prev) g_printf(_("*** prev pointer mismatch!\n"));
1359         if(dp->next == NULL && lst->tail != dp) g_printf(_("tail mismatch!\n"));
1360     }
1361 }
1362
1363 int
1364 main(
1365     int         argc,
1366     char **     argv)
1367 {
1368   char *conffile;
1369   char *conf_diskfile;
1370   disklist_t lst;
1371   int result;
1372
1373   /*
1374    * Configure program for internationalization:
1375    *   1) Only set the message locale for now.
1376    *   2) Set textdomain for all amanda related programs to "amanda"
1377    *      We don't want to be forced to support dozens of message catalogs.
1378    */  
1379   setlocale(LC_MESSAGES, "C");
1380   textdomain("amanda"); 
1381
1382   safe_fd(-1, 0);
1383
1384   set_pname("diskfile");
1385
1386   dbopen(DBG_SUBDIR_SERVER);
1387
1388   /* Don't die when child closes pipe */
1389   signal(SIGPIPE, SIG_IGN);
1390
1391   if (argc>1) {
1392     config_name = argv[1];
1393     if (strchr(config_name, '/') != NULL) {
1394       config_dir = stralloc2(argv[1], "/");
1395       config_name = strrchr(config_name, '/') + 1;
1396     } else {
1397       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1398     }
1399   } else {
1400     config_dir = stralloc("");
1401   }
1402   conffile = stralloc2(config_dir, CONFFILE_NAME);
1403   if((result = read_conffile(conffile)) == 0) {
1404     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
1405     result = read_diskfile(conf_diskfile, &lst);
1406     if(result == 0) {
1407       dump_disklist(&lst);
1408     }
1409     amfree(conf_diskfile);
1410   }
1411   amfree(conffile);
1412   amfree(config_dir);
1413
1414   return result;
1415 }
1416 #endif /* TEST */