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.64 2006/07/25 18:18:48 martinea Exp $
30 * manage current info file
37 static void zero_info(info_t *);
40 static char *infodir = (char *)0;
41 static char *infofile = (char *)0;
42 static char *newinfofile;
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 *);
52 /*# define HEADER (SIZEOF(info_t)-DUMP_LEVELS*SIZEOF(stats_t))*/
54 static DBM *infodb = NULL;
70 assert(infofile == (char *)0);
72 writing = (*mode == 'w');
74 myhost = sanitise_filename(host);
75 mydisk = sanitise_filename(disk);
77 infofile = vstralloc(infodir,
86 /* create the directory structure if in write mode */
88 if (mkpdir(infofile, 02755, (uid_t)-1, (gid_t)-1) == -1) {
94 newinfofile = stralloc2(infofile, ".new");
97 infof = fopen(newinfofile, mode);
99 amflock(fileno(infof), "info");
102 infof = fopen(infofile, mode);
103 /* no need to lock readers */
106 if(infof == (FILE *)0) {
121 assert(infofile != (char *)0);
124 rc = rename(newinfofile, infofile);
126 amfunlock(fileno(infof), "info");
132 rc = rc || fclose(infof);
139 /* XXX - code assumes AVG_COUNT == 3 */
154 /* get version: command: lines */
156 while ((line = agets(infof)) != NULL) {
161 if (line == NULL) return -1;
162 rc = sscanf(line, "version: %d", &version);
164 if(rc != 1) return -2;
166 while ((line = agets(infof)) != NULL) {
171 if (line == NULL) return -1;
172 rc = sscanf(line, "command: %u", &info->command);
174 if(rc != 1) return -2;
176 /* get rate: and comp: lines for full dumps */
180 while ((line = agets(infof)) != NULL) {
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]);
189 if(rc > 3) return -2;
191 while ((line = agets(infof)) != NULL) {
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]);
200 if(rc > 3) return -2;
202 /* get rate: and comp: lines for incr dumps */
206 while ((line = agets(infof)) != NULL) {
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]);
215 if(rc > 3) return -2;
217 while ((line = agets(infof)) != NULL) {
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]);
226 if(rc > 3) return -2;
228 /* get stats for dump levels */
230 for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
231 stats_t onestat; /* one stat record */
233 time_t *date_p = &date;
239 if(line[0] == '/' && line[1] == '/') {
242 return 0; /* normal end of record */
244 else if (strncmp(line,"last_level:",11) == 0) {
247 else if (strncmp(line,"history:",8) == 0) {
250 memset(&onestat, 0, SIZEOF(onestat));
256 if(strncmp(line, sc, SIZEOF(sc)-1) != 0) {
263 skip_whitespace(s, ch);
264 if(ch == '\0' || sscanf((s - 1), "%d", &level) != 1) {
269 skip_whitespace(s, ch);
270 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
271 (OFF_T_FMT_TYPE *)&onestat.size) != 1) {
276 skip_whitespace(s, ch);
277 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
278 (OFF_T_FMT_TYPE *)&onestat.csize) != 1) {
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) {
291 skip_whitespace(s, ch);
292 if(ch == '\0' || sscanf((s - 1), TIME_T_FMT,
293 (TIME_T_FMT_TYPE *)date_p) != 1) {
298 skip_whitespace(s, ch);
300 if(sscanf((s - 1), OFF_T_FMT,
301 (OFF_T_FMT_TYPE *)&onestat.filenum) != 1) {
306 skip_whitespace(s, ch);
310 strncpy(onestat.label, s-1, SIZEOF(onestat.label)-1);
311 onestat.label[SIZEOF(onestat.label)-1] = '\0';
314 onestat.date = date; /* time_t not guarranteed to be long */
316 if(level < 0 || level > DUMP_LEVELS-1)
319 info->inf[level] = onestat;
322 if(line == NULL) return -1;
324 rc = sscanf(line, "last_level: %d %d",
325 &info->last_level, &info->consecutive_runs);
328 if(rc > 2) return -2;
332 for(i=0;i<=NB_HISTORY;i++) {
333 info->history[i].level = -2;
336 for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
337 history_t onehistory; /* one history record */
339 time_t *date_p = &date;
345 if(line[0] == '/' && line[1] == '/') {
346 info->history[nb_history].level = -2;
349 return 0; /* normal end of record */
352 memset(&onehistory, 0, SIZEOF(onehistory));
357 #define sc "history:"
358 if(strncmp(line, sc, SIZEOF(sc)-1) != 0) {
366 skip_whitespace(s, ch);
367 if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
373 skip_whitespace(s, ch);
374 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
375 (OFF_T_FMT_TYPE *)&onehistory.size) != 1) {
381 skip_whitespace(s, ch);
382 if(ch == '\0' || sscanf((s - 1), OFF_T_FMT,
383 (OFF_T_FMT_TYPE *)&onehistory.csize) != 1) {
389 skip_whitespace(s, ch);
390 if(ch == '\0' || sscanf((s - 1), TIME_T_FMT,
391 (TIME_T_FMT_TYPE *)date_p) != 1) {
397 onehistory.date = date; /* time_t not guaranteed to be long */
399 onehistory.secs = (unsigned long)-1;
400 skip_whitespace(s, ch);
401 secs_p = &onehistory.secs;
403 if(sscanf((s - 1), TIME_T_FMT,
404 (TIME_T_FMT_TYPE *)secs_p) != 1) {
411 info->history[nb_history++] = onehistory;
415 while ((line = agets(infof)) != NULL) {
420 if (line == NULL) return -1;
436 fprintf(infof, "version: %d\n", 0);
438 fprintf(infof, "command: %u\n", info->command);
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");
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");
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");
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");
468 for(level=0; level<DUMP_LEVELS; level++) {
469 sp = &info->inf[level];
471 if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
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");
483 fprintf(infof, "last_level: %d %d\n", info->last_level, info->consecutive_runs);
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);
494 fprintf(infof, "//\n");
504 char *fn = NULL, *fn_new = NULL;
509 myhost = sanitise_filename(host);
510 mydisk = sanitise_filename(disk);
511 fn = vstralloc(infodir,
516 fn_new = stralloc2(fn, ".new");
524 rc = rmpdir(fn, infodir);
532 static char *lockname = NULL;
540 assert(infodir == (char *)0);
542 infodir = stralloc(filename);
544 return 0; /* success! */
546 /* lock the dbm file */
548 lockname = newstralloc2(lockname, filename, ".lck");
549 if((lockfd = open(lockname, O_CREAT|O_RDWR, 0644)) == -1)
552 if(amflock(lockfd, "info") == -1) {
558 if(!(infodb = dbm_open(filename, O_CREAT|O_RDWR, 0644))) {
559 amfunlock(lockfd, "info");
565 return (infodb == NULL); /* return 1 on error */
573 assert(infodir != (char *)0);
579 if(amfunlock(lockfd, "info") == -1) {
580 error("could not unlock infofile: %s", strerror(errno));
591 /* Convert a dump level to a GMT based time stamp */
597 static char stamp[20]; /* YYYY:MM:DD:hh:mm:ss */
604 for(l = 0; l < lev; l++) {
605 this = info->inf[l].date;
606 if (this > last) last = this;
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);
622 double * a, /* array of items to average */
623 double d) /* default value */
625 double sum; /* running total */
626 int n; /* number of items in sum */
633 for(i = 0; i < AVG_COUNT; i++) {
651 memset(info, '\0', SIZEOF(info_t));
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;
658 for(i = 0; i < DUMP_LEVELS; i++) {
659 info->inf[i].date = (time_t)-1;
662 info->last_level = -1;
663 info->consecutive_runs = -1;
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;
682 (void) zero_info(info);
688 infof = open_txinfofile(hostname, diskname, "r");
691 rc = -1; /* record not found */
694 rc = read_txinfofile(infof, info);
696 close_txinfofile(infof);
703 k.dptr = vstralloc(hostname, ":", diskname, NULL);
704 k.dsize = strlen(k.dptr)+1;
708 d = dbm_fetch(infodb, k);
711 rc = -1; /* record not found */
714 memcpy(info, d.dptr, d.dsize);
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 */
745 k = dbm_firstkey(infodb);
746 if(k.dptr == NULL) return 0;
751 skip_whitespace(s, ch);
752 if(ch == '\0') return 0;
754 while(ch && ch != ':') {
755 if(fp >= hostname+hostname_size-1) {
762 if(fp == NULL) return 0;
765 if(ch != ':') return 0;
767 copy_string(s, ch, diskname, diskname_size, fp);
768 if(fp == NULL) return 0;
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 */
796 k = dbm_nextkey(infodb);
797 if(k.dptr == NULL) return 0;
802 skip_whitespace(s, ch);
803 if(ch == '\0') return 0;
805 while(ch && ch != ':') {
806 if(fp >= hostname+hostname_size-1) {
813 if(fp == NULL) return 0;
816 if(ch != ':') return 0;
818 copy_string(s, ch, diskname, diskname_size, fp);
819 if(fp == NULL) return 0;
836 infof = open_txinfofile(hostname, diskname, "w");
838 if(infof == NULL) return -1;
840 rc = write_txinfofile(infof, info);
842 rc = rc || close_txinfofile(infof);
851 k.dptr = vstralloc(hostname, ":", diskname, NULL);
852 k.dsize = strlen(k.dptr)+1;
854 d.dptr = (char *)info;
855 d.dsize = SIZEOF(info_t);
859 if(dbm_store(infodb, k, d, DBM_REPLACE) != 0) {
876 return delete_txinfofile(hostname, diskname);
883 k.dptr = vstralloc(hostname, ":", diskname, NULL);
884 k.dsize = strlen(key)+1;
886 /* delete key and record */
888 if(dbm_delete(infodb, k) != 0) {
900 void dump_rec(info_t *info);
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++) {
920 if( sp->size != -1) {
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);
928 printf("last_level: %d %d\n", info->last_level, info->consecutive_runs);
932 void dump_db( char *host, char *disk);
942 if((rc = get_info(host, disk, &info)) == 0) {
945 printf("cannot fetch information for %s:%s rc=%d\n", host, disk, rc);
958 printf("info database %s:\n--------\n", str);
960 k = dbm_firstkey(infodb);
961 while(k.dptr != NULL) {
963 printf("%3d: KEY %s =\n", rec, k.dptr);
965 d = dbm_fetch(infodb, k);
966 memset(&info, '\0', SIZEOF(info));
967 memcpy(&info, d.dptr, d.dsize);
969 num = (d.dsize-HEADER)/SIZEOF(stats_t);
972 k = dbm_nextkey(infodb);
985 unsigned long malloc_hist_1, malloc_size_1;
986 unsigned long malloc_hist_2, malloc_size_2;
990 set_pname("infofile");
992 dbopen(DBG_SUBDIR_SERVER);
994 malloc_size_1 = malloc_inuse(&malloc_hist_1);
996 for(i = 1; i < argc; ++i) {
999 fprintf(stderr,"usage: %s host disk [host disk ...]\n",argv[0]);
1002 open_infofile("curinfo");
1003 dump_db(argv[i], argv[i+1]);
1006 open_infofile(argv[i]);
1012 malloc_size_2 = malloc_inuse(&malloc_hist_2);
1014 if(malloc_size_1 != malloc_size_2) {
1015 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);