2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: diskfile.c,v 1.27.4.6.4.3.2.15.2.5 2004/11/19 13:28:15 martinea Exp $
38 static disklist_t lst;
40 static char *diskfname = NULL;
41 static am_host_t *hostlist;
42 static int line_num, got_parserror;
45 static char *upcase P((char *st));
46 static int read_diskline P((void));
47 static void parserror P((char *format, ...))
48 __attribute__ ((format (printf, 1, 2)));
51 disklist_t *read_diskfile(filename)
59 lst.head = lst.tail = NULL;
60 diskfname = newstralloc(diskfname, filename);
61 malloc_mark(diskfname);
62 line_num = got_parserror = 0;
64 if((diskf = fopen(filename, "r")) == NULL)
65 error("could not open disklist file \"%s\": %s",
66 filename, strerror(errno));
68 while(read_diskline());
71 if(got_parserror) return NULL;
75 am_host_t *lookup_host(hostname)
80 for(p = hostlist; p != NULL; p = p->next) {
81 if(strcasecmp(p->hostname, hostname) == 0) return p;
86 disk_t *lookup_disk(hostname, diskname)
87 char *hostname, *diskname;
92 host = lookup_host(hostname);
93 if(host == NULL) return NULL;
95 for(disk = host->disks; disk != NULL; disk = disk->hostnext) {
96 if(strcmp(disk->name, diskname) == 0) return disk;
101 void enqueue_disk(list, disk) /* put disk on end of queue */
105 if(list->tail == NULL) list->head = disk;
106 else list->tail->next = disk;
107 disk->prev = list->tail;
113 void headqueue_disk(list, disk) /* put disk on head of queue */
117 if(list->head == NULL) list->tail = disk;
118 else list->head->prev = disk;
119 disk->next = list->head;
125 void insert_disk(list, disk, cmp) /* insert in sorted order */
128 int (*cmp) P((disk_t *a, disk_t *b));
136 if(cmp(disk, ptr) < 0) break;
143 if(prev == NULL) list->head = disk;
144 else prev->next = disk;
145 if(ptr == NULL) list->tail = disk;
146 else ptr->prev = disk;
149 disk_t *add_disk(hostname, diskname)
156 disk = alloc(sizeof(disk_t));
158 disk->name = stralloc(diskname);
159 disk->device = stralloc(diskname);
162 disk->compress = COMP_NONE;
166 host = lookup_host(hostname);
168 host = alloc(sizeof(am_host_t));
169 host->next = hostlist;
172 host->hostname = stralloc(hostname);
174 host->inprogress = 0;
179 host->features = NULL;
181 enqueue_disk(&lst, disk);
184 disk->hostnext = host->disks;
190 int find_disk(list, disk)
193 /* check if disk is present in list. Return true if so, false otherwise. */
197 for( t = list->head; t && t != disk; t = t->next );
202 void sort_disk(in, out, cmp) /* sort a whole queue */
205 int (*cmp) P((disk_t *a, disk_t *b));
210 tmp = in; /* just in case in == out */
212 out->head = (disk_t *)0;
213 out->tail = (disk_t *)0;
215 while((disk = dequeue_disk(tmp)))
216 insert_disk(out, disk, cmp);
219 disk_t *dequeue_disk(list) /* remove disk from front of queue */
224 if(list->head == NULL) return NULL;
227 list->head = disk->next;
229 if(list->head == NULL) list->tail = NULL;
230 else list->head->prev = NULL;
232 disk->prev = disk->next = NULL; /* for debugging */
236 void remove_disk(list, disk)
240 if(disk->prev == NULL) list->head = disk->next;
241 else disk->prev->next = disk->next;
243 if(disk->next == NULL) list->tail = disk->prev;
244 else disk->next->prev = disk->prev;
246 disk->prev = disk->next = NULL;
249 static char *upcase(st)
255 if(islower((int)*s)) *s = toupper(*s);
262 static int read_diskline()
267 interface_t *netif = 0;
268 char *hostname = NULL;
269 char *diskname, *diskdevice;
270 static char *line = NULL;
272 int ch = '\0', dup = 0;
276 for(; (line = agets(diskf)) != NULL; free(line)) {
281 skip_whitespace(s, ch);
282 if(ch != '\0' && ch != '#') break;
284 if(line == NULL) return 0;
287 skip_non_whitespace(s, ch);
289 host = lookup_host(fp);
291 hostname = stralloc(fp);
292 malloc_mark(hostname);
294 hostname = host->hostname;
297 skip_whitespace(s, ch);
298 if(ch == '\0' || ch == '#') {
299 parserror("disk device name expected");
303 skip_non_whitespace(s, ch);
305 diskname = stralloc(fp);
307 skip_whitespace(s, ch);
308 if(ch == '\0' || ch == '#') {
309 parserror("disk dumptype expected");
310 if(host == NULL) amfree(hostname);
315 skip_non_whitespace(s, ch);
320 if(fp[0] != '{' && (dtype = lookup_dumptype(upcase(fp))) == NULL) {
322 skip_whitespace(s, ch);
323 if(ch == '\0' || ch == '#') {
324 parserror("disk dumptype expected");
325 if(host == NULL) amfree(hostname);
331 skip_non_whitespace(s, ch);
339 /* check for duplicate disk */
340 if(host && (disk = lookup_disk(hostname, diskname)) != NULL) {
341 parserror("duplicate disk record, previous on line %d", disk->line);
344 disk = alloc(sizeof(disk_t));
346 disk->line = line_num;
347 disk->name = diskname;
348 disk->device = diskdevice;
349 malloc_mark(disk->name);
352 disk->inprogress = 0;
358 skip_whitespace(s, ch);
359 if (ch != '\0' && ch != '#') {
360 parserror("expected line break after `{\', ignoring rest of line");
363 if (strchr(s-1, '}') &&
364 (strchr(s-1, '#') == NULL ||
365 strchr(s-1, '}') < strchr(s-1, '#'))) {
366 if(host == NULL) amfree(hostname);
375 dtype = read_dumptype(vstralloc("custom(", hostname,
376 ":", disk->name, ")", 0),
377 diskf, diskfname, &line_num);
380 /* line_num += 1; */ /* read_dumptype did it already */
382 if (dtype == NULL || dup) {
383 if(host == NULL) amfree(hostname);
395 } else if((dtype = lookup_dumptype(upcase(fp))) == NULL) {
396 parserror("undefined dumptype `%s'", fp);
397 if(host == NULL) amfree(hostname);
406 if(host == NULL) amfree(hostname);
410 disk->dtype_name = dtype->name;
411 disk->program = dtype->program;
412 disk->exclude_file = duplicate_sl(dtype->exclude_file);
413 disk->exclude_list = duplicate_sl(dtype->exclude_list);
414 disk->include_file = duplicate_sl(dtype->include_file);
415 disk->include_list = duplicate_sl(dtype->include_list);
416 disk->exclude_optional = dtype->exclude_optional;
417 disk->include_optional = dtype->include_optional;
418 disk->priority = dtype->priority;
419 disk->dumpcycle = dtype->dumpcycle;
420 disk->frequency = dtype->frequency;
421 disk->auth = dtype->auth;
422 disk->maxdumps = dtype->maxdumps;
423 disk->maxpromoteday = dtype->maxpromoteday;
424 disk->bumppercent = dtype->bumppercent;
425 disk->bumpsize = dtype->bumpsize;
426 disk->bumpdays = dtype->bumpdays;
427 disk->bumpmult = dtype->bumpmult;
428 disk->start_t = dtype->start_t;
429 disk->strategy = dtype->strategy;
430 disk->estimate = dtype->estimate;
431 disk->compress = dtype->compress;
432 disk->comprate[0] = dtype->comprate[0];
433 disk->comprate[1] = dtype->comprate[1];
434 disk->record = dtype->record;
435 disk->skip_incr = dtype->skip_incr;
436 disk->skip_full = dtype->skip_full;
437 disk->no_hold = dtype->no_hold;
438 disk->kencrypt = dtype->kencrypt;
439 disk->index = dtype->index;
442 skip_whitespace(s, ch);
444 if(ch && ch != '#') { /* get optional spindle number */
447 skip_non_whitespace(s, ch);
450 if (*fp1 == '-') fp1++;
451 for(;*fp1!='\0';fp1++) {
457 parserror("non-integer spindle `%s'", fp);
458 if(host == NULL) amfree(hostname);
463 disk->spindle = atoi(fp);
467 skip_whitespace(s, ch);
469 if(ch && ch != '#') { /* get optional network interface */
470 skip_non_whitespace(s, ch);
472 if((netif = lookup_interface(upcase(fp))) == NULL) {
473 parserror("undefined network interface `%s'", fp);
474 if(host == NULL) amfree(hostname);
480 netif = lookup_interface("");
483 skip_whitespace(s, ch);
484 if(ch && ch != '#') { /* now we have garbage, ignore it */
485 parserror("end of line expected");
488 if(dtype->ignore || dtype->strategy == DS_SKIP) {
490 free_sl(disk->exclude_file);
491 free_sl(disk->exclude_list);
492 free_sl(disk->include_file);
493 free_sl(disk->include_list);
498 /* success, add disk to lists */
500 if(host == NULL) { /* new host */
501 host = alloc(sizeof(am_host_t));
503 host->next = hostlist;
506 host->hostname = hostname;
509 host->inprogress = 0;
510 host->maxdumps = 1; /* will be overwritten */
514 host->features = NULL;
519 enqueue_disk(&lst, disk);
522 disk->hostnext = host->disks;
524 host->maxdumps = disk->maxdumps;
530 printf_arglist_function(static void parserror, char *, format)
534 /* print error message */
536 fprintf(stderr, "\"%s\", line %d: ", diskfname, line_num);
537 arglist_start(argp, format);
538 vfprintf(stderr, format, argp);
546 void dump_queue(st, q, npr, f)
549 int npr; /* we print first npr disks on queue, plus last two */
556 fprintf(f, "%s QUEUE: empty\n", st);
559 fprintf(f, "%s QUEUE:\n", st);
560 for(pos = 0, d = q.head, p = NULL; d != NULL; p = d, d = d->next, pos++) {
561 if(pos < npr) fprintf(f, "%3d: %-10s %-4s\n",
562 pos, d->host->hostname, d->name);
565 if(pos > npr+2) fprintf(f, " ...\n");
568 fprintf(f, "%3d: %-10s %-4s\n", pos-2, d->host->hostname, d->name);
571 fprintf(f, "%3d: %-10s %-4s\n", pos-1, d->host->hostname, d->name);
575 char *optionstr(dp, their_features, fdout)
577 am_feature_t * their_features;
580 char *auth_opt = NULL;
581 char *kencrypt_opt = "";
582 char *compress_opt = "";
583 char *record_opt = "";
584 char *index_opt = "";
585 char *exclude_file = NULL;
586 char *exclude_list = NULL;
587 char *include_file = NULL;
588 char *include_list = NULL;
597 /* modification by BIS@BBN 4/25/2003:
598 * The first "if" statement has a number of problems, so I removed the
599 * am_has_feature(dp->host->features, fe_options_auth)
600 * condition and caused the first case to always fail.
601 * 1) If dp->host is a NULL pointer, subsequent tests which look
602 * at dp->host->features or dp->host->hostname will cause a dump.
603 * It appears that there should be an assertion that dp->host
605 * 2) The code which checks for the kencrypt feature is only executed
606 * in the case where dp->auth == AUTH_KRB4. If we enable Kerberos IV
607 * in the first case, then the code checking for kencrypt will never
609 * 3) The option processing code in server-src/dumper.c and
610 * client-src/sendbackup.c do not yet handle the "auth=krb4"
611 * option style of specifying Kerberos IV.
612 * Getting rid of the whole first case seems to take care of problems
613 * 2 and 3, but not problem 1.
616 /* && am_has_feature(dp->host->features, fe_options_auth)) */ {
617 auth_opt = stralloc("auth=");
618 if(dp->auth == AUTH_BSD) {
619 strappend(auth_opt, "bsd");
620 } else if(dp->auth == AUTH_KRB4) {
621 strappend(auth_opt, "krb4");
623 strappend(auth_opt, "unknown");
625 strappend(auth_opt, ";");
626 } else if(dp->auth == AUTH_BSD) {
627 if(am_has_feature(dp->host->features, fe_options_bsd_auth)) {
628 auth_opt = stralloc("bsd-auth;");
631 fprintf(fdout, "WARNING: %s:%s does not support bsd-auth\n",
632 dp->host->hostname, dp->name);
634 } else if(dp->auth == AUTH_KRB4) {
635 if(am_has_feature(dp->host->features, fe_options_krb4_auth)) {
636 auth_opt = stralloc("krb4-auth;");
639 fprintf(fdout, "WARNING: %s:%s does not support krb4-auth\n",
640 dp->host->hostname, dp->name);
643 if(am_has_feature(dp->host->features, fe_options_kencrypt)) {
644 kencrypt_opt = "kencrypt;";
648 "WARNING: %s:%s does not support kencrypt\n",
649 dp->host->hostname, dp->name);
654 switch(dp->compress) {
656 if(am_has_feature(their_features, fe_options_compress_fast)) {
657 compress_opt = "compress-fast;";
661 "WARNING: %s:%s does not support fast compression\n",
662 dp->host->hostname, dp->name);
666 if(am_has_feature(their_features, fe_options_compress_best)) {
667 compress_opt = "compress-best;";
671 "WARNING: %s:%s does not support best compression\n",
672 dp->host->hostname, dp->name);
676 if(am_has_feature(their_features, fe_options_srvcomp_fast)) {
677 compress_opt = "srvcomp-fast;";
681 if(am_has_feature(their_features, fe_options_srvcomp_best)) {
682 compress_opt = "srvcomp-best;";
688 if(am_has_feature(their_features, fe_options_no_record)) {
689 record_opt = "no-record;";
692 fprintf(fdout, "WARNING: %s:%s does not support no record\n",
693 dp->host->hostname, dp->name);
698 if(am_has_feature(their_features, fe_options_index)) {
699 index_opt = "index;";
702 fprintf(fdout, "WARNING: %s:%s does not support index\n",
703 dp->host->hostname, dp->name);
707 exclude_file = stralloc("");
709 if(dp->exclude_file != NULL && dp->exclude_file->nb_element > 0) {
710 nb_exclude_file = dp->exclude_file->nb_element;
711 if(am_has_feature(their_features, fe_options_exclude_file)) {
712 if(am_has_feature(their_features, fe_options_multiple_exclude) ||
713 dp->exclude_file->nb_element == 1) {
714 for(excl = dp->exclude_file->first; excl != NULL;
716 exc = newvstralloc( exc, "exclude-file=", excl->name,
718 strappend(exclude_file, exc);
721 exc = newvstralloc(exc, "exclude-file=",
722 dp->exclude_file->last->name, ";", NULL);
723 strappend(exclude_file, exc);
726 "WARNING: %s:%s does not support multiple exclude\n",
727 dp->host->hostname, dp->name);
731 fprintf(fdout, "WARNING: %s:%s does not support exclude file\n",
732 dp->host->hostname, dp->name);
735 exclude_list = stralloc("");
736 if(dp->exclude_list != NULL && dp->exclude_list->nb_element > 0) {
737 if(am_has_feature(their_features, fe_options_exclude_list)) {
738 if(am_has_feature(their_features, fe_options_multiple_exclude) ||
739 (dp->exclude_list->nb_element == 1 && nb_exclude_file == 0)) {
740 for(excl = dp->exclude_list->first; excl != NULL;
742 exc = newvstralloc( exc, "exclude-list=", excl->name,
744 strappend(exclude_list, exc);
747 exc = newvstralloc(exc, "exclude-list=",
748 dp->exclude_list->last->name, ";", NULL);
749 strappend(exclude_list, exc);
752 "WARNING: %s:%s does not support multiple exclude\n",
753 dp->host->hostname, dp->name);
757 fprintf(fdout, "WARNING: %s:%s does not support exclude list\n",
758 dp->host->hostname, dp->name);
762 include_file = stralloc("");
764 if(dp->include_file != NULL && dp->include_file->nb_element > 0) {
765 nb_include_file = dp->include_file->nb_element;
766 if(am_has_feature(their_features, fe_options_include_file)) {
767 if(am_has_feature(their_features, fe_options_multiple_include) ||
768 dp->include_file->nb_element == 1) {
769 for(excl = dp->include_file->first; excl != NULL;
771 exc = newvstralloc( exc, "include-file=", excl->name,
773 strappend(include_file, exc);
776 exc = newvstralloc(exc, "include-file=",
777 dp->include_file->last->name, ";", NULL);
778 strappend(include_file, exc);
781 "WARNING: %s:%s does not support multiple include\n",
782 dp->host->hostname, dp->name);
786 fprintf(fdout, "WARNING: %s:%s does not support include file\n",
787 dp->host->hostname, dp->name);
790 include_list = stralloc("");
791 if(dp->include_list != NULL && dp->include_list->nb_element > 0) {
792 if(am_has_feature(their_features, fe_options_include_list)) {
793 if(am_has_feature(their_features, fe_options_multiple_include) ||
794 (dp->include_list->nb_element == 1 && nb_include_file == 0)) {
795 for(excl = dp->include_list->first; excl != NULL;
797 exc = newvstralloc( exc, "include-list=", excl->name,
799 strappend(include_list, exc);
802 exc = newvstralloc(exc, "include-list=",
803 dp->include_list->last->name, ";", NULL);
804 strappend(include_list, exc);
807 "WARNING: %s:%s does not support multiple include\n",
808 dp->host->hostname, dp->name);
812 fprintf(fdout, "WARNING: %s:%s does not support include list\n",
813 dp->host->hostname, dp->name);
817 if(dp->exclude_optional) {
818 if(am_has_feature(their_features, fe_options_optional_exclude)) {
819 excl_opt = "exclude-optional;";
823 "WARNING: %s:%s does not support optional exclude\n",
824 dp->host->hostname, dp->name);
827 if(dp->include_optional) {
828 if(am_has_feature(their_features, fe_options_optional_include)) {
829 incl_opt = "include-optional;";
833 "WARNING: %s:%s does not support optional include\n",
834 dp->host->hostname, dp->name);
838 result = vstralloc(";",
852 amfree(exclude_file);
853 amfree(exclude_list);
854 amfree(include_file);
855 amfree(include_list);
862 void match_disklist(disklist_t *origqp, int sargc, char **sargv)
864 char *prevhost = NULL;
874 for(dp = origqp->head; dp != NULL; dp = dp->next) {
880 for(i=0;i<sargc;i++) {
882 for(dp = origqp->head; dp != NULL; dp = dp->next) {
883 if(match_host(sargv[i], dp->host->hostname))
887 for(dp = origqp->head; dp != NULL; dp = dp->next) {
888 if(prevhost != NULL &&
889 match_host(prevhost, dp->host->hostname) &&
890 (match_disk(sargv[i], dp->name) ||
891 (dp->device && match_disk(sargv[i], dp->device)))) {
893 error("Argument %s match a host and a disk",sargv[i]);
905 if(match_a_host == 1) {
906 if(prev_match == 1) { /* all disk of the previous host */
907 for(dp = origqp->head; dp != NULL; dp = dp->next) {
908 if(match_host(prevhost,dp->host->hostname))
918 /*error("%s match nothing",sargv[i]);*/
923 if(prev_match == 1) { /* all disk of the previous host */
924 for(dp = origqp->head; dp != NULL; dp = dp->next) {
925 if(match_host(prevhost,dp->host->hostname))
931 for(dp = origqp->head; dp != NULL; dp = dp->next) {
944 printf(" DISK %s (HOST %s, LINE %d) TYPE %s NAME %s SPINDLE %d\n",
945 dp->name, dp->host->hostname, dp->line, dp->dtype_name,
946 dp->name == NULL? "(null)": dp->name,
956 if(hostlist == NULL) {
957 printf("DISKLIST not read in\n");
961 printf("DISKLIST BY HOSTNAME:\n");
963 for(hp = hostlist; hp != NULL; hp = hp->next) {
964 printf("HOST %s INTERFACE %s\n",
966 (hp->netif == NULL||hp->netif->name == NULL) ? "(null)"
968 for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
974 printf("DISKLIST IN FILE ORDER:\n");
977 for(dp = lst.head; dp != NULL; prev = dp, dp = dp->next) {
980 if(dp->prev != prev) printf("*** prev pointer mismatch!\n");
981 if(dp->next == NULL && lst.tail != dp) printf("tail mismatch!\n");
994 unsigned long malloc_hist_1, malloc_size_1;
995 unsigned long malloc_hist_2, malloc_size_2;
997 for(fd = 3; fd < FD_SETSIZE; fd++) {
999 * Make sure nobody spoofs us with a lot of extra open files
1000 * that would cause an open we do to get a very high file
1001 * descriptor, which in turn might be used as an index into
1002 * an array (e.g. an fd_set).
1007 set_pname("diskfile");
1009 malloc_size_1 = malloc_inuse(&malloc_hist_1);
1012 config_name = argv[1];
1013 if (strchr(config_name, '/') != NULL) {
1014 config_dir = stralloc2(argv[1], "/");
1015 config_name = strrchr(config_name, '/') + 1;
1017 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1020 config_dir = stralloc("");
1022 conffile = stralloc2(config_dir, CONFFILE_NAME);
1023 if((result = read_conffile(conffile)) == 0) {
1024 conf_diskfile = getconf_str(CNF_DISKFILE);
1025 if (*conf_diskfile == '/') {
1026 conf_diskfile = stralloc(conf_diskfile);
1028 conf_diskfile = stralloc2(config_dir, conf_diskfile);
1030 if((result = (read_diskfile(conf_diskfile) == NULL)) == 0) {
1033 amfree(conf_diskfile);
1038 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1040 if(malloc_size_1 != malloc_size_2) {
1041 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);