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