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