2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: infofile.c,v 1.57 2006/03/10 11:56:06 martinea Exp $
30 * manage current info file
37 static void zero_info P((info_t *));
40 static char *infodir = (char *)0;
41 static char *infofile = (char *)0;
42 static char *newinfofile;
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 *));
52 /*# define HEADER (sizeof(info_t)-DUMP_LEVELS*sizeof(stats_t))*/
54 static DBM *infodb = NULL;
60 static FILE *open_txinfofile(host, disk, mode)
67 assert(infofile == (char *)0);
69 writing = (*mode == 'w');
71 host = sanitise_filename(host);
72 disk = sanitise_filename(disk);
74 infofile = vstralloc(infodir,
83 /* create the directory structure if in write mode */
85 if (mkpdir(infofile, 02755, (uid_t)-1, (gid_t)-1) == -1) {
91 newinfofile = stralloc2(infofile, ".new");
94 infof = fopen(newinfofile, mode);
96 amflock(fileno(infof), "info");
99 infof = fopen(infofile, mode);
100 /* no need to lock readers */
103 if(infof == (FILE *)0) {
112 static int close_txinfofile(infof)
117 assert(infofile != (char *)0);
120 rc = rename(newinfofile, infofile);
122 amfunlock(fileno(infof), "info");
128 rc = rc || fclose(infof);
135 static int read_txinfofile(infof, info) /* XXX - code assumes AVG_COUNT == 3 */
148 /* get version: command: lines */
150 if((line = agets(infof)) == NULL) return -1;
151 rc = sscanf(line, "version: %d", &version);
153 if(rc != 1) return -2;
155 if((line = agets(infof)) == NULL) return -1;
156 rc = sscanf(line, "command: %d", &info->command);
158 if(rc != 1) return -2;
160 /* get rate: and comp: lines for full dumps */
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]);
168 if(rc > 3) return -2;
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]);
174 if(rc > 3) return -2;
176 /* get rate: and comp: lines for incr dumps */
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]);
184 if(rc > 3) return -2;
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]);
190 if(rc > 3) return -2;
192 /* get stats for dump levels */
194 for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
195 stats_t onestat; /* one stat record */
199 if(line[0] == '/' && line[1] == '/') {
202 return 0; /* normal end of record */
204 else if (strncmp(line,"last_level:",11) == 0) {
207 else if (strncmp(line,"history:",8) == 0) {
210 memset(&onestat, 0, sizeof(onestat));
216 if(strncmp(line, sc, sizeof(sc)-1) != 0) {
223 skip_whitespace(s, ch);
224 if(ch == '\0' || sscanf((s - 1), "%d", &level) != 1) {
229 skip_whitespace(s, ch);
230 if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.size) != 1) {
235 skip_whitespace(s, ch);
236 if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.csize) != 1) {
241 skip_whitespace(s, ch);
242 if(ch == '\0' || sscanf((s - 1), "%ld", &onestat.secs) != 1) {
247 skip_whitespace(s, ch);
248 if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
253 skip_whitespace(s, ch);
255 if(sscanf((s - 1), "%d", &onestat.filenum) != 1) {
260 skip_whitespace(s, ch);
264 strncpy(onestat.label, s-1, sizeof(onestat.label)-1);
265 onestat.label[sizeof(onestat.label)-1] = '\0';
268 onestat.date = date; /* time_t not guarranteed to be long */
270 if(level < 0 || level > DUMP_LEVELS-1)
273 info->inf[level] = onestat;
276 if(line == NULL) return -1;
278 rc = sscanf(line, "last_level: %d %d",
279 &info->last_level, &info->consecutive_runs);
282 if(rc > 2) return -2;
286 for(i=0;i<=NB_HISTORY;i++) {
287 info->history[i].level = -2;
290 for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
291 history_t onehistory; /* one history record */
294 if(line[0] == '/' && line[1] == '/') {
295 info->history[nb_history].level = -2;
298 return 0; /* normal end of record */
301 memset(&onehistory, 0, sizeof(onehistory));
306 #define sc "history:"
307 if(strncmp(line, sc, sizeof(sc)-1) != 0) {
315 skip_whitespace(s, ch);
316 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
322 skip_whitespace(s, ch);
323 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.size) != 1) {
329 skip_whitespace(s, ch);
330 if(ch == '\0' || sscanf((s - 1), "%ld", &onehistory.csize) != 1) {
336 skip_whitespace(s, ch);
337 if(ch == '\0' || sscanf((s - 1), "%ld", &date) != 1) {
343 onehistory.date = date; /* time_t not guarranteed to be long */
345 onehistory.secs = -1;
346 skip_whitespace(s, ch);
348 if(sscanf((s - 1), "%ld", &onehistory.secs) != 1) {
355 info->history[nb_history++] = onehistory;
359 if((line = agets(infof)) == NULL) return -1; /* // line */
365 static int write_txinfofile(infof, info)
374 fprintf(infof, "version: %d\n", 0);
376 fprintf(infof, "command: %d\n", info->command);
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");
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");
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");
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");
406 for(level=0; level<DUMP_LEVELS; level++) {
407 sp = &info->inf[level];
409 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
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");
418 fprintf(infof, "last_level: %d %d\n", info->last_level, info->consecutive_runs);
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);
425 fprintf(infof, "//\n");
430 static int delete_txinfofile(host, disk)
434 char *fn = NULL, *fn_new = NULL;
437 host = sanitise_filename(host);
438 disk = sanitise_filename(disk);
439 fn = vstralloc(infodir,
444 fn_new = stralloc2(fn, ".new");
452 rc = rmpdir(fn, infodir);
460 static char *lockname = NULL;
463 int open_infofile(filename)
467 assert(infodir == (char *)0);
469 infodir = stralloc(filename);
471 return 0; /* success! */
473 /* lock the dbm file */
475 lockname = newstralloc2(lockname, filename, ".lck");
476 if((lockfd = open(lockname, O_CREAT|O_RDWR, 0644)) == -1)
479 if(amflock(lockfd, "info") == -1) {
485 if(!(infodb = dbm_open(filename, O_CREAT|O_RDWR, 0644))) {
486 amfunlock(lockfd, "info");
492 return (infodb == NULL); /* return 1 on error */
496 void close_infofile()
499 assert(infodir != (char *)0);
505 if(amfunlock(lockfd, "info") == -1)
506 error("could not unlock infofile: %s", strerror(errno));
515 /* Convert a dump level to a GMT based time stamp */
516 char *get_dumpdate(info, lev)
520 static char stamp[20]; /* YYYY:MM:DD:hh:mm:ss */
527 for(l = 0; l < lev; l++) {
528 this = info->inf[l].date;
529 if (this > last) last = this;
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);
540 double perf_average(a, d)
541 /* Weighted average */
542 float *a; /* array of items to average */
543 double d; /* default value */
545 double sum; /* running total */
546 int n; /* number of items in sum */
553 for(i = 0; i < AVG_COUNT; i++) {
565 static void zero_info(info)
570 memset(info, '\0', sizeof(info_t));
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;
577 for(i = 0; i < DUMP_LEVELS; i++) {
578 info->inf[i].date = (time_t)-1;
581 info->last_level = -1;
582 info->consecutive_runs = -1;
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;
593 int get_info(hostname, diskname, info)
594 char *hostname, *diskname;
599 (void) zero_info(info);
605 infof = open_txinfofile(hostname, diskname, "r");
608 rc = -1; /* record not found */
611 rc = read_txinfofile(infof, info);
613 close_txinfofile(infof);
620 k.dptr = vstralloc(hostname, ":", diskname, NULL);
621 k.dsize = strlen(k.dptr)+1;
625 d = dbm_fetch(infodb, k);
628 rc = -1; /* record not found */
631 memcpy(info, d.dptr, d.dsize);
641 int get_firstkey(hostname, hostname_size, diskname, diskname_size)
642 char *hostname, *diskname;
643 int hostname_size, diskname_size;
654 k = dbm_firstkey(infodb);
655 if(k.dptr == NULL) return 0;
660 skip_whitespace(s, ch);
661 if(ch == '\0') return 0;
663 while(ch && ch != ':') {
664 if(fp >= hostname+hostname_size-1) {
671 if(fp == NULL) return 0;
674 if(ch != ':') return 0;
676 copy_string(s, ch, diskname, diskname_size, fp);
677 if(fp == NULL) return 0;
684 int get_nextkey(hostname, hostname_size, diskname, diskname_size)
685 char *hostname, *diskname;
686 int hostname_size, diskname_size;
697 k = dbm_nextkey(infodb);
698 if(k.dptr == NULL) return 0;
703 skip_whitespace(s, ch);
704 if(ch == '\0') return 0;
706 while(ch && ch != ':') {
707 if(fp >= hostname+hostname_size-1) {
714 if(fp == NULL) return 0;
717 if(ch != ':') return 0;
719 copy_string(s, ch, diskname, diskname_size, fp);
720 if(fp == NULL) return 0;
727 int put_info(hostname, diskname, info)
728 char *hostname, *diskname;
735 infof = open_txinfofile(hostname, diskname, "w");
737 if(infof == NULL) return -1;
739 rc = write_txinfofile(infof, info);
741 rc = rc || close_txinfofile(infof);
750 k.dptr = vstralloc(hostname, ":", diskname, NULL);
751 k.dsize = strlen(k.dptr)+1;
753 d.dptr = (char *)info;
754 d.dsize = sizeof(info_t);
758 if(dbm_store(infodb, k, d, DBM_REPLACE) != 0) {
769 int del_info(hostname, diskname)
770 char *hostname, *diskname;
773 return delete_txinfofile(hostname, diskname);
780 k.dptr = vstralloc(hostname, ":", diskname, NULL);
781 k.dsize = strlen(key)+1;
783 /* delete key and record */
785 if(dbm_delete(infodb, k) != 0) {
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++) {
814 if( sp->size != -1) {
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);
822 printf("last_level: %d %d\n", info->last_level, info->consecutive_runs);
826 void dump_db(host, disk)
832 if((rc = get_info(host, disk, &info)) == 0) {
835 printf("cannot fetch information for %s:%s rc=%d\n", host, disk, rc);
847 printf("info database %s:\n--------\n", str);
849 k = dbm_firstkey(infodb);
850 while(k.dptr != NULL) {
852 printf("%3d: KEY %s =\n", rec, k.dptr);
854 d = dbm_fetch(infodb, k);
855 memset(&info, '\0', sizeof(info));
856 memcpy(&info, d.dptr, d.dsize);
858 num = (d.dsize-HEADER)/sizeof(stats_t);
861 k = dbm_nextkey(infodb);
874 unsigned long malloc_hist_1, malloc_size_1;
875 unsigned long malloc_hist_2, malloc_size_2;
879 set_pname("infofile");
881 malloc_size_1 = malloc_inuse(&malloc_hist_1);
883 for(i = 1; i < argc; ++i) {
886 fprintf(stderr,"usage: %s host disk [host disk ...]\n",argv[0]);
889 open_infofile("curinfo");
890 dump_db(argv[i], argv[i+1]);
893 open_infofile(argv[i]);
899 malloc_size_2 = malloc_inuse(&malloc_hist_2);
901 if(malloc_size_1 != malloc_size_2) {
902 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);