/*
* 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.
*
* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
/*
* $Id: tapefile.c,v 1.37 2006/07/21 00:25:52 martinea Exp $
*
* routines to read and write the amanda active tape list
*/
#include "amanda.h"
#include "match.h"
#include "tapefile.h"
#include "conffile.h"
#include "timestamp.h"
#include "find.h"
#include "cmdfile.h"
static tape_t *tape_list = NULL;
static tape_t *tape_list_end = NULL;
static GHashTable *tape_table_storage_label = NULL;
static GHashTable *tape_table_label = NULL;
static gboolean retention_computed = FALSE;
/* local functions */
static char *tape_hash_key(const char *pool, const char *label);
static tape_t *parse_tapeline(int *status, char *line);
static tape_t *insert(tape_t *list, tape_t *tp);
static time_t stamp2time(char *datestamp);
static void compute_storage_retention_nb(const char *storage,
const char *tapepool,
const char *l_template,
int retention_tapes);
static void compute_storage_retention(find_result_t *output_find,
const char *storage,
const char *tapepool,
const char *l_template,
int retention_tapes,
int retention_days,
int retention_recover,
int retention_full);
static char *
tape_hash_key(
const char *pool,
const char *label)
{
char *tape_key;
if (pool) {
tape_key = g_strdup_printf("P:%s-L:%s", pool, label);
} else {
tape_key = g_strdup_printf("P:%s-L:%s", get_config_name(), label);
}
return tape_key;
}
int
read_tapelist(
char *tapefile)
{
tape_t *tp;
FILE *tapef;
int pos;
char *line = NULL;
int status = 0;
reset_tapelist();
if((tapef = fopen(tapefile,"r")) == NULL) {
if (errno == ENOENT) {
/* no tapelist is equivalent to an empty tapelist */
return 0;
} else {
g_debug("Error opening '%s': %s", tapefile, strerror(errno));
return 1;
}
}
while((line = agets(tapef)) != NULL) {
if (line[0] == '\0') {
amfree(line);
continue;
}
tp = parse_tapeline(&status, line);
amfree(line);
if (tp == NULL && status != 0) {
afclose(tapef);
return 1;
}
if (tp != NULL) {
char *tape_key = tape_hash_key(tp->pool, tp->label);
tape_list = insert(tape_list, tp);
g_hash_table_insert(tape_table_storage_label, tape_key, tp);
g_hash_table_insert(tape_table_label, tp->label, tp);
}
}
afclose(tapef);
for(pos=1,tp=tape_list; tp != NULL; pos++,tp=tp->next) {
tp->position = pos;
}
retention_computed = FALSE;
return 0;
}
int
write_tapelist(
char *tapefile)
{
tape_t *tp;
FILE *tapef;
char *newtapefile;
int rc;
char *pid_str;
char *last_read_str;
newtapefile = g_strconcat(tapefile, ".new", NULL);
if((tapef = fopen(newtapefile,"w")) == NULL) {
amfree(newtapefile);
return 1;
}
for(tp = tape_list; tp != NULL; tp = tp->next) {
g_fprintf(tapef, "%s %s", tp->datestamp, tp->label);
if(tp->reuse) g_fprintf(tapef, " reuse");
else g_fprintf(tapef, " no-reuse");
if (tp->barcode)
g_fprintf(tapef, " BARCODE:%s", tp->barcode);
if (tp->meta)
g_fprintf(tapef, " META:%s", tp->meta);
if (tp->blocksize)
g_fprintf(tapef, " BLOCKSIZE:%jd", (intmax_t)tp->blocksize);
if (tp->pool)
g_fprintf(tapef, " POOL:%s", tp->pool);
if (tp->storage)
g_fprintf(tapef, " STORAGE:%s", tp->storage);
if (tp->config)
g_fprintf(tapef, " CONFIG:%s", tp->config);
if (tp->comment)
g_fprintf(tapef, " #%s", tp->comment);
g_fprintf(tapef, "\n");
}
if (fclose(tapef) == EOF) {
g_fprintf(stderr,_("error [closing %s: %s]"), newtapefile, strerror(errno));
amfree(newtapefile);
return 1;
}
pid_str = g_strdup_printf("%d", (int)getpid());
last_read_str = g_strdup_printf("%s.last_write", tapefile);
unlink(last_read_str);
rc = rename(newtapefile, tapefile);
if (symlink(pid_str, last_read_str) == -1) {
g_debug("failed to symlink %s to %s: %s", last_read_str, pid_str,
strerror(errno));
}
amfree(newtapefile);
amfree(pid_str);
amfree(last_read_str);
return(rc != 0);
}
void
clear_tapelist(void)
{
tape_t *tp, *next;
if (tape_table_storage_label) {
g_hash_table_destroy(tape_table_storage_label);
tape_table_storage_label = NULL;
}
if (tape_table_label) {
g_hash_table_destroy(tape_table_label);
tape_table_label = NULL;
}
for(tp = tape_list; tp; tp = next) {
amfree(tp->label);
amfree(tp->datestamp);
amfree(tp->barcode);
amfree(tp->meta);
amfree(tp->config);
amfree(tp->pool);
amfree(tp->storage);
amfree(tp->comment);
next = tp->next;
amfree(tp);
}
tape_list = NULL;
tape_list_end = NULL;
}
void
reset_tapelist(void)
{
clear_tapelist();
tape_table_storage_label = g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, NULL);
tape_table_label = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
}
tape_t *
lookup_tapelabel(
const char *label)
{
// tape_t *tp;
return g_hash_table_lookup(tape_table_label, label);
// for(tp = tape_list; tp != NULL; tp = tp->next) {
// if(g_str_equal(label, tp->label)) return tp;
// }
// return NULL;
}
tape_t *
lookup_tapepoollabel(
const char *pool,
const char *label)
{
tape_t *tp;
char *tape_key;
tape_key = tape_hash_key(pool, label);
tp = g_hash_table_lookup(tape_table_storage_label, tape_key);
return tp;
}
tape_t *
lookup_tapepos(
int pos)
{
tape_t *tp;
for(tp = tape_list; tp != NULL; tp = tp->next) {
if(tp->position == pos) return tp;
}
return NULL;
}
tape_t *
lookup_tapedate(
char *datestamp)
{
tape_t *tp;
for(tp = tape_list; tp != NULL; tp = tp->next) {
if(g_str_equal(tp->datestamp, datestamp)) return tp;
}
return NULL;
}
int
lookup_nb_tape(void)
{
tape_t *tp;
int pos=0;
for(tp = tape_list; tp != NULL; tp = tp->next) {
pos=tp->position;
}
return pos;
}
char *
get_last_reusable_tape_label(
const char *l_template,
const char *tapepool,
const char *storage,
int retention_tapes,
int retention_days,
int retention_recover,
int retention_full,
int skip)
{
tape_t *tp = lookup_last_reusable_tape(l_template, tapepool, storage,
retention_tapes,
retention_days, retention_recover,
retention_full, skip);
return (tp != NULL) ? tp->label : NULL;
}
tape_t *
lookup_last_reusable_tape(
const char *l_template,
const char *tapepool,
const char *storage,
int retention_tapes,
int retention_days G_GNUC_UNUSED,
int retention_recover G_GNUC_UNUSED,
int retention_full G_GNUC_UNUSED,
int skip)
{
tape_t *tp, **tpsave;
int count=0;
int s;
/*
* The idea here is we keep the last "several" reusable tapes we
* find in a stack and then return the n-th oldest one to the
* caller. If skip is zero, the oldest is returned, if it is
* one, the next oldest, two, the next to next oldest and so on.
*/
compute_retention();
tpsave = g_malloc((skip + 1) * sizeof(*tpsave));
for (s = 0; s <= skip; s++) {
tpsave[s] = NULL;
}
for (tp = tape_list; tp != NULL; tp = tp->next) {
if (tp->reuse == 1 && !tp->retention &&
!g_str_equal(tp->datestamp, "0") &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, storage)) &&
(!tp->pool || g_str_equal(tp->pool, tapepool)) &&
(match_labelstr_template(l_template, tp->label,
tp->barcode, tp->meta,
tp->storage))) {
count++;
for(s = skip; s > 0; s--) {
tpsave[s] = tpsave[s - 1];
}
tpsave[0] = tp;
}
}
s = retention_tapes + 1 - count;
if (s < 0)
s = 0;
if (skip < s)
tp = NULL;
else
tp = tpsave[skip - s];
amfree(tpsave);
return tp;
}
int
reusable_tape(
tape_t *tp)
{
if (tp == NULL) return 0;
if (tp->reuse == 0) return 0;
if (g_str_equal(tp->datestamp, "0")) return 1;
if (tp->config && !g_str_equal(tp->config, get_config_name())) return 0;
compute_retention();
return (!tp->retention && !tp->retention_nb);
}
int
volume_is_reusable(
const char *label)
{
tape_t *tp = lookup_tapelabel(label);
return reusable_tape(tp);
}
void
remove_tapelabel(
const char *label)
{
tape_t *tp, *prev, *next;
tp = lookup_tapelabel(label);
if (tp) {
char *tape_key = tape_hash_key(tp->pool, tp->label);
g_hash_table_remove(tape_table_storage_label, tape_key);
g_hash_table_remove(tape_table_label, tp->label);
g_free(tape_key);
prev = tp->prev;
next = tp->next;
/*@ignore@*/
if(prev != NULL)
prev->next = next;
else /* begin of list */
tape_list = next;
if(next != NULL)
next->prev = prev;
else /* end of list */
tape_list_end = prev;
/*@end@*/
while (next != NULL) {
next->position--;
next = next->next;
}
amfree(tp->datestamp);
amfree(tp->label);
amfree(tp->meta);
amfree(tp->comment);
amfree(tp->pool);
amfree(tp->storage);
amfree(tp->config);
amfree(tp->barcode);
amfree(tp);
}
}
tape_t *
add_tapelabel(
const char *datestamp,
const char *label,
const char *comment,
gboolean reuse,
const char *meta,
const char *barcode,
guint64 blocksize,
const char *pool,
const char *storage,
const char *config)
{
tape_t *cur, *new;
char *tape_key;
tape_t *tp;
for (tp = tape_list; tp != NULL; tp = tp->next) {
if (g_str_equal(tp->label, label) &&
(storage && tp->storage && g_str_equal(tp->storage, storage))) {
g_critical("ERROR: add_tapelabel that already exists: %s %s", label, storage);
}
}
/* insert a new record to the front of the list */
new = g_new0(tape_t,1);
new->datestamp = g_strdup(datestamp);
new->position = 0;
new->reuse = reuse;
new->label = g_strdup(label);
new->comment = comment? g_strdup(comment) : NULL;
new->meta = meta? g_strdup(meta) : NULL;
new->barcode = barcode? g_strdup(barcode) : NULL;
new->blocksize = blocksize;
new->pool = pool? g_strdup(pool) : NULL;
new->storage = storage? g_strdup(storage) : NULL;
new->config = config? g_strdup(config) : NULL;
new->retention = FALSE;
new->retention_nb = FALSE;
new->retention_type = RETENTION_NO;
new->when_overwrite = -1;
new->prev = NULL;
new->next = NULL;
tape_list = insert(tape_list, new);
/* scan list, updating positions */
cur = tape_list;
while(cur != NULL) {
cur->position++;
cur = cur->next;
}
tape_key = tape_hash_key(new->pool, new->label);
g_hash_table_insert(tape_table_storage_label, tape_key, new);
g_hash_table_insert(tape_table_label, new->label, new);
return new;
}
int
guess_runs_from_tapelist(void)
{
tape_t *tp;
int i, ntapes, tape_ndays, dumpcycle, runtapes, runs;
time_t tape_time, today;
today = time(0);
dumpcycle = getconf_int(CNF_DUMPCYCLE);
runtapes = getconf_int(CNF_RUNTAPES);
if(runtapes == 0) runtapes = 1; /* just in case */
ntapes = 0;
tape_ndays = 0;
for(i = 1; i < getconf_int(CNF_TAPECYCLE); i++) {
if((tp = lookup_tapepos(i)) == NULL) break;
tape_time = stamp2time(tp->datestamp);
tape_ndays = (int)days_diff(tape_time, today);
if(tape_ndays < dumpcycle) ntapes++;
else break;
}
if(tape_ndays < dumpcycle) {
/* scale for best guess */
if(tape_ndays == 0) ntapes = dumpcycle * runtapes;
else ntapes = ntapes * dumpcycle / tape_ndays;
}
else if(ntapes == 0) {
/* no dumps within the last dumpcycle, guess as above */
ntapes = dumpcycle * runtapes;
}
runs = (ntapes + runtapes - 1) / runtapes;
if (runs <= 0)
runs = 1;
return runs;
}
static tape_t *
parse_tapeline(
int *status,
char *line)
{
tape_t *tp = NULL;
char *s, *s1;
int ch;
char *cline;
*status = 0;
s = line;
ch = *s++;
skip_whitespace(s, ch);
if(ch == '\0') {
return NULL;
}
cline = g_strdup(line);
tp = g_new0(tape_t, 1);
tp->when_overwrite = -1;
s1 = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
tp->datestamp = g_strdup(s1);
skip_whitespace(s, ch);
s1 = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
tp->label = g_strdup(s1);
skip_whitespace(s, ch);
tp->reuse = 1;
if(strncmp_const(s - 1, "reuse") == 0) {
tp->reuse = 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
}
tp->retention = !tp->reuse;
tp->retention_nb = FALSE;
if(strncmp_const(s - 1, "no-reuse") == 0) {
tp->reuse = 0;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
}
if (strncmp_const(s - 1, "BARCODE:") == 0) {
s1 = s - 1 + 8;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->barcode = g_strdup(s1);
}
if (strncmp_const(s - 1, "META:") == 0) {
s1 = s - 1 + 5;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->meta = g_strdup(s1);
}
if (strncmp_const(s - 1, "BLOCKSIZE:") == 0) {
s1 = s - 1 + 10;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->blocksize = atol(s1);
}
if (strncmp_const(s - 1, "POOL:") == 0) {
s1 = s - 1 + 5;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->pool = g_strdup(s1);
}
if (strncmp_const(s - 1, "STORAGE:") == 0) {
s1 = s - 1 + 8;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->storage = g_strdup(s1);
}
if (strncmp_const(s - 1, "CONFIG:") == 0) {
s1 = s - 1 + 7;
skip_non_whitespace(s, ch);
s[-1] = '\0';
skip_whitespace(s, ch);
tp->config = g_strdup(s1);
}
if (*(s - 1) == '#') {
tp->comment = g_strdup(s); /* skip leading '#' */
} else if (*(s-1)) {
g_critical("Bogus line in the tapelist file: %s", cline);
}
g_free(cline);
return tp;
}
/* insert in reversed datestamp order */
/*@ignore@*/
static tape_t *
insert(
tape_t *list,
tape_t *tp)
{
tape_t *prev, *cur;
if (tape_list_end && strcmp(tape_list_end->datestamp, tp->datestamp) >= 0) {
prev = tape_list_end;
cur = NULL;
} else {
prev = NULL;
cur = list;
while (cur != NULL && strcmp(cur->datestamp, tp->datestamp) >= 0) {
prev = cur;
cur = cur->next;
}
}
tp->prev = prev;
tp->next = cur;
if (prev == NULL) {
list = tp;
#ifndef __lint
} else {
prev->next = tp;
#endif
}
if (cur == NULL) {
tape_list_end = tp;
} else {
cur->prev = tp;
}
return list;
}
/*@end@*/
/*
* Converts datestamp (an char of the form YYYYMMDD or YYYYMMDDHHMMSS) into a real
* time_t value.
* Since the datestamp contains no timezone or hh/mm/ss information, the
* value is approximate. This is ok for our purposes, since we round off
* scheduling calculations to the nearest day.
*/
static time_t
stamp2time(
char *datestamp)
{
struct tm *tm, *tm1;
time_t now;
char date[9];
int dateint;
time_t tt;
strncpy(date, datestamp, 8);
date[8] = '\0';
dateint = atoi(date);
now = time(0);
tm = g_malloc(sizeof(struct tm));
tm1 = localtime_r(&now, tm); /* initialize sec/min/hour & gmtoff */
if (!tm1) {
tm->tm_sec = 0;
tm->tm_min = 0;
tm->tm_hour = 0;
tm->tm_wday = 0;
tm->tm_yday = 0;
tm->tm_isdst = 0;
}
tm->tm_year = ( dateint / 10000) - 1900;
tm->tm_mon = ((dateint % 10000) / 100) - 1;
tm->tm_mday = ((dateint % 100) );
tt = mktime(tm);
amfree(tm);
return (tt);
}
gchar **list_new_tapes(
char *storage_n,
int nb)
{
tape_t *last_tape, *iter;
int c;
labelstr_s *labelstr;
char *tapepool;
GSList *l_list = NULL;
GSList *list1;
storage_t *storage;
char **lists;
int d;
if (nb <= 0)
return NULL;
storage = lookup_storage(storage_n);
/* Find latest reusable new tape */
last_tape = lookup_tapepos(lookup_nb_tape());
while (last_tape && last_tape->reuse == 0)
last_tape = last_tape->prev;
if (!last_tape)
return NULL;
if (!g_str_equal(last_tape->datestamp, "0"))
return NULL;
labelstr = storage_get_labelstr(storage);
tapepool = storage_get_tapepool(storage);
/* count the number of tapes we *actually* used */
iter = last_tape;
c = 0;
while (iter && nb > 0 && g_str_equal(iter->datestamp, "0")) {
if (iter->reuse &&
(!iter->config || g_str_equal(iter->config, get_config_name())) &&
(!iter->storage || g_str_equal(iter->storage, storage_n)) &&
((iter->pool && g_str_equal(iter->pool, tapepool)) ||
(!iter->pool && match_labelstr_template(labelstr->template, iter->label,
iter->barcode, iter->meta,
iter->storage)))) {
c++;
nb--;
l_list = g_slist_append(l_list, iter->label);
}
iter = iter->prev;
}
lists = g_new0(gchar *, (c+1));
d = 0;
list1 = l_list;
while (list1 != NULL) {
lists[d] = list1->data;
list1 = list1->next;
d++;
}
lists[d] = 0;
g_slist_free(l_list);
return lists;
}
static find_result_t *output_find = NULL;
void
compute_retention(void)
{
tape_t *tp;
storage_t *storage;
disklist_t *diskp;
for (tp = tape_list; tp != NULL; tp = tp->next) {
tp->retention_nb = FALSE;
}
if (!retention_computed) {
for (tp = tape_list; tp != NULL; tp = tp->next) {
if (!tp->reuse) {
tp->retention = TRUE;
tp->retention_type = RETENTION_NO_REUSE;
}
if (tp->reuse && !tp->retention &&
tp->config &&
!g_str_equal(tp->config, get_config_name())) {
tp->retention = TRUE;
tp->retention_type = RETENTION_OTHER_CONFIG;
}
}
}
for (storage = get_first_storage(); storage != NULL;
storage = get_next_storage(storage)) {
char *policy_name = storage_get_policy(storage);
policy_s *policy = lookup_policy(policy_name);
labelstr_s *labelstr = storage_get_labelstr(storage);
compute_storage_retention_nb(storage_name(storage),
storage_get_tapepool(storage),
labelstr->template,
policy_get_retention_tapes(policy));
}
if (retention_computed)
return;
for (storage = get_first_storage(); storage != NULL;
storage = get_next_storage(storage)) {
char *policy_name = storage_get_policy(storage);
policy_s *policy = lookup_policy(policy_name);
labelstr_s *labelstr = storage_get_labelstr(storage);
if (!output_find && (policy_get_retention_recover(policy) ||
policy_get_retention_full(policy))) {
char *conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
diskp = get_disklist();
if (!diskp) {
diskp = g_new0(disklist_t, 1);
read_diskfile(conf_diskfile, diskp);
}
output_find = find_dump(diskp, 1);
sort_find_result("hkDLpbfw", &output_find);
g_free(conf_diskfile);
}
compute_storage_retention(output_find, storage_name(storage),
storage_get_tapepool(storage),
labelstr->template,
policy_get_retention_tapes(policy),
policy_get_retention_days(policy),
policy_get_retention_recover(policy),
policy_get_retention_full(policy));
}
retention_computed = TRUE;
}
static void
compute_storage_retention_nb(
const char *storage,
const char *tapepool,
const char *l_template,
int retention_tapes)
{
tape_t *tp;
if (retention_tapes) {
int count = 0;
for (tp = tape_list; tp != NULL; tp = tp->next) {
if (tp->reuse == 1 &&
!tp->retention &&
!g_str_equal(tp->datestamp, "0") &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, storage)) &&
((tp->pool && g_str_equal(tp->pool, tapepool)) ||
(!tp->pool && match_labelstr_template(l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
count++;
if (count <= retention_tapes) {
/* Do not mark them, as it change when a tape is
* overwritten */
/* tp->retention = TRUE; */
tp->retention_nb = TRUE;
tp->retention_type = RETENTION_TAPES;
}
}
}
}
}
typedef struct cmdfile_add_retention_s {
const char *storage;
const char *pool;
const char *l_template;
} cmdfile_add_retention_t;
static void
cmdfile_add_retention(
gpointer key G_GNUC_UNUSED,
gpointer value,
gpointer user_data)
{
cmddata_t *cmddata = value;
cmdfile_add_retention_t *data = user_data;
tape_t *tp;
GSList *sl;
if (cmddata->operation == CMD_COPY &&
cmddata->status != CMD_DONE &&
g_str_equal(cmddata->src_storage, data->storage) &&
g_str_equal(cmddata->src_pool, data->pool)) {
for (sl = cmddata->src_labels; sl != NULL; sl = sl->next) {
char *label = (char *)sl->data;
tp = lookup_tapelabel(label);
if (tp && !tp->retention && !tp->retention_nb &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, data->storage)) &&
((tp->pool && g_str_equal(tp->pool, data->pool)) ||
(!tp->pool && match_labelstr_template(data->l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
tp->retention = TRUE;
tp->retention_type = RETENTION_CMD_COPY;
}
}
}
if (cmddata->operation == CMD_RESTORE &&
cmddata->status != CMD_DONE &&
g_str_equal(cmddata->src_storage, data->storage) &&
g_str_equal(cmddata->src_pool, data->pool) &&
cmddata->src_label) {
char *label = cmddata->src_label;
tp = lookup_tapelabel(label);
if (tp && !tp->retention && !tp->retention_nb &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, data->storage)) &&
((tp->pool && g_str_equal(tp->pool, data->pool)) ||
(!tp->pool && match_labelstr_template(data->l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
tp->retention = TRUE;
tp->retention_type = RETENTION_CMD_RESTORE;
}
}
}
static void
compute_storage_retention(
find_result_t *output_find,
const char *storage,
const char *tapepool,
const char *l_template,
int retention_tapes,
int retention_days,
int retention_recover,
int retention_full)
{
tape_t *tp;
char *conf_cmdfile;
cmddatas_t *cmddatas;
cmdfile_add_retention_t data;
if (retention_tapes) {
/* done in compute_storage_retention_nb */
}
if (retention_days) {
char *datestr = get_timestamp_from_time(time(NULL) -
retention_days*86400);
for(tp = tape_list; tp != NULL; tp = tp->next) {
if (tp->reuse == 1 &&
!tp->retention && !tp->retention_nb &&
g_ascii_strcasecmp(tp->datestamp, datestr) > 0 &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, storage)) &&
((tp->pool && g_str_equal(tp->pool, tapepool)) ||
(!tp->pool && match_labelstr_template(l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
tp->retention = TRUE;
tp->retention_type = RETENTION_DAYS;
}
}
g_free(datestr);
}
if (retention_recover) {
find_result_t *ofr; /* output_find_result */
char *datestr = get_timestamp_from_time(time(NULL) -
retention_recover*86400);
char *hostname = "AlKHDA";
char *diskname = "ADJAOLDUIN";
int level = -1;
for (ofr = output_find;
ofr;
ofr = ofr->next) {
if (!g_str_equal(hostname, ofr->hostname) ||
!g_str_equal(diskname, ofr->diskname)) {
/* new dle */
hostname = ofr->hostname;
diskname = ofr->diskname;
level = -1;
}
if (ofr->level < level ||
g_ascii_strcasecmp(ofr->timestamp, datestr) > 0) {
if (ofr->label && ofr->label[0] != '/') {
tp = lookup_tapelabel(ofr->label);
if (!tp->retention && !tp->retention_nb &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, storage)) &&
((tp->pool && g_str_equal(tp->pool, tapepool)) ||
(!tp->pool && match_labelstr_template(l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
/* keep that label */
tp->retention = TRUE;
tp->retention_type = RETENTION_RECOVER;
}
}
level = ofr->level;
}
}
g_free(datestr);
}
if (retention_full) {
find_result_t *ofr; /* output_find_result */
char *hostname = "AlKHDA";
char *diskname = "ADJAOLDUIN";
int count = 0;
sort_find_result("hkDLpbfw", &output_find);
for (ofr = output_find;
ofr;
ofr = ofr->next) {
if (!g_str_equal(hostname, ofr->hostname) ||
!g_str_equal(diskname, ofr->diskname)) {
/* new dle */
hostname = ofr->hostname;
diskname = ofr->diskname;
count = 0;
}
if (ofr->level == 0 &&
count < retention_full) {
if (ofr->label && ofr->label[0] != '/') {
tp = lookup_tapelabel(ofr->label);
if (!tp->retention && !tp->retention_nb &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!tp->storage || g_str_equal(tp->storage, storage)) &&
((tp->pool && g_str_equal(tp->pool, tapepool)) ||
(!tp->pool && match_labelstr_template(l_template, tp->label,
tp->barcode, tp->meta,
tp->storage)))) {
/* keep that label */
tp->retention = TRUE;
tp->retention_type = RETENTION_FULL;
count++;
}
}
}
}
}
conf_cmdfile = config_dir_relative(getconf_str(CNF_CMDFILE));
cmddatas = read_cmdfile(conf_cmdfile);
g_free(conf_cmdfile);
unlock_cmdfile(cmddatas);
data.storage = storage;
data.pool = tapepool;
data.l_template = l_template;
// keep label if it have a command in cmdfile not yet executed.
g_hash_table_foreach(cmddatas->cmdfile, &cmdfile_add_retention, &data);
close_cmdfile(cmddatas);
}
gchar **list_retention(void)
{
int nb_tapes = 0;
tape_t *tp;
gchar **rv;
int r;
GHashTable* storage_hash = NULL;
compute_retention();
for (tp = tape_list; tp != NULL; tp = tp->next) {
nb_tapes++;
}
if (getconf_seen(CNF_STORAGE) == -2) {
identlist_t il;
storage_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
for (il = getconf_identlist(CNF_STORAGE); il != NULL; il = il->next) {
char *storage_name = (char *)il->data;
g_hash_table_insert(storage_hash, storage_name, GINT_TO_POINTER(1));
}
}
rv = g_new0(gchar *, nb_tapes+1);
r = 0;
for (tp = tape_list; tp != NULL; tp = tp->next) {
if ((tp->retention || tp->retention_nb) &&
!g_str_equal(tp->datestamp, "0") &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!storage_hash || !tp->storage || g_hash_table_lookup(storage_hash, tp->storage))) {
rv[r++] = tp->label;
}
}
rv[r] = NULL;
if (storage_hash) {
g_hash_table_destroy(storage_hash);
}
return rv;
}
gchar **list_no_retention(void)
{
int nb_tapes = 0;
tape_t *tp;
gchar **rv;
int r;
GHashTable* storage_hash = NULL;
compute_retention();
for (tp = tape_list; tp != NULL; tp = tp->next) {
nb_tapes++;
}
if (getconf_seen(CNF_STORAGE) == -2) {
identlist_t il;
storage_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
for (il = getconf_identlist(CNF_STORAGE); il != NULL; il = il->next) {
char *storage_name = (char *)il->data;
g_hash_table_insert(storage_hash, storage_name, GINT_TO_POINTER(1));
}
}
rv = g_new0(gchar *, nb_tapes+1);
r = 0;
for (tp = tape_list; tp != NULL; tp = tp->next) {
if ((!tp->retention && !tp->retention_nb) &&
!g_str_equal(tp->datestamp, "0") &&
(!tp->config || g_str_equal(tp->config, get_config_name())) &&
(!storage_hash || !tp->storage || g_hash_table_lookup(storage_hash, tp->storage))) {
rv[r++] = tp->label;
}
}
rv[r] = NULL;
if (storage_hash) {
g_hash_table_destroy(storage_hash);
}
return rv;
}
RetentionType
get_retention_type(
char *pool,
char *label)
{
tape_t *tp;
for(tp = tape_list; tp != NULL; tp = tp->next) {
if (g_str_equal(label, tp->label) &&
((!pool && !tp->pool) ||
(pool && tp->pool && g_str_equal(pool, tp->pool))))
return tp->retention_type;
}
return RETENTION_NO;
}
int
tape_overwrite(
storage_t *st,
tape_t *tp)
{
tape_t *tp1;
int nb_tapes = 0;
for (tp1 = tp; tp1 != NULL; tp1 = tp1->next) {
if (!tp1->retention &&
(((!tp1->storage || !tp->storage) &&
match_labelstr(storage_get_labelstr(st),
storage_get_autolabel(st),
tp1->label, tp1->barcode, tp1->meta,
storage_name(st))) ||
(tp1->storage && tp->storage &&
g_str_equal(tp->storage, tp1->storage)))) {
nb_tapes++;
}
}
return nb_tapes;
}
int
nb_tape_in_storage(
storage_t *st)
{
tape_t *tp;
int nb_tapes = 0;
char *storage = storage_name(st);
for (tp = tape_list; tp != NULL; tp = tp->next) {
if (((!storage || !tp->storage) &&
match_labelstr(storage_get_labelstr(st),
storage_get_autolabel(st),
tp->label, tp->barcode, tp->meta,
storage_name(st))) ||
(storage && tp->storage && g_str_equal(storage, tp->storage))) {
nb_tapes++;
}
}
return nb_tapes;
}