Imported Upstream version 2.4.5
[debian/amanda] / server-src / infofile.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: infofile.c,v 1.44.4.4.8.2 2005/03/16 18:15:28 martinea Exp $
29  *
30  * manage current info file
31  */
32 #include "amanda.h"
33 #include "conffile.h"
34 #include "infofile.h"
35 #include "token.h"
36
37 #ifdef TEXTDB
38   static char *infodir = (char *)0;
39   static char *infofile = (char *)0;
40   static char *newinfofile;
41   static int writing;
42 #else
43 #  define MAX_KEY 256
44 /*#  define HEADER      (sizeof(info_t)-DUMP_LEVELS*sizeof(stats_t))*/
45
46   static DBM *infodb = NULL;
47   static lockfd = -1;
48 #endif
49
50 #ifdef TEXTDB
51
52 FILE *open_txinfofile(host, disk, mode)
53 char *host;
54 char *disk;
55 char *mode;
56 {
57     FILE *infof;
58
59     assert(infofile == (char *)0);
60
61     writing = (*mode == 'w');
62
63     host = sanitise_filename(host);
64     disk = sanitise_filename(disk);
65
66     infofile = vstralloc(infodir,
67                          "/", host,
68                          "/", disk,
69                          "/info",
70                          NULL);
71
72     amfree(host);
73     amfree(disk);
74
75     /* create the directory structure if in write mode */
76     if (writing) {
77         if (mkpdir(infofile, 02755, (uid_t)-1, (gid_t)-1) == -1) {
78             amfree(infofile);
79             return NULL;
80         }
81     }
82
83     newinfofile = stralloc2(infofile, ".new");
84
85     if(writing) {
86         infof = fopen(newinfofile, mode);
87         if(infof != NULL)
88             amflock(fileno(infof), "info");
89     }
90     else {
91         infof = fopen(infofile, mode);
92         /* no need to lock readers */
93     }
94
95     if(infof == (FILE *)0) {
96         amfree(infofile);
97         amfree(newinfofile);
98         return NULL;
99     }
100
101     return infof;
102 }
103
104 int close_txinfofile(infof)
105 FILE *infof;
106 {
107     int rc = 0;
108
109     assert(infofile != (char *)0);
110
111     if(writing) {
112         rc = rename(newinfofile, infofile);
113
114         amfunlock(fileno(infof), "info");
115     }
116
117     amfree(infofile);
118     amfree(newinfofile);
119
120     rc = rc || fclose(infof);
121     infof = NULL;
122     if (rc) rc = -1;
123
124     return rc;
125 }
126
127 int read_txinfofile(infof, info) /* XXX - code assumes AVG_COUNT == 3 */
128 FILE *infof;
129 info_t *info;
130 {
131     char *line = NULL;
132     int version;
133     int rc;
134     perf_t *pp;
135     char *s;
136     int ch;
137     int nb_history;
138     int i;
139
140     /* get version: command: lines */
141
142     if((line = agets(infof)) == NULL) return -1;
143     rc = sscanf(line, "version: %d", &version);
144     amfree(line);
145     if(rc != 1) return -2;
146
147     if((line = agets(infof)) == NULL) return -1;
148     rc = sscanf(line, "command: %d", &info->command);
149     amfree(line);
150     if(rc != 1) return -2;
151
152     /* get rate: and comp: lines for full dumps */
153
154     pp = &info->full;
155
156     if((line = agets(infof)) == NULL) return -1;
157     rc = sscanf(line, "full-rate: %f %f %f",
158                 &pp->rate[0], &pp->rate[1], &pp->rate[2]);
159     amfree(line);
160     if(rc > 3) return -2;
161
162     if((line = agets(infof)) == NULL) return -1;
163     rc = sscanf(line, "full-comp: %f %f %f",
164                 &pp->comp[0], &pp->comp[1], &pp->comp[2]);
165     amfree(line);
166     if(rc > 3) return -2;
167
168     /* get rate: and comp: lines for incr dumps */
169
170     pp = &info->incr;
171
172     if((line = agets(infof)) == NULL) return -1;
173     rc = sscanf(line, "incr-rate: %f %f %f",
174                 &pp->rate[0], &pp->rate[1], &pp->rate[2]);
175     amfree(line);
176     if(rc > 3) return -2;
177
178     if((line = agets(infof)) == NULL) return -1;
179     rc = sscanf(line, "incr-comp: %f %f %f",
180                 &pp->comp[0], &pp->comp[1], &pp->comp[2]);
181     amfree(line);
182     if(rc > 3) return -2;
183
184     /* get stats for dump levels */
185
186     for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
187         stats_t onestat;        /* one stat record */
188         long date;
189         int level;
190
191         if(line[0] == '/' && line[1] == '/') {
192             rc = 0;
193             amfree(line);
194             return 0;                           /* normal end of record */
195         }
196         else if (strncmp(line,"last_level:",11) == 0) {
197             break;                              /* normal */
198         }
199         else if (strncmp(line,"history:",8) == 0) {
200             break;                              /* normal */
201         }
202         memset(&onestat, 0, sizeof(onestat));
203
204         s = line;
205         ch = *s++;
206
207 #define sc "stats:"
208         if(strncmp(line, sc, sizeof(sc)-1) != 0) {
209             break;
210         }
211         s += sizeof(sc)-1;
212         ch = s[-1];
213 #undef sc
214
215         skip_whitespace(s, ch);
216         if(ch == '\0' || sscanf((s - 1), "%d", &level) != 1) {
217             break;
218         }
219         skip_integer(s, ch);
220
221         skip_whitespace(s, ch);
222         if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.size) != 1) {
223             break;
224         }
225         skip_integer(s, ch);
226
227         skip_whitespace(s, ch);
228         if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.csize) != 1) {
229             break;
230         }
231         skip_integer(s, ch);
232
233         skip_whitespace(s, ch);
234         if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.secs) != 1) {
235             break;
236         }
237         skip_integer(s, ch);
238
239         skip_whitespace(s, ch);
240         if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
241             break;
242         }
243         skip_integer(s, ch);
244
245         skip_whitespace(s, ch);
246         if(ch != '\0') {
247             if(sscanf((s - 1), "%d", &onestat.filenum) != 1) {
248                 break;
249             }
250             skip_integer(s, ch);
251
252             skip_whitespace(s, ch);
253             if(ch == '\0') {
254                 break;
255             }
256             strncpy(onestat.label, s-1, sizeof(onestat.label)-1);
257             onestat.label[sizeof(onestat.label)-1] = '\0';
258         }
259
260         onestat.date = date;    /* time_t not guarranteed to be long */
261
262         if(level < 0 || level > DUMP_LEVELS-1) break;
263
264         info->inf[level] = onestat;
265     }
266    
267     if(line == NULL) return -1;
268
269     rc = sscanf(line, "last_level: %d %d", 
270                 &info->last_level, &info->consecutive_runs);
271                 
272     amfree(line);
273     if(rc > 2) return -2;
274     rc = 0;
275
276     nb_history = 0;
277     for(i=0;i<=NB_HISTORY+1;i++) {
278         info->history[i].level = -2;
279     }
280     for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
281         history_t onehistory;   /* one history record */
282         long date;
283
284         if(line[0] == '/' && line[1] == '/') {
285             info->history[nb_history].level = -2;
286             rc = 0;
287             amfree(line);
288             return 0;                           /* normal end of record */
289         }
290
291         memset(&onehistory, 0, sizeof(onehistory));
292
293         s = line;
294         ch = *s++;
295
296 #define sc "history:"
297         if(strncmp(line, sc, sizeof(sc)-1) != 0) {
298             break;
299         }
300         s += sizeof(sc)-1;
301         ch = s[-1];
302 #undef sc
303
304         skip_whitespace(s, ch);
305         if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
306             break;
307         }
308         skip_integer(s, ch);
309
310         skip_whitespace(s, ch);
311         if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.size) != 1) {
312             break;
313         }
314         skip_integer(s, ch);
315
316         skip_whitespace(s, ch);
317         if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.csize) != 1) {
318             break;
319         }
320         skip_integer(s, ch);
321
322         skip_whitespace(s, ch);
323         if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
324             break;
325         }
326         skip_integer(s, ch);
327
328         onehistory.date = date; /* time_t not guarranteed to be long */
329
330         onehistory.secs = -1;
331         skip_whitespace(s, ch);
332         if(ch != '\0') {
333             if(sscanf((s - 1), "%ld", &onehistory.secs) != 1) {
334                 break;
335             }
336             skip_integer(s, ch);
337         }
338
339         info->history[nb_history++] = onehistory;
340     }
341
342     if((line = agets(infof)) == NULL) return -1; /* // line */
343     amfree(line);
344
345     return rc;
346 }
347
348 int write_txinfofile(infof, info)
349 FILE *infof;
350 info_t *info;
351 {
352     int i;
353     stats_t *sp;
354     perf_t *pp;
355     int level;
356
357     fprintf(infof, "version: %d\n", 0);
358
359     fprintf(infof, "command: %d\n", info->command);
360
361     pp = &info->full;
362
363     fprintf(infof, "full-rate:");
364     for(i=0; i<AVG_COUNT; i++)
365         if(pp->rate[i] >= 0.0)
366             fprintf(infof, " %f", pp->rate[i]);
367     fprintf(infof, "\n");
368
369     fprintf(infof, "full-comp:");
370     for(i=0; i<AVG_COUNT; i++)
371         if(pp->comp[i] >= 0.0)
372             fprintf(infof, " %f", pp->comp[i]);
373     fprintf(infof, "\n");
374
375     pp = &info->incr;
376
377     fprintf(infof, "incr-rate:");
378     for(i=0; i<AVG_COUNT; i++)
379         if(pp->rate[i] >= 0.0)
380             fprintf(infof, " %f", pp->rate[i]);
381     fprintf(infof, "\n");
382
383     fprintf(infof, "incr-comp:");
384     for(i=0; i<AVG_COUNT; i++)
385         if(pp->comp[i] >= 0.0)
386             fprintf(infof, " %f", pp->comp[i]);
387     fprintf(infof, "\n");
388
389     for(level=0; level<DUMP_LEVELS; level++) {
390         sp = &info->inf[level];
391
392         if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
393
394         fprintf(infof, "stats: %d %ld %ld %ld %ld", level,
395                sp->size, sp->csize, sp->secs, (long)sp->date);
396         if(sp->label[0] != '\0')
397             fprintf(infof, " %d %s", sp->filenum, sp->label);
398         fprintf(infof, "\n");
399     }
400
401     fprintf(infof, "last_level: %d %d\n", info->last_level, info->consecutive_runs);
402
403     for(i=0;info->history[i].level > -1;i++) {
404         fprintf(infof, "history: %d %ld %ld %ld %ld\n",info->history[i].level,
405                 info->history[i].size, info->history[i].csize,
406                 info->history[i].date, info->history[i].secs);
407     }
408     fprintf(infof, "//\n");
409
410     return 0;
411 }
412
413 int delete_txinfofile(host, disk)
414 char *host;
415 char *disk;
416 {
417     char *fn = NULL, *fn_new = NULL;
418     int rc;
419
420     host = sanitise_filename(host);
421     disk = sanitise_filename(disk);
422     fn = vstralloc(infodir,
423                    "/", host,
424                    "/", disk,
425                    "/info",
426                    NULL);
427     fn_new = stralloc2(fn, ".new");
428
429     amfree(host);
430     amfree(disk);
431
432     unlink(fn_new);
433     amfree(fn_new);
434
435     rc = rmpdir(fn, infodir);
436     amfree(fn);
437
438     return rc;
439 }
440 #endif
441
442 #ifndef TEXTDB
443 static char *lockname = NULL;
444 #endif
445
446 int open_infofile(filename)
447 char *filename;
448 {
449 #ifdef TEXTDB
450     assert(infodir == (char *)0);
451
452     infodir = stralloc(filename);
453
454     return 0; /* success! */
455 #else
456     /* lock the dbm file */
457
458     lockname = newstralloc2(lockname, filename, ".lck");
459     if((lockfd = open(lockname, O_CREAT|O_RDWR, 0644)) == -1)
460         return 2;
461
462     if(amflock(lockfd, "info") == -1) {
463         aclose(lockfd);
464         unlink(lockname);
465         return 3;
466     }
467
468     if(!(infodb = dbm_open(filename, O_CREAT|O_RDWR, 0644))) {
469         amfunlock(lockfd, "info");
470         aclose(lockfd);
471         unlink(lockname);
472         return 1;
473     }
474
475     return (infodb == NULL);    /* return 1 on error */
476 #endif
477 }
478
479 void close_infofile()
480 {
481 #ifdef TEXTDB
482     assert(infodir != (char *)0);
483
484     amfree(infodir);
485 #else
486     dbm_close(infodb);
487
488     if(amfunlock(lockfd, "info") == -1)
489         error("could not unlock infofile: %s", strerror(errno));
490
491     aclose(lockfd);
492     lockfd = -1;
493
494     unlink(lockname);
495 #endif
496 }
497
498 /* Convert a dump level to a GMT based time stamp */
499 char *get_dumpdate(info, lev)
500 info_t *info;
501 int lev;
502 {
503     static char stamp[20]; /* YYYY:MM:DD:hh:mm:ss */
504     int l;
505     time_t this, last;
506     struct tm *t;
507
508     last = EPOCH;
509
510     for(l = 0; l < lev; l++) {
511         this = info->inf[l].date;
512         if (this > last) last = this;
513     }
514
515     t = gmtime(&last);
516     ap_snprintf(stamp, sizeof(stamp), "%d:%d:%d:%d:%d:%d",
517                 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
518                 t->tm_hour, t->tm_min, t->tm_sec);
519
520     return stamp;
521 }
522
523 double perf_average(a, d)
524 /* Weighted average */
525 float *a;       /* array of items to average */
526 double d;       /* default value */
527 {
528     double sum; /* running total */
529     int n;      /* number of items in sum */
530     int w;      /* weight */
531     int i;      /* counter */
532
533     sum = 0.0;
534     n = 0;
535
536     for(i = 0; i < AVG_COUNT; i++) {
537         if(a[i] >= 0.0) {
538             w = AVG_COUNT - i;
539             sum += a[i] * w;
540             n += w;
541         }
542     }
543
544     if(n == 0) return d;
545     return sum / n;
546 }
547
548 void zero_info(info)
549 info_t *info;
550 {
551     int i;
552
553     memset(info, '\0', sizeof(info_t));
554
555     for(i = 0; i < AVG_COUNT; i++) {
556         info->full.comp[i] = info->incr.comp[i] = -1.0;
557         info->full.rate[i] = info->incr.rate[i] = -1.0;
558     }
559
560     for(i = 0; i < DUMP_LEVELS; i++) {
561         info->inf[i].date = (time_t)-1;
562     }
563
564     info->last_level = -1;
565     info->consecutive_runs = -1;
566
567     for(i=0;i<=NB_HISTORY;i++) {
568         info->history[i].level = -2;
569         info->history[i].size = 0;
570         info->history[i].csize = 0;
571         info->history[i].date = 0;
572     }
573     return;
574 }
575
576 int get_info(hostname, diskname, info)
577 char *hostname, *diskname;
578 info_t *info;
579 {
580     int rc;
581
582     (void) zero_info(info);
583
584     {
585 #ifdef TEXTDB
586         FILE *infof;
587
588         infof = open_txinfofile(hostname, diskname, "r");
589
590         if(infof == NULL) {
591             rc = -1; /* record not found */
592         }
593         else {
594             rc = read_txinfofile(infof, info);
595
596             close_txinfofile(infof);
597         }
598 #else
599         datum k, d;
600
601         /* setup key */
602
603         k.dptr = vstralloc(hostname, ":", diskname, NULL);
604         k.dsize = strlen(k.dptr)+1;
605
606         /* lookup record */
607
608         d = dbm_fetch(infodb, k);
609         amfree(k.dptr);
610         if(d.dptr == NULL) {
611             rc = -1; /* record not found */
612         }
613         else {
614             memcpy(info, d.dptr, d.dsize);
615             rc = 0;
616         }
617 #endif
618     }
619
620     return rc;
621 }
622
623
624 int get_firstkey(hostname, hostname_size, diskname, diskname_size)
625 char *hostname, *diskname;
626 int hostname_size, diskname_size;
627 {
628 #ifdef TEXTDB
629     assert(0);
630     return 0;
631 #else
632     datum k;
633     int rc;
634     char *s, *fp;
635     int ch;
636
637     k = dbm_firstkey(infodb);
638     if(k.dptr == NULL) return 0;
639
640     s = k.dptr;
641     ch = *s++;
642
643     skip_whitespace(s, ch);
644     if(ch == '\0') return 0;
645     fp = hostname;
646     while(ch && ch != ':') {
647         if(fp >= hostname+hostname_size-1) {
648             fp = NULL;
649             break;
650         }
651         *fp = ch;
652         ch = *s++;
653     }
654     if(fp == NULL) return 0;
655     *fp = '\0';
656
657     if(ch != ':') return 0;
658     ch = *s++;
659     copy_string(s, ch, diskname, diskname_size, fp);
660     if(fp == NULL) return 0;
661
662     return 1;
663 #endif
664 }
665
666
667 int get_nextkey(hostname, hostname_size, diskname, diskname_size)
668 char *hostname, *diskname;
669 int hostname_size, diskname_size;
670 {
671 #ifdef TEXTDB
672     assert(0);
673     return 0;
674 #else
675     datum k;
676     int rc;
677     char *s, *fp;
678     int ch;
679
680     k = dbm_nextkey(infodb);
681     if(k.dptr == NULL) return 0;
682
683     s = k.dptr;
684     ch = *s++;
685
686     skip_whitespace(s, ch);
687     if(ch == '\0') return 0;
688     fp = hostname;
689     while(ch && ch != ':') {
690         if(fp >= hostname+hostname_size-1) {
691             fp = NULL;
692             break;
693         }
694         *fp = ch;
695         ch = *s++;
696     }
697     if(fp == NULL) return 0;
698     *fp = '\0';
699
700     if(ch != ':') return 0;
701     ch = *s++;
702     copy_string(s, ch, diskname, diskname_size, fp);
703     if(fp == NULL) return 0;
704
705     return 1;
706 #endif
707 }
708
709
710 int put_info(hostname, diskname, info)
711      char *hostname, *diskname;
712      info_t *info;
713 {
714 #ifdef TEXTDB
715     FILE *infof;
716     int rc;
717
718     infof = open_txinfofile(hostname, diskname, "w");
719
720     if(infof == NULL) return -1;
721
722     rc = write_txinfofile(infof, info);
723
724     rc = rc || close_txinfofile(infof);
725
726     return rc;
727 #else
728     datum k, d;
729     int maxlev;
730
731     /* setup key */
732
733     k.dptr = vstralloc(hostname, ":", diskname, NULL);
734     k.dsize = strlen(k.dptr)+1;
735
736     d.dptr = (char *)info;
737     d.dsize = sizeof(info_t);
738
739     /* store record */
740
741     if(dbm_store(infodb, k, d, DBM_REPLACE) != 0) {
742         amfree(k.dptr);
743         return -1;
744     }
745
746     amfree(k.dptr);
747     return 0;
748 #endif
749 }
750
751
752 int del_info(hostname, diskname)
753 char *hostname, *diskname;
754 {
755 #ifdef TEXTDB
756     return delete_txinfofile(hostname, diskname);
757 #else
758     char key[MAX_KEY];
759     datum k;
760
761     /* setup key */
762
763     k.dptr = vstralloc(hostname, ":", diskname, NULL);
764     k.dsize = strlen(key)+1;
765
766     /* delete key and record */
767
768     if(dbm_delete(infodb, k) != 0) {
769         amfree(k.dptr);
770         return -1;
771     }
772     amfree(k.dptr);
773     return 0;
774 #endif
775 }
776
777
778 #ifdef TEST
779
780 void dump_rec(info)
781 info_t *info;
782 {
783     int i;
784     stats_t *sp;
785
786     printf("command word: %d\n", info->command);
787     printf("full dump rate (K/s) %5.1f, %5.1f, %5.1f\n",
788            info->full.rate[0],info->full.rate[1],info->full.rate[2]);
789     printf("full comp rate %5.1f, %5.1f, %5.1f\n",
790            info->full.comp[0]*100,info->full.comp[1]*100,info->full.comp[2]*100);
791     printf("incr dump rate (K/s) %5.1f, %5.1f, %5.1f\n",
792            info->incr.rate[0],info->incr.rate[1],info->incr.rate[2]);
793     printf("incr comp rate %5.1f, %5.1f, %5.1f\n",
794            info->incr.comp[0]*100,info->incr.comp[1]*100,info->incr.comp[2]*100);
795     for(i = 0; i < DUMP_LEVELS; i++) {
796         sp = &info->inf[i];
797         if( sp->size != -1) {
798
799             printf("lev %d date %ld tape %s filenum %d size %ld csize %ld secs %ld\n",
800                    i, (long)sp->date, sp->label, sp->filenum,
801                    sp->size, sp->csize, sp->secs);
802         }
803     }
804     putchar('\n');
805    printf("last_level: %d %d\n", info->last_level, info->consecutive_runs);
806 }
807
808 #ifdef TEXTDB
809 void dump_db(host, disk)
810 char *host, *disk;
811 {
812     info_t info;
813     int rc;
814
815     if((rc = get_info(host, disk, &info)) == 0) {
816         dump_rec(&info);
817     } else {
818         printf("cannot fetch information for %s:%s rc=%d\n", host, disk, rc);
819     }
820 }
821 #else
822 void dump_db(str)
823 char *str;
824 {
825     datum k,d;
826     int rec,r,num;
827     info_t info;
828
829
830     printf("info database %s:\n--------\n", str);
831     rec = 0;
832     k = dbm_firstkey(infodb);
833     while(k.dptr != NULL) {
834
835         printf("%3d: KEY %s =\n", rec, k.dptr);
836
837         d = dbm_fetch(infodb, k);
838         memset(&info, '\0', sizeof(info));
839         memcpy(&info, d.dptr, d.dsize);
840
841         num = (d.dsize-HEADER)/sizeof(stats_t);
842         dump_rec(&info);
843
844         k = dbm_nextkey(infodb);
845         rec++;
846     }
847     puts("--------\n");
848 }
849 #endif
850
851 int
852 main(argc, argv)
853 int argc;
854 char *argv[];
855 {
856   int i;
857   int fd;
858   unsigned long malloc_hist_1, malloc_size_1;
859   unsigned long malloc_hist_2, malloc_size_2;
860
861   for(fd = 3; fd < FD_SETSIZE; fd++) {
862     /*
863      * Make sure nobody spoofs us with a lot of extra open files
864      * that would cause an open we do to get a very high file
865      * descriptor, which in turn might be used as an index into
866      * an array (e.g. an fd_set).
867      */
868     close(fd);
869   }
870
871   set_pname("infofile");
872
873   malloc_size_1 = malloc_inuse(&malloc_hist_1);
874
875   for(i = 1; i < argc; ++i) {
876 #ifdef TEXTDB
877     if(i+1 >= argc) {
878       fprintf(stderr,"usage: %s host disk [host disk ...]\n",argv[0]);
879       return 1;
880     }
881     open_infofile("curinfo");
882     dump_db(argv[i], argv[i+1]);
883     i++;
884 #else
885     open_infofile(argv[i]);
886     dump_db(argv[i]);
887 #endif
888     close_infofile();
889   }
890
891   malloc_size_2 = malloc_inuse(&malloc_hist_2);
892
893   if(malloc_size_1 != malloc_size_2) {
894     malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
895   }
896
897   return 0;
898 }
899
900 #endif /* TEST */