Imported Upstream version 3.1.0
[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 "util.h"
36
37 static void zero_info(info_t *);
38
39   static char *infodir = NULL;
40   static char *infofile = NULL;
41   static char *newinfofile;
42   static int writing;
43
44   static FILE *open_txinfofile(char *, char *, char *);
45   static int close_txinfofile(FILE *);
46   static int read_txinfofile(FILE *, info_t *);
47   static int write_txinfofile(FILE *, info_t *);
48   static int delete_txinfofile(char *, char *);
49
50 static FILE *
51 open_txinfofile(
52     char *      host,
53     char *      disk,
54     char *      mode)
55 {
56     FILE *infof;
57     char *myhost;
58     char *mydisk;
59
60     assert(infofile == (char *)0);
61
62     writing = (*mode == 'w');
63
64     myhost = sanitise_filename(host);
65     mydisk = sanitise_filename(disk);
66
67     infofile = vstralloc(infodir,
68                          "/", myhost,
69                          "/", mydisk,
70                          "/info",
71                          NULL);
72
73     amfree(myhost);
74     amfree(mydisk);
75
76     /* create the directory structure if in write mode */
77     if (writing) {
78         if (mkpdir(infofile, 0755, (uid_t)-1, (gid_t)-1) == -1) {
79             amfree(infofile);
80             return NULL;
81         }
82     }
83
84     newinfofile = stralloc2(infofile, ".new");
85
86     if(writing) {
87         infof = fopen(newinfofile, mode);
88         if(infof != NULL)
89             amflock(fileno(infof), "info");
90     }
91     else {
92         infof = fopen(infofile, mode);
93         /* no need to lock readers */
94     }
95
96     if(infof == (FILE *)0) {
97         amfree(infofile);
98         amfree(newinfofile);
99         return NULL;
100     }
101
102     return infof;
103 }
104
105 static int
106 close_txinfofile(
107     FILE *infof)
108 {
109     int rc = 0;
110
111     assert(infofile != (char *)0);
112
113     if(writing) {
114         rc = rename(newinfofile, infofile);
115
116         amfunlock(fileno(infof), "info");
117     }
118
119     amfree(infofile);
120     amfree(newinfofile);
121
122     rc = rc || fclose(infof);
123     infof = NULL;
124     if (rc) rc = -1;
125
126     return rc;
127 }
128
129 /* XXX - code assumes AVG_COUNT == 3 */
130 static int
131 read_txinfofile(
132     FILE *      infof,
133     info_t *    info)
134 {
135     char *line = NULL;
136     int version;
137     int rc;
138     perf_t *pp;
139     char *s;
140     int ch;
141     int nb_history;
142     int i;
143
144     /* get version: command: lines */
145
146     while ((line = agets(infof)) != NULL) {
147         if (line[0] != '\0')
148             break;
149         amfree(line);
150     }
151     if (line == NULL) return -1;
152     rc = sscanf(line, _("version: %d"), &version);
153     amfree(line);
154     if(rc != 1) return -2;
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, _("command: %u"), &info->command);
163     amfree(line);
164     if(rc != 1) return -2;
165
166     /* get rate: and comp: lines for full dumps */
167
168     pp = &info->full;
169
170     while ((line = agets(infof)) != NULL) {
171         if (line[0] != '\0')
172             break;
173         amfree(line);
174     }
175     if (line == NULL) return -1;
176     rc = sscanf(line, "full-rate: %lf %lf %lf",
177                 &pp->rate[0], &pp->rate[1], &pp->rate[2]);
178     amfree(line);
179     if(rc > 3) return -2;
180
181     while ((line = agets(infof)) != NULL) {
182         if (line[0] != '\0')
183             break;
184         amfree(line);
185     }
186     if (line == NULL) return -1;
187     rc = sscanf(line, "full-comp: %lf %lf %lf",
188                 &pp->comp[0], &pp->comp[1], &pp->comp[2]);
189     amfree(line);
190     if(rc > 3) return -2;
191
192     /* get rate: and comp: lines for incr dumps */
193
194     pp = &info->incr;
195
196     while ((line = agets(infof)) != NULL) {
197         if (line[0] != '\0')
198             break;
199         amfree(line);
200     }
201     if (line == NULL) return -1;
202     rc = sscanf(line, "incr-rate: %lf %lf %lf",
203                 &pp->rate[0], &pp->rate[1], &pp->rate[2]);
204     amfree(line);
205     if(rc > 3) return -2;
206
207     while ((line = agets(infof)) != NULL) {
208         if (line[0] != '\0')
209             break;
210         amfree(line);
211     }
212     if (line == NULL) return -1;
213     rc = sscanf(line, "incr-comp: %lf %lf %lf",
214                 &pp->comp[0], &pp->comp[1], &pp->comp[2]);
215     amfree(line);
216     if(rc > 3) return -2;
217
218     /* get stats for dump levels */
219
220     for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
221         stats_t onestat;        /* one stat record */
222         int level = 0;
223         long long off_t_tmp;
224
225         if (line[0] == '\0')
226             continue;
227         if(line[0] == '/' && line[1] == '/') {
228             rc = 0;
229             amfree(line);
230             return 0;                           /* normal end of record */
231         }
232         else if (strncmp_const(line,"last_level:") == 0) {
233             break;                              /* normal */
234         }
235         else if (strncmp_const(line,"history:") == 0) {
236             break;                              /* normal */
237         }
238         memset(&onestat, 0, SIZEOF(onestat));
239
240         s = line;
241         ch = *s++;
242
243         /* from here on, we had better be parsing a 'stats' line */
244         if(strncmp_const_skip(line, "stats:", s, ch) != 0) {
245             return -1;
246         }
247
248         skip_whitespace(s, ch);
249         if(ch == '\0' || sscanf((s - 1), "%d", &level) != 1) {
250             return -1;
251         }
252         skip_integer(s, ch);
253
254         skip_whitespace(s, ch);
255         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
256             return -1;
257         }
258         onestat.size = (off_t)off_t_tmp;
259         skip_integer(s, ch);
260
261         skip_whitespace(s, ch);
262         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
263             return -1;
264         }
265         onestat.csize = (off_t)off_t_tmp;
266         skip_integer(s, ch);
267
268         /* assume that the time fits in a long long */
269         skip_whitespace(s, ch);
270         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
271             return -1;
272         }
273         onestat.secs = (time_t)off_t_tmp;
274         skip_integer(s, ch);
275
276         skip_whitespace(s, ch);
277         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
278             return -1;
279         }
280         onestat.date = (time_t)off_t_tmp;
281         skip_integer(s, ch);
282
283         skip_whitespace(s, ch);
284         if(ch != '\0') {
285             if(sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
286                 return -1;
287             }
288             onestat.filenum = (off_t)off_t_tmp;
289             skip_integer(s, ch);
290
291             skip_whitespace(s, ch);
292             if(ch == '\0') {
293                 return -1;
294             }
295             strncpy(onestat.label, s-1, SIZEOF(onestat.label)-1);
296             onestat.label[SIZEOF(onestat.label)-1] = '\0';
297         }
298
299         if(level < 0 || level > DUMP_LEVELS-1)
300             return -1;
301
302         info->inf[level] = onestat;
303     }
304    
305     if(line == NULL) return -1;
306
307     rc = sscanf(line, "last_level: %d %d", 
308                 &info->last_level, &info->consecutive_runs);
309
310     amfree(line);
311     if(rc > 2) return -2;
312     rc = 0;
313
314     nb_history = 0;
315     for(i=0;i<=NB_HISTORY;i++) {
316         info->history[i].level = -2;
317     }
318
319     for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
320         history_t onehistory;   /* one history record */
321         long long off_t_tmp;
322
323         if (line[0] == '\0')
324             continue;
325         if(line[0] == '/' && line[1] == '/') {
326             info->history[nb_history].level = -2;
327             rc = 0;
328             amfree(line);
329             return 0;                           /* normal end of record */
330         }
331
332         memset(&onehistory, 0, SIZEOF(onehistory));
333
334         s = line;
335         ch = *s++;
336
337         if(strncmp_const_skip(line, "history:", s, ch) != 0) {
338             amfree(line);
339             break;
340         }
341
342         skip_whitespace(s, ch);
343         if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
344             amfree(line);
345             break;
346         }
347         skip_integer(s, ch);
348
349         skip_whitespace(s, ch);
350         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
351             amfree(line);
352             break;
353         }
354         onehistory.size = (off_t)off_t_tmp;
355         skip_integer(s, ch);
356
357         skip_whitespace(s, ch);
358         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
359             amfree(line);
360             break;
361         }
362         onehistory.csize = (off_t)off_t_tmp;
363         skip_integer(s, ch);
364
365         skip_whitespace(s, ch);
366         if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
367             amfree(line);
368             break;
369         }
370         onehistory.date = (time_t)off_t_tmp;
371         skip_integer(s, ch);
372
373         onehistory.secs = (unsigned long)-1;
374         skip_whitespace(s, ch);
375         if(ch != '\0') {
376             if(sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
377                 amfree(line);
378                 break;
379             }
380             onehistory.secs = (time_t)off_t_tmp;
381             skip_integer(s, ch);
382         }
383
384         info->history[nb_history++] = onehistory;
385     }
386     amfree(line);
387
388     while ((line = agets(infof)) != NULL) {
389         if (line[0] != '\0')
390             break;
391         amfree(line);
392     }
393     if (line == NULL) return -1;
394     amfree(line);
395
396     return rc;
397 }
398
399 static int
400 write_txinfofile(
401     FILE *      infof,
402     info_t *    info)
403 {
404     int i;
405     stats_t *sp;
406     perf_t *pp;
407     int level;
408
409     g_fprintf(infof, _("version: %d\n"), 0);
410
411     g_fprintf(infof, _("command: %u\n"), info->command);
412
413     pp = &info->full;
414
415     g_fprintf(infof, "full-rate:");
416     for(i=0; i<AVG_COUNT; i++)
417         if(pp->rate[i] >= 0.0)
418             g_fprintf(infof, " %lf", pp->rate[i]);
419     g_fprintf(infof, "\n");
420
421     g_fprintf(infof, "full-comp:");
422     for(i=0; i<AVG_COUNT; i++)
423         if(pp->comp[i] >= 0.0)
424             g_fprintf(infof, " %lf", pp->comp[i]);
425     g_fprintf(infof, "\n");
426
427     pp = &info->incr;
428
429     g_fprintf(infof, "incr-rate:");
430     for(i=0; i<AVG_COUNT; i++)
431         if(pp->rate[i] >= 0.0)
432             g_fprintf(infof, " %lf", pp->rate[i]);
433     g_fprintf(infof, "\n");
434
435     g_fprintf(infof, "incr-comp:");
436     for(i=0; i<AVG_COUNT; i++)
437         if(pp->comp[i] >= 0.0)
438             g_fprintf(infof, " %lf", pp->comp[i]);
439     g_fprintf(infof, "\n");
440
441     for(level=0; level<DUMP_LEVELS; level++) {
442         sp = &info->inf[level];
443
444         if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
445
446         g_fprintf(infof, "stats: %d %lld %lld %jd %lld",
447                 level, (long long)sp->size, (long long)sp->csize,
448                 (intmax_t)sp->secs, (long long)sp->date);
449         if(sp->label[0] != '\0')
450             g_fprintf(infof, " %lld %s", (long long)sp->filenum, sp->label);
451         g_fprintf(infof, "\n");
452     }
453
454     g_fprintf(infof, _("last_level: %d %d\n"), info->last_level, info->consecutive_runs);
455
456     for(i=0;info->history[i].level > -1;i++) {
457         g_fprintf(infof, _("history: %d %lld %lld %jd %jd\n"),
458                 info->history[i].level,
459                 (long long)info->history[i].size,
460                 (long long)info->history[i].csize,
461                 (intmax_t)info->history[i].date,
462                 (intmax_t)info->history[i].secs);
463     }
464     g_fprintf(infof, "//\n");
465
466     return 0;
467 }
468
469 static int
470 delete_txinfofile(
471     char *      host,
472     char *      disk)
473 {
474     char *fn = NULL, *fn_new = NULL;
475     int rc;
476     char *myhost;
477     char *mydisk;
478
479     myhost = sanitise_filename(host);
480     mydisk = sanitise_filename(disk);
481     fn = vstralloc(infodir,
482                    "/", myhost,
483                    "/", mydisk,
484                    "/info",
485                    NULL);
486     fn_new = stralloc2(fn, ".new");
487
488     amfree(myhost);
489     amfree(mydisk);
490
491     unlink(fn_new);
492     amfree(fn_new);
493
494     rc = rmpdir(fn, infodir);
495     amfree(fn);
496
497     return rc;
498 }
499
500 int
501 open_infofile(
502     char *      filename)
503 {
504     assert(infodir == NULL);
505
506     infodir = stralloc(filename);
507
508     return 0; /* success! */
509 }
510
511 void
512 close_infofile(void)
513 {
514     assert(infodir != NULL);
515
516     amfree(infodir);
517 }
518
519 /* Convert a dump level to a GMT based time stamp */
520 char *
521 get_dumpdate(
522     info_t *    info,
523     int         lev)
524 {
525     static char stamp[20]; /* YYYY:MM:DD:hh:mm:ss */
526     int l;
527     time_t this, last;
528     struct tm *t;
529
530     last = EPOCH;
531
532     for(l = 0; l < lev; l++) {
533         this = info->inf[l].date;
534         if (this > last) last = this;
535     }
536
537     t = gmtime(&last);
538     g_snprintf(stamp, SIZEOF(stamp), "%d:%d:%d:%d:%d:%d",
539                 t->tm_year+1900, t->tm_mon+1, t->tm_mday,
540                 t->tm_hour, t->tm_min, t->tm_sec);
541
542     return stamp;
543 }
544
545 /*
546  * Weighted average
547  */
548 double
549 perf_average(
550     double *    a,      /* array of items to average */
551     double      d)      /* default value */
552 {
553     double sum; /* running total */
554     int n;      /* number of items in sum */
555     int w;      /* weight */
556     int i;      /* counter */
557
558     sum = 0.0;
559     n = 0;
560
561     for(i = 0; i < AVG_COUNT; i++) {
562         if(a[i] >= 0.0) {
563             w = AVG_COUNT - i;
564             sum += a[i] * w;
565             n += w;
566         }
567     }
568
569     if(n == 0) return d;
570     return sum / n;
571 }
572
573 static void
574 zero_info(
575     info_t *info)
576 {
577     int i;
578
579     memset(info, '\0', SIZEOF(info_t));
580
581     for(i = 0; i < AVG_COUNT; i++) {
582         info->full.comp[i] = info->incr.comp[i] = -1.0;
583         info->full.rate[i] = info->incr.rate[i] = -1.0;
584     }
585
586     for(i = 0; i < DUMP_LEVELS; i++) {
587         info->inf[i].date = (time_t)-1;
588     }
589
590     info->last_level = -1;
591     info->consecutive_runs = -1;
592
593     for(i=0;i<=NB_HISTORY;i++) {
594         info->history[i].level = -2;
595         info->history[i].size = (off_t)0;
596         info->history[i].csize = (off_t)0;
597         info->history[i].date = 0UL;
598     }
599     return;
600 }
601
602 int
603 get_info(
604     char *      hostname,
605     char *      diskname,
606     info_t *    info)
607 {
608     int rc;
609
610     (void) zero_info(info);
611
612     {
613         FILE *infof;
614
615         infof = open_txinfofile(hostname, diskname, "r");
616
617         if(infof == NULL) {
618             rc = -1; /* record not found */
619         }
620         else {
621             rc = read_txinfofile(infof, info);
622
623             close_txinfofile(infof);
624         }
625     }
626
627     return rc;
628 }
629
630
631 int
632 put_info(
633      char *     hostname,
634      char *     diskname,
635      info_t *   info)
636 {
637     FILE *infof;
638     int rc;
639
640     infof = open_txinfofile(hostname, diskname, "w");
641
642     if(infof == NULL) return -1;
643
644     rc = write_txinfofile(infof, info);
645
646     rc = rc || close_txinfofile(infof);
647
648     return rc;
649 }
650
651
652 int
653 del_info(
654     char *      hostname,
655     char *      diskname)
656 {
657     return delete_txinfofile(hostname, diskname);
658 }
659
660
661 #ifdef TEST
662
663 void dump_rec(info_t *info);
664
665 void
666 dump_rec(
667     info_t *    info)
668 {
669     int i;
670     stats_t *sp;
671
672     g_printf(_("command word: %d\n"), info->command);
673     g_printf(_("full dump rate (K/s) %5.1lf, %5.1lf, %5.1lf\n"),
674            info->full.rate[0],info->full.rate[1],info->full.rate[2]);
675     g_printf(_("full comp rate %5.1lf, %5.1lf, %5.1lf\n"),
676            info->full.comp[0]*100,info->full.comp[1]*100,info->full.comp[2]*100);
677     g_printf(_("incr dump rate (K/s) %5.1lf, %5.1lf, %5.1lf\n"),
678            info->incr.rate[0],info->incr.rate[1],info->incr.rate[2]);
679     g_printf(_("incr comp rate %5.1lf, %5.1lf, %5.1lf\n"),
680            info->incr.comp[0]*100,info->incr.comp[1]*100,info->incr.comp[2]*100);
681     for(i = 0; i < DUMP_LEVELS; i++) {
682         sp = &info->inf[i];
683         if( sp->size != -1) {
684
685             g_printf(_("lev %d date %ld tape %s filenum %lld size %ld csize %ld secs %ld\n"),
686                    i, (long)sp->date, sp->label, sp->filenum,
687                    sp->size, sp->csize, sp->secs);
688         }
689     }
690     putchar('\n');
691     g_printf(_("last_level: %d %d\n"), info->last_level, info->consecutive_runs);
692 }
693
694 void dump_db( char *host, char *disk);
695
696 void
697 dump_db(
698     char *      host,
699     char *      disk)
700 {
701     info_t info;
702     int rc;
703
704     if((rc = get_info(host, disk, &info)) == 0) {
705         dump_rec(&info);
706     } else {
707         g_printf(_("cannot fetch information for %s:%s rc=%d\n"), host, disk, rc);
708     }
709 }
710
711 int
712 main(
713     int         argc,
714     char **     argv)
715 {
716   int i;
717
718   /*
719    * Configure program for internationalization:
720    *   1) Only set the message locale for now.
721    *   2) Set textdomain for all amanda related programs to "amanda"
722    *      We don't want to be forced to support dozens of message catalogs.
723    */  
724   setlocale(LC_MESSAGES, "C");
725   textdomain("amanda"); 
726
727   safe_fd(-1, 0);
728
729   set_pname("infofile");
730
731   dbopen(DBG_SUBDIR_SERVER);
732
733   for(i = 1; i < argc; ++i) {
734     if(i+1 >= argc) {
735       g_fprintf(stderr,_("usage: %s host disk [host disk ...]\n"),argv[0]);
736       return 1;
737     }
738     open_infofile("curinfo");
739     dump_db(argv[i], argv[i+1]);
740     i++;
741     close_infofile();
742   }
743
744   dbclose();
745   return 0;
746 }
747
748 #endif /* TEST */