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