/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1991-1998 University of Maryland at College Park
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: James da Silva, Systems Design and Analysis Group
* Computer Science Department
* University of Maryland at College Park
*/
/*
* $Id: infofile.c,v 1.64 2006/07/25 18:18:48 martinea Exp $
*
* manage current info file
*/
#include "amanda.h"
#include "conffile.h"
#include "infofile.h"
#include "amutil.h"
static void zero_info(info_t *);
static char *infodir = NULL;
static char *infofile = NULL;
static char *newinfofile;
static int writing;
static FILE *open_txinfofile(char *, char *, char *);
static int close_txinfofile(FILE *);
static int read_txinfofile(FILE *, info_t *);
static int write_txinfofile(FILE *, info_t *);
static int delete_txinfofile(char *, char *);
static FILE *
open_txinfofile(
char * host,
char * disk,
char * mode)
{
FILE *infof;
char *myhost;
char *mydisk;
assert(infofile == (char *)0);
writing = (*mode == 'w');
myhost = sanitise_filename(host);
mydisk = sanitise_filename(disk);
infofile = g_strjoin(NULL, infodir,
"/", myhost,
"/", mydisk,
"/info",
NULL);
amfree(myhost);
amfree(mydisk);
/* create the directory structure if in write mode */
if (writing) {
if (mkpdir(infofile, 0755, (uid_t)-1, (gid_t)-1) == -1) {
amfree(infofile);
return NULL;
}
}
newinfofile = g_strconcat(infofile, ".new", NULL);
if(writing) {
infof = fopen(newinfofile, mode);
if(infof != NULL)
amflock(fileno(infof), "info");
}
else {
infof = fopen(infofile, mode);
/* no need to lock readers */
}
if(infof == (FILE *)0) {
amfree(infofile);
amfree(newinfofile);
return NULL;
}
return infof;
}
static int
close_txinfofile(
FILE *infof)
{
int rc = 0;
assert(infofile != (char *)0);
if(writing) {
rc = rename(newinfofile, infofile);
amfunlock(fileno(infof), "info");
}
amfree(infofile);
amfree(newinfofile);
rc = rc || fclose(infof);
infof = NULL;
if (rc) rc = -1;
return rc;
}
/* XXX - code assumes AVG_COUNT == 3 */
static int
read_txinfofile(
FILE * infof,
info_t * info)
{
char *line = NULL;
int version;
int rc;
perf_t *pp;
char *s;
int ch;
int nb_history;
int i;
/* get version: command: lines */
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, _("version: %d"), &version);
amfree(line);
if(rc != 1) return -2;
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, _("command: %u"), &info->command);
amfree(line);
if(rc != 1) return -2;
/* get rate: and comp: lines for full dumps */
pp = &info->full;
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, "full-rate: %lf %lf %lf",
&pp->rate[0], &pp->rate[1], &pp->rate[2]);
amfree(line);
if(rc > 3) return -2;
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, "full-comp: %lf %lf %lf",
&pp->comp[0], &pp->comp[1], &pp->comp[2]);
amfree(line);
if(rc > 3) return -2;
/* get rate: and comp: lines for incr dumps */
pp = &info->incr;
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, "incr-rate: %lf %lf %lf",
&pp->rate[0], &pp->rate[1], &pp->rate[2]);
amfree(line);
if(rc > 3) return -2;
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line == NULL) return -1;
rc = sscanf(line, "incr-comp: %lf %lf %lf",
&pp->comp[0], &pp->comp[1], &pp->comp[2]);
amfree(line);
if(rc > 3) return -2;
/* get stats for dump levels */
for(rc = -2; (line = agets(infof)) != NULL; free(line)) {
stats_t onestat; /* one stat record */
int level = 0;
long long off_t_tmp;
if (line[0] == '\0')
continue;
if(line[0] == '/' && line[1] == '/') {
rc = 0;
amfree(line);
return 0; /* normal end of record */
}
else if (strncmp_const(line,"last_level:") == 0) {
break; /* normal */
}
else if (strncmp_const(line,"history:") == 0) {
break; /* normal */
}
memset(&onestat, 0, sizeof(onestat));
s = line;
ch = *s++;
/* from here on, we had better be parsing a 'stats' line */
if(strncmp_const_skip(line, "stats:", s, ch) != 0) {
amfree(line);
return -1;
}
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%d", &level) != 1) {
amfree(line);
return -1;
}
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
return -1;
}
onestat.size = (off_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
return -1;
}
onestat.csize = (off_t)off_t_tmp;
skip_integer(s, ch);
/* assume that the time fits in a long long */
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
return -1;
}
onestat.secs = (time_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
return -1;
}
onestat.date = (time_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch != '\0') {
if(sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
return -1;
}
onestat.filenum = (off_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0') {
amfree(line);
return -1;
}
strncpy(onestat.label, s-1, sizeof(onestat.label)-1);
onestat.label[sizeof(onestat.label)-1] = '\0';
}
if (level < 0 || level > DUMP_LEVELS-1) {
amfree(line);
return -1;
}
info->inf[level] = onestat;
}
if(line == NULL) return -1;
rc = sscanf(line, "last_level: %d %d",
&info->last_level, &info->consecutive_runs);
amfree(line);
if(rc > 2) return -2;
rc = 0;
nb_history = 0;
for(i=0;i<NB_HISTORY;i++) {
info->history[i].level = -2;
}
while ((line = agets(infof)) != NULL) {
history_t onehistory; /* one history record */
long long off_t_tmp;
if (line[0] == '\0')
continue;
if(line[0] == '/' && line[1] == '/') {
info->history[nb_history].level = -2;
rc = 0;
amfree(line);
return 0; /* normal end of record */
}
if (nb_history >= NB_HISTORY) break;
memset(&onehistory, 0, sizeof(onehistory));
s = line;
ch = *s++;
if(strncmp_const_skip(line, "history:", s, ch) != 0) {
amfree(line);
break;
}
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%d", &onehistory.level) != 1) {
amfree(line);
break;
}
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
break;
}
onehistory.size = (off_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
break;
}
onehistory.csize = (off_t)off_t_tmp;
skip_integer(s, ch);
skip_whitespace(s, ch);
if(ch == '\0' || sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
break;
}
onehistory.date = (time_t)off_t_tmp;
skip_integer(s, ch);
onehistory.secs = (unsigned long)-1;
skip_whitespace(s, ch);
if(ch != '\0') {
if(sscanf((s - 1), "%lld", &off_t_tmp) != 1) {
amfree(line);
break;
}
onehistory.secs = (time_t)off_t_tmp;
skip_integer(s, ch);
}
info->history[nb_history++] = onehistory;
amfree(line);
}
amfree(line);
while ((line = agets(infof)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
return rc;
}
static int
write_txinfofile(
FILE * infof,
info_t * info)
{
int i;
stats_t *sp;
perf_t *pp;
int level;
g_fprintf(infof, _("version: %d\n"), 0);
g_fprintf(infof, _("command: %u\n"), info->command);
pp = &info->full;
g_fprintf(infof, "full-rate:");
for(i=0; i<AVG_COUNT; i++)
if(pp->rate[i] >= 0.0)
g_fprintf(infof, " %lf", pp->rate[i]);
g_fprintf(infof, "\n");
g_fprintf(infof, "full-comp:");
for(i=0; i<AVG_COUNT; i++)
if(pp->comp[i] >= 0.0)
g_fprintf(infof, " %lf", pp->comp[i]);
g_fprintf(infof, "\n");
pp = &info->incr;
g_fprintf(infof, "incr-rate:");
for(i=0; i<AVG_COUNT; i++)
if(pp->rate[i] >= 0.0)
g_fprintf(infof, " %lf", pp->rate[i]);
g_fprintf(infof, "\n");
g_fprintf(infof, "incr-comp:");
for(i=0; i<AVG_COUNT; i++)
if(pp->comp[i] >= 0.0)
g_fprintf(infof, " %lf", pp->comp[i]);
g_fprintf(infof, "\n");
for(level=0; level<DUMP_LEVELS; level++) {
sp = &info->inf[level];
if(sp->date < (time_t)0 && sp->label[0] == '\0') continue;
g_fprintf(infof, "stats: %d %lld %lld %jd %lld",
level, (long long)sp->size, (long long)sp->csize,
(intmax_t)sp->secs, (long long)sp->date);
if(sp->label[0] != '\0')
g_fprintf(infof, " %lld %s", (long long)sp->filenum, sp->label);
g_fprintf(infof, "\n");
}
g_fprintf(infof, _("last_level: %d %d\n"), info->last_level, info->consecutive_runs);
for(i=0;i < NB_HISTORY && info->history[i].level > -1;i++) {
g_fprintf(infof, _("history: %d %lld %lld %jd %jd\n"),
info->history[i].level,
(long long)info->history[i].size,
(long long)info->history[i].csize,
(intmax_t)info->history[i].date,
(intmax_t)info->history[i].secs);
}
g_fprintf(infof, "//\n");
return 0;
}
static int
delete_txinfofile(
char * host,
char * disk)
{
char *fn = NULL, *fn_new = NULL;
int rc;
char *myhost;
char *mydisk;
myhost = sanitise_filename(host);
mydisk = sanitise_filename(disk);
fn = g_strjoin(NULL, infodir,
"/", myhost,
"/", mydisk,
"/info",
NULL);
fn_new = g_strconcat(fn, ".new", NULL);
amfree(myhost);
amfree(mydisk);
unlink(fn_new);
amfree(fn_new);
rc = rmpdir(fn, infodir);
amfree(fn);
return rc;
}
int
open_infofile(
char * filename)
{
assert(infodir == NULL);
infodir = g_strdup(filename);
return 0; /* success! */
}
void
close_infofile(void)
{
assert(infodir != NULL);
amfree(infodir);
}
/* Convert a dump level to a GMT based time stamp */
char *
get_dumpdate(
info_t * info,
int lev)
{
static char stamp[20]; /* YYYY:MM:DD:hh:mm:ss */
int l;
time_t this, last;
struct tm *t;
last = EPOCH;
for(l = 0; l < lev; l++) {
this = info->inf[l].date;
if (this > last) last = this;
}
t = gmtime(&last);
g_snprintf(stamp, sizeof(stamp), "%d:%d:%d:%d:%d:%d",
t->tm_year+1900, t->tm_mon+1, t->tm_mday,
t->tm_hour, t->tm_min, t->tm_sec);
return stamp;
}
/*
* Weighted average
*/
double
perf_average(
double * a, /* array of items to average */
double d) /* default value */
{
double sum; /* running total */
int n; /* number of items in sum */
int w; /* weight */
int i; /* counter */
sum = 0.0;
n = 0;
for(i = 0; i < AVG_COUNT; i++) {
if(a[i] >= 0.0) {
w = AVG_COUNT - i;
sum += a[i] * w;
n += w;
}
}
if(n == 0) return d;
return sum / n;
}
static void
zero_info(
info_t *info)
{
int i;
memset(info, '\0', sizeof(info_t));
for(i = 0; i < AVG_COUNT; i++) {
info->full.comp[i] = info->incr.comp[i] = -1.0;
info->full.rate[i] = info->incr.rate[i] = -1.0;
}
for(i = 0; i < DUMP_LEVELS; i++) {
info->inf[i].date = (time_t)-1;
}
info->last_level = -1;
info->consecutive_runs = -1;
for(i=0;i<NB_HISTORY;i++) {
info->history[i].level = -2;
info->history[i].size = (off_t)0;
info->history[i].csize = (off_t)0;
info->history[i].date = 0UL;
}
return;
}
int
get_info(
char * hostname,
char * diskname,
info_t * info)
{
int rc;
(void) zero_info(info);
{
FILE *infof;
infof = open_txinfofile(hostname, diskname, "r");
if(infof == NULL) {
rc = -1; /* record not found */
}
else {
rc = read_txinfofile(infof, info);
close_txinfofile(infof);
}
}
return rc;
}
int
put_info(
char * hostname,
char * diskname,
info_t * info)
{
FILE *infof;
int rc;
infof = open_txinfofile(hostname, diskname, "w");
if(infof == NULL) return -1;
rc = write_txinfofile(infof, info);
rc = rc || close_txinfofile(infof);
return rc;
}
int
del_info(
char * hostname,
char * diskname)
{
return delete_txinfofile(hostname, diskname);
}
#ifdef TEST
void dump_rec(info_t *info);
void
dump_rec(
info_t * info)
{
int i;
stats_t *sp;
g_printf(_("command word: %d\n"), info->command);
g_printf(_("full dump rate (K/s) %5.1lf, %5.1lf, %5.1lf\n"),
info->full.rate[0],info->full.rate[1],info->full.rate[2]);
g_printf(_("full comp rate %5.1lf, %5.1lf, %5.1lf\n"),
info->full.comp[0]*100,info->full.comp[1]*100,info->full.comp[2]*100);
g_printf(_("incr dump rate (K/s) %5.1lf, %5.1lf, %5.1lf\n"),
info->incr.rate[0],info->incr.rate[1],info->incr.rate[2]);
g_printf(_("incr comp rate %5.1lf, %5.1lf, %5.1lf\n"),
info->incr.comp[0]*100,info->incr.comp[1]*100,info->incr.comp[2]*100);
for(i = 0; i < DUMP_LEVELS; i++) {
sp = &info->inf[i];
if( sp->size != -1) {
g_printf(_("lev %d date %ld tape %s filenum %lld size %ld csize %ld secs %ld\n"),
i, (long)sp->date, sp->label, sp->filenum,
sp->size, sp->csize, sp->secs);
}
}
putchar('\n');
g_printf(_("last_level: %d %d\n"), info->last_level, info->consecutive_runs);
}
void dump_db( char *host, char *disk);
void
dump_db(
char * host,
char * disk)
{
info_t info;
int rc;
if((rc = get_info(host, disk, &info)) == 0) {
dump_rec(&info);
} else {
g_printf(_("cannot fetch information for %s:%s rc=%d\n"), host, disk, rc);
}
}
int
main(
int argc,
char ** argv)
{
int i;
glib_init();
/*
* Configure program for internationalization:
* 1) Only set the message locale for now.
* 2) Set textdomain for all amanda related programs to "amanda"
* We don't want to be forced to support dozens of message catalogs.
*/
setlocale(LC_MESSAGES, "C");
textdomain("amanda");
safe_fd(-1, 0);
set_pname("infofile");
dbopen(DBG_SUBDIR_SERVER);
for(i = 1; i < argc; ++i) {
if(i+1 >= argc) {
g_fprintf(stderr,_("usage: %s host disk [host disk ...]\n"),argv[0]);
return 1;
}
open_infofile("curinfo");
dump_db(argv[i], argv[i+1]);
i++;
close_infofile();
}
dbclose();
return 0;
}
#endif /* TEST */