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