X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Fplanner.c;h=f7a38be691eeedbdf900a7043fc8dc3e9fbde377;hb=d28952249e392eb31bc8eecc53f6c477f30c617b;hp=92de8d830fda7a8856edb83ce25b2a75d61c9470;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/server-src/planner.c b/server-src/planner.c index 92de8d8..f7a38be 100644 --- a/server-src/planner.c +++ b/server-src/planner.c @@ -1,6 +1,7 @@ /* * Amanda, The Advanced Maryland Automatic Network Disk Archiver * Copyright (c) 1991-1999 University of Maryland at College Park + * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved. * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -24,12 +25,13 @@ * file named AUTHORS, in the root directory of this distribution. */ /* - * $Id: planner.c,v 1.180 2006/03/10 13:51:06 martinea Exp $ + * $Id: planner.c 10421 2008-03-06 18:48:30Z martineau $ * * backup schedule planner for the Amanda backup system. */ #include "amanda.h" #include "arglist.h" +#include "find.h" #include "conffile.h" #include "diskfile.h" #include "tapefile.h" @@ -43,6 +45,14 @@ #include "amfeatures.h" #include "server_util.h" #include "holding.h" +#include "timestamp.h" +#include "amxml.h" + +#define planner_debug(i,x) do { \ + if ((i) <= debug_planner) { \ + dbprintf(x); \ + } \ +} while (0) #define MAX_LEVELS 3 /* max# of estimates per filesys */ @@ -53,15 +63,16 @@ /* configuration file stuff */ -char *conf_tapetype; -am64_t conf_maxdumpsize; -int conf_runtapes; -int conf_dumpcycle; -int conf_runspercycle; -int conf_tapecycle; -int conf_etimeout; -int conf_reserve; -int conf_autoflush; +char * conf_tapetype; +gint64 conf_maxdumpsize; +int conf_runtapes; +int conf_dumpcycle; +int conf_runspercycle; +int conf_tapecycle; +time_t conf_etimeout; +int conf_reserve; +int conf_autoflush; +int conf_usetimestamps; #define HOST_READY ((void *)0) /* must be 0 */ #define HOST_ACTIVE ((void *)1) @@ -72,41 +83,50 @@ int conf_autoflush; #define DISK_PARTIALY_DONE 2 #define DISK_DONE 3 +typedef struct one_est_s { + int level; + gint64 nsize; /* native size */ + gint64 csize; /* compressed size */ + char *dumpdate; + int guessed; /* If server guessed the estimate size */ +} one_est_t; +static one_est_t default_one_est = {-1, -1, -1, "INVALID_DATE", 0}; + typedef struct est_s { int state; int got_estimate; int dump_priority; - int dump_level; - long dump_size; - int degr_level; /* if dump_level == 0, what would be the inc level */ - long degr_size; + one_est_t *dump_est; + one_est_t *degr_est; + one_est_t estimate[MAX_LEVELS]; int last_level; - long last_lev0size; + gint64 last_lev0size; int next_level0; int level_days; int promote; + int post_dle; double fullrate, incrrate; double fullcomp, incrcomp; char *errstr; - int level[MAX_LEVELS]; - char *dumpdate[MAX_LEVELS]; - long est_size[MAX_LEVELS]; + char *degr_mesg; + info_t *info; } est_t; #define est(dp) ((est_t *)(dp)->up) /* pestq = partial estimate */ disklist_t startq, waitq, pestq, estq, failq, schedq; -am64_t total_size; +gint64 total_size; double total_lev0, balanced_size, balance_threshold; -am64_t tape_length, tape_mark; +gint64 tape_length; +size_t tape_mark; tapetype_t *tape; -long tt_blocksize; -long tt_blocksize_kb; +size_t tt_blocksize; +size_t tt_blocksize_kb; int runs_per_cycle = 0; time_t today; -char *datestamp = NULL; +char *planner_timestamp = NULL; static am_feature_t *our_features = NULL; static char *our_feature_string = NULL; @@ -122,7 +142,8 @@ typedef struct bi_s { int deleted; /* 0=modified, 1=deleted */ disk_t *dp; /* The disk that was changed */ int level; /* The original level */ - long size; /* The original size */ + gint64 nsize; /* The original native size */ + gint64 csize; /* The original compressed size */ char *errstr; /* A message describing why this disk is here */ } bi_t; @@ -138,100 +159,140 @@ bilist_t biq; /* The BI queue itself */ * */ -static void setup_estimate P((disk_t *dp)); -static void get_estimates P((void)); -static void analyze_estimate P((disk_t *dp)); -static void handle_failed P((disk_t *dp)); -static void delay_dumps P((void)); -static int promote_highest_priority_incremental P((void)); -static int promote_hills P((void)); -static void output_scheduleline P((disk_t *dp)); -int main P((int, char **)); - -int main(argc, argv) -int argc; -char **argv; +static void setup_estimate(disk_t *dp); +static void get_estimates(void); +static void analyze_estimate(disk_t *dp); +static void handle_failed(disk_t *dp); +static void delay_dumps(void); +static int promote_highest_priority_incremental(void); +static int promote_hills(void); +static void output_scheduleline(disk_t *dp); +static void server_estimate(disk_t *dp, int i, info_t *info, int level); +int main(int, char **); + +int +main( + int argc, + char ** argv) { disklist_t origq; disk_t *dp; int moved_one; - unsigned long malloc_hist_1, malloc_size_1; - unsigned long malloc_hist_2, malloc_size_2; - long initial_size; + int diskarg_offset; + gint64 initial_size; int i; - char *conffile; char *conf_diskfile; char *conf_tapelist; char *conf_infofile; times_t section_start; + char *qname; + int nb_disk; + char *errstr = NULL; + config_overrides_t *cfg_ovr = NULL; + char *cfg_opt = NULL; + int planner_setuid; + int exit_status = EXIT_SUCCESS; + gboolean no_taper = FALSE; + gboolean from_client = FALSE; + gboolean exact_match = FALSE; + + if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) { + printf("planner-%s\n", VERSION); + return (0); + } + + /* + * 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"); + + /* drop root privileges */ + planner_setuid = set_root_privs(0); safe_fd(-1, 0); - setvbuf(stderr, (char *)NULL, _IOLBF, 0); + set_pname("planner"); - if (argc > 1) { - config_name = stralloc(argv[1]); - config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL); - } else { - char my_cwd[STR_SIZE]; + dbopen(DBG_SUBDIR_SERVER); - if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) { - error("cannot determine current working directory"); - } - config_dir = stralloc2(my_cwd, "/"); - if ((config_name = strrchr(my_cwd, '/')) != NULL) { - config_name = stralloc(config_name + 1); - } - } + cfg_ovr = extract_commandline_config_overrides(&argc, &argv); + if (argc > 1) + cfg_opt = argv[1]; - safe_cd(); + set_config_overrides(cfg_ovr); + config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt); - set_pname("planner"); + /* conf_diskfile is freed later, as it may be used in an error message */ + conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE)); + read_diskfile(conf_diskfile, &origq); + disable_skip_disk(&origq); /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); - malloc_size_1 = malloc_inuse(&malloc_hist_1); + setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0); + + add_amanda_log_handler(amanda_log_stderr); + add_amanda_log_handler(amanda_log_trace_log); + + if (!planner_setuid) { + error(_("planner must be run setuid root")); + } + + if (config_errors(NULL) >= CFGERR_ERRORS) { + g_critical(_("errors processing config file")); + } + + safe_cd(); + + check_running_as(RUNNING_AS_ROOT | RUNNING_AS_UID_ONLY); + + dbrename(get_config_name(), DBG_SUBDIR_SERVER); - erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE); - set_logerror(logerror); startclock(); section_start = curclock(); our_features = am_init_feature_set(); our_feature_string = am_feature_to_string(our_features); - fprintf(stderr, "%s: pid %ld executable %s version %s\n", - get_pname(), (long) getpid(), argv[0], version()); + log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); + g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"), + get_pname(), (long) getpid(), argv[0], VERSION); for (i = 0; version_info[i] != NULL; i++) - fprintf(stderr, "%s: %s", get_pname(), version_info[i]); + g_fprintf(stderr, _("%s: %s"), get_pname(), version_info[i]); - /* - * 1. Networking Setup - * - * Planner runs setuid to get a priviledged socket for BSD security. - * We get the socket right away as root, then setuid back to a normal - * user. If we are not using BSD security, planner is not installed - * setuid root. - */ + diskarg_offset = 2; + if (argc - diskarg_offset > 1 && strcmp(argv[diskarg_offset], "--starttime") == 0) { + planner_timestamp = stralloc(argv[diskarg_offset+1]); + diskarg_offset += 2; + } + if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--no-taper") == 0) { + no_taper = TRUE; + diskarg_offset += 1; + } + if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--from-client") == 0) { + from_client = TRUE; + diskarg_offset += 1; + } + if (argc - diskarg_offset > 0 && g_str_equal(argv[diskarg_offset], + "--exact_match")) { + exact_match = TRUE; + diskarg_offset += 1; + } - protocol_init(); - if(geteuid() == 0) { - uid_t ruid = getuid(); - setuid(0); - seteuid(ruid); - setgid(getgid()); - } + run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name()); /* - * From this point on we are running under our real uid, so we don't - * have to worry about opening security holes below. Make sure we - * are a valid user. + * 1. Networking Setup + * */ - if(getpwuid(getuid()) == NULL) - error("can't get login name for my uid %ld", (long)getuid()); + protocol_init(); /* * 2. Read in Configuration Information @@ -239,66 +300,101 @@ char **argv; * All the Amanda configuration files are loaded before we begin. */ - fprintf(stderr,"READING CONF FILES...\n"); + g_fprintf(stderr,_("READING CONF INFO...\n")); - conffile = stralloc2(config_dir, CONFFILE_NAME); - if(read_conffile(conffile)) { - error("errors processing config file \"%s\"", conffile); + if(origq.head == NULL) { + error(_("empty disklist \"%s\""), conf_diskfile); + /*NOTREACHED*/ } - amfree(conffile); - conf_diskfile = getconf_str(CNF_DISKFILE); - if (*conf_diskfile == '/') { - conf_diskfile = stralloc(conf_diskfile); - } else { - conf_diskfile = stralloc2(config_dir, conf_diskfile); - } - if (read_diskfile(conf_diskfile, &origq) < 0) { - error("could not load disklist \"%s\"", conf_diskfile); - } - match_disklist(&origq, argc-2, argv+2); - for(dp = origq.head; dp != NULL; dp = dp->next) { - if(dp->todo) - log_add(L_DISK, "%s %s", dp->host->hostname, dp->name); - } amfree(conf_diskfile); - conf_tapelist = getconf_str(CNF_TAPELIST); - if (*conf_tapelist == '/') { - conf_tapelist = stralloc(conf_tapelist); - } else { - conf_tapelist = stralloc2(config_dir, conf_tapelist); - } + conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST)); if(read_tapelist(conf_tapelist)) { - error("could not load tapelist \"%s\"", conf_tapelist); + error(_("could not load tapelist \"%s\""), conf_tapelist); + /*NOTREACHED*/ } amfree(conf_tapelist); - conf_infofile = getconf_str(CNF_INFOFILE); - if (*conf_infofile == '/') { - conf_infofile = stralloc(conf_infofile); - } else { - conf_infofile = stralloc2(config_dir, conf_infofile); - } + conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE)); if(open_infofile(conf_infofile)) { - error("could not open info db \"%s\"", conf_infofile); + error(_("could not open info db \"%s\""), conf_infofile); + /*NOTREACHED*/ + } + if (check_infofile(conf_infofile, &origq, &errstr) == -1) { + log_add(L_WARNING, "problem copying infofile: %s", errstr); + amfree(errstr); } amfree(conf_infofile); conf_tapetype = getconf_str(CNF_TAPETYPE); - conf_maxdumpsize = getconf_am64(CNF_MAXDUMPSIZE); + conf_maxdumpsize = getconf_int64(CNF_MAXDUMPSIZE); conf_runtapes = getconf_int(CNF_RUNTAPES); conf_dumpcycle = getconf_int(CNF_DUMPCYCLE); conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE); conf_tapecycle = getconf_int(CNF_TAPECYCLE); - conf_etimeout = getconf_int(CNF_ETIMEOUT); + conf_etimeout = (time_t)getconf_int(CNF_ETIMEOUT); conf_reserve = getconf_int(CNF_RESERVE); - conf_autoflush = getconf_int(CNF_AUTOFLUSH); + conf_autoflush = getconf_no_yes_all(CNF_AUTOFLUSH); + conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS); - amfree(datestamp); today = time(0); - datestamp = construct_datestamp(NULL); - log_add(L_START, "date %s", datestamp); + if (planner_timestamp) { + if (conf_usetimestamps == 0) { + planner_timestamp[8] = '\0'; + } + } else if(conf_usetimestamps == 0) { + planner_timestamp = get_datestamp_from_time(0); + } + else { + planner_timestamp = get_timestamp_from_time(0); + } + log_add(L_START, _("date %s"), planner_timestamp); + g_printf("DATE %s\n", planner_timestamp); + fflush(stdout); + g_fprintf(stderr, _("%s: timestamp %s\n"), + get_pname(), planner_timestamp); + + errstr = match_disklist(&origq, exact_match, argc-diskarg_offset, + argv+diskarg_offset); + if (errstr) { + g_fprintf(stderr,"%s",errstr); + exit_status = EXIT_FAILURE; + } + + for (dp = origq.head; dp != NULL; dp = dp->next) { + if (dp->todo) { + if (from_client) { + if (!dp->dump_limit || !dp->dump_limit->same_host) + dp->todo = 0; + } else { + if (dp->dump_limit && !dp->dump_limit->server) + dp->todo = 0; + } + } + } + + nb_disk = 0; + for (dp = origq.head; dp != NULL; dp = dp->next) { + if (dp->todo) { + qname = quote_string(dp->name); + log_add(L_DISK, "%s %s", dp->host->hostname, qname); + amfree(qname); + nb_disk++; + } + } + + if (nb_disk == 0) { + if (errstr) { + error(_("no DLE to backup; %s"), errstr); + } else { + error(_("no DLE to backup")); + } + /*NOTREACHED*/ + } else if (errstr) { + log_add(L_WARNING, "WARNING: %s", errstr); + } + amfree(errstr); /* some initializations */ @@ -317,22 +413,28 @@ char **argv; * do some basic sanity checking */ if(conf_tapecycle <= runs_per_cycle) { - log_add(L_WARNING, "tapecycle (%d) <= runspercycle (%d)", + log_add(L_WARNING, _("tapecycle (%d) <= runspercycle (%d)"), conf_tapecycle, runs_per_cycle); } tape = lookup_tapetype(conf_tapetype); - if(conf_maxdumpsize > 0) { + if(conf_maxdumpsize > (gint64)0) { tape_length = conf_maxdumpsize; + g_fprintf(stderr, "planner: tape_length is set from maxdumpsize (%jd KB)\n", + (intmax_t)conf_maxdumpsize); } else { - tape_length = tape->length * conf_runtapes; - } - tape_mark = tape->filemark; - tt_blocksize_kb = tape->blocksize; + tape_length = tapetype_get_length(tape) * (gint64)conf_runtapes; + g_fprintf(stderr, "planner: tape_length is set from tape length (%jd KB) * runtapes (%d) == %jd KB\n", + (intmax_t)tapetype_get_length(tape), + conf_runtapes, + (intmax_t)tape_length); + } + tape_mark = (size_t)tapetype_get_filemark(tape); + tt_blocksize_kb = (size_t)tapetype_get_blocksize(tape); tt_blocksize = tt_blocksize_kb * 1024; - fprintf(stderr, "%s: time %s: startup took %s secs\n", + g_fprintf(stderr, _("%s: time %s: startup took %s secs\n"), get_pname(), walltime_str(curclock()), walltime_str(timessub(curclock(), section_start))); @@ -344,38 +446,61 @@ char **argv; * dump schedule. */ - fprintf(stderr,"\nSENDING FLUSHES...\n"); + g_fprintf(stderr,_("\nSENDING FLUSHES...\n")); + + if(conf_autoflush && !no_taper) { + dumpfile_t file; + GSList *holding_list, *holding_file; + char *qdisk, *qhname; - if(conf_autoflush) { - dumpfile_t file; - sl_t *holding_list; - sle_t *holding_file; - holding_list = get_flush(NULL, NULL, 0, 0); - for(holding_file=holding_list->first; holding_file != NULL; + /* get *all* flushable files in holding, without checking against + * the disklist (which may not contain some of the dumps) */ + holding_list = holding_get_files_for_flush(NULL); + for(holding_file=holding_list; holding_file != NULL; holding_file = holding_file->next) { - get_dumpfile(holding_file->name, &file); - - log_add(L_DISK, "%s %s", file.name, file.disk); - fprintf(stderr, + holding_file_get_dumpfile((char *)holding_file->data, &file); + + if (holding_file_size((char *)holding_file->data, 1) <= 0) { + log_add(L_INFO, "%s: removing file with no data.", + (char *)holding_file->data); + holding_file_unlink((char *)holding_file->data); + dumpfile_free_data(&file); + continue; + } + + /* see if this matches the command-line arguments */ + if (conf_autoflush == 1 && + !match_dumpfile(&file, exact_match, argc-diskarg_offset, + argv+diskarg_offset)) { + continue; + } + + qdisk = quote_string(file.disk); + qhname = quote_string((char *)holding_file->data); + log_add(L_DISK, "%s %s", file.name, qdisk); + g_fprintf(stderr, "FLUSH %s %s %s %d %s\n", file.name, - file.disk, + qdisk, file.datestamp, file.dumplevel, - holding_file->name); - fprintf(stdout, + qhname); + g_fprintf(stdout, "FLUSH %s %s %s %d %s\n", file.name, - file.disk, + qdisk, file.datestamp, file.dumplevel, - holding_file->name); + qhname); + amfree(qdisk); + amfree(qhname); + dumpfile_free_data(&file); } - free_sl(holding_list); + slist_free_full(holding_list, g_free); holding_list = NULL; } - fprintf(stderr, "ENDFLUSH\n"); - fprintf(stdout, "ENDFLUSH\n"); + g_fprintf(stderr, _("ENDFLUSH\n")); + g_fprintf(stdout, _("ENDFLUSH\n")); fflush(stdout); /* @@ -386,7 +511,7 @@ char **argv; * based on the curinfo database. */ - fprintf(stderr,"\nSETTING UP FOR ESTIMATES...\n"); + g_fprintf(stderr,_("\nSETTING UP FOR ESTIMATES...\n")); section_start = curclock(); startq.head = startq.tail = NULL; @@ -397,7 +522,7 @@ char **argv; } } - fprintf(stderr, "%s: time %s: setting up estimates took %s secs\n", + g_fprintf(stderr, _("%s: time %s: setting up estimates took %s secs\n"), get_pname(), walltime_str(curclock()), walltime_str(timessub(curclock(), section_start))); @@ -412,7 +537,7 @@ char **argv; /* go out and get the dump estimates */ - fprintf(stderr,"\nGETTING ESTIMATES...\n"); + g_fprintf(stderr,_("\nGETTING ESTIMATES...\n")); section_start = curclock(); estq.head = estq.tail = NULL; @@ -422,7 +547,7 @@ char **argv; get_estimates(); - fprintf(stderr, "%s: time %s: getting estimates took %s secs\n", + g_fprintf(stderr, _("%s: time %s: getting estimates took %s secs\n"), get_pname(), walltime_str(curclock()), walltime_str(timessub(curclock(), section_start))); @@ -436,6 +561,9 @@ char **argv; dump_queue("FAILED", failq, 15, stderr); dump_queue("DONE", estq, 15, stderr); + if (!empty(failq)) { + exit_status = EXIT_FAILURE; + } /* * 6. Analyze Dump Estimates @@ -446,11 +574,11 @@ char **argv; * total expected size is accumulated as well. */ - fprintf(stderr,"\nANALYZING ESTIMATES...\n"); + g_fprintf(stderr,_("\nANALYZING ESTIMATES...\n")); section_start = curclock(); /* an empty tape still has a label and an endmark */ - total_size = (tt_blocksize_kb + tape_mark) * 2; + total_size = ((gint64)tt_blocksize_kb + (gint64)tape_mark) * (gint64)2; total_lev0 = 0.0; balanced_size = 0.0; @@ -458,6 +586,8 @@ char **argv; while(!empty(estq)) analyze_estimate(dequeue_disk(&estq)); while(!empty(failq)) handle_failed(dequeue_disk(&failq)); + run_server_global_scripts(EXECUTE_ON_POST_ESTIMATE, get_config_name()); + /* * At this point, all the disks are on schedq sorted by priority. * The total estimated size of the backups is in total_size. @@ -466,11 +596,16 @@ char **argv; { disk_t *dp; - fprintf(stderr, "INITIAL SCHEDULE (size " AM64_FMT "):\n", total_size); + g_fprintf(stderr, _("INITIAL SCHEDULE (size %lld):\n"), + (long long)total_size); for(dp = schedq.head; dp != NULL; dp = dp->next) { - fprintf(stderr, " %s %s pri %d lev %d size %ld\n", - dp->host->hostname, dp->name, est(dp)->dump_priority, - est(dp)->dump_level, est(dp)->dump_size); + qname = quote_string(dp->name); + g_fprintf(stderr, _(" %s %s pri %d lev %d nsize %lld csize %lld\n"), + dp->host->hostname, qname, est(dp)->dump_priority, + est(dp)->dump_est->level, + (long long)est(dp)->dump_est->nsize, + (long long)est(dp)->dump_est->csize); + amfree(qname); } } @@ -487,17 +622,20 @@ char **argv; * until the dumps fit on the tape. */ - fprintf(stderr, - "\nDELAYING DUMPS IF NEEDED, total_size " AM64_FMT ", tape length " AM64_FMT " mark " AM64_FMT "\n", - total_size, tape_length, tape_mark); + g_fprintf(stderr, _("\nDELAYING DUMPS IF NEEDED, total_size %lld, tape length %lld mark %zu\n"), + (long long)total_size, + (long long)tape_length, + tape_mark); initial_size = total_size; delay_dumps(); /* XXX - why bother checking this? */ - if(empty(schedq) && total_size < initial_size) - error("cannot fit anything on tape, bailing out"); + if(empty(schedq) && total_size < initial_size) { + error(_("cannot fit anything on tape, bailing out")); + /*NOTREACHED*/ + } /* @@ -518,8 +656,8 @@ char **argv; * a big bump. */ - fprintf(stderr, - "\nPROMOTING DUMPS IF NEEDED, total_lev0 %1.0f, balanced_size %1.0f...\n", + g_fprintf(stderr, + _("\nPROMOTING DUMPS IF NEEDED, total_lev0 %1.0lf, balanced_size %1.0lf...\n"), total_lev0, balanced_size); balance_threshold = balanced_size * PROMOTE_THRESHOLD; @@ -529,7 +667,7 @@ char **argv; moved_one = promote_hills(); - fprintf(stderr, "%s: time %s: analysis took %s secs\n", + g_fprintf(stderr, _("%s: time %s: analysis took %s secs\n"), get_pname(), walltime_str(curclock()), walltime_str(timessub(curclock(), section_start))); @@ -542,28 +680,28 @@ char **argv; * on stderr for the debug file. */ - fprintf(stderr,"\nGENERATING SCHEDULE:\n--------\n"); - - while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq)); - fprintf(stderr, "--------\n"); + g_fprintf(stderr,_("\nGENERATING SCHEDULE:\n--------\n")); + if (empty(schedq)) { + exit_status = EXIT_FAILURE; + g_fprintf(stderr, _("--> Generated empty schedule! <--\n")); + } else { + while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq)); + } + g_fprintf(stderr, _("--------\n")); close_infofile(); - log_add(L_FINISH, "date %s time %s", datestamp, walltime_str(curclock())); + log_add(L_FINISH, _("date %s time %s"), planner_timestamp, walltime_str(curclock())); + log_add(L_INFO, "pid-done %ld", (long)getpid()); - amfree(datestamp); - amfree(config_dir); - amfree(config_name); + clear_tapelist(); + amfree(planner_timestamp); amfree(our_feature_string); am_release_feature_set(our_features); our_features = NULL; - malloc_size_2 = malloc_inuse(&malloc_hist_2); - - if(malloc_size_1 != malloc_size_2) { - malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2); - } + dbclose(); - return 0; + return exit_status; } @@ -574,81 +712,126 @@ char **argv; * */ -static void askfor P((est_t *, int, int, info_t *)); -static int last_level P((info_t *info)); /* subroutines */ -static long est_size P((disk_t *dp, int level)); -static long est_tape_size P((disk_t *dp, int level)); -static int next_level0 P((disk_t *dp, info_t *info)); -static int runs_at P((info_t *info, int lev)); -static long bump_thresh P((int level, long size_level_0, int bumppercent, int bumpsize, double bumpmult)); -static int when_overwrite P((char *label)); - -static void askfor(ep, seq, lev, info) -est_t *ep; /* esimate data block */ -int seq; /* sequence number of request */ -int lev; /* dump level being requested */ -info_t *info; /* info block for disk */ +static void askfor(est_t *, int, int, info_t *); +static int last_level(info_t *info); /* subroutines */ +static one_est_t *est_for_level(disk_t *dp, int level); +static void est_csize(disk_t *dp, one_est_t *one_est); +static gint64 est_tape_size(disk_t *dp, int level); +static int next_level0(disk_t *dp, info_t *info); +static int runs_at(info_t *info, int lev); +static gint64 bump_thresh(int level, gint64 size_level_0, int bumppercent, gint64 bumpsize, double bumpmult); +static int when_overwrite(char *label); + +static void askfor( + est_t *ep, /* esimate data block */ + int seq, /* sequence number of request */ + int lev, /* dump level being requested */ + info_t *info) /* info block for disk */ { if(seq < 0 || seq >= MAX_LEVELS) { - error("error [planner askfor: seq out of range 0..%d: %d]", + error(_("error [planner askfor: seq out of range 0..%d: %d]"), MAX_LEVELS, seq); + /*NOTREACHED*/ } if(lev < -1 || lev >= DUMP_LEVELS) { - error("error [planner askfor: lev out of range -1..%d: %d]", + error(_("error [planner askfor: lev out of range -1..%d: %d]"), DUMP_LEVELS, lev); + /*NOTREACHED*/ } if (lev == -1) { - ep->level[seq] = -1; - ep->dumpdate[seq] = (char *)0; - ep->est_size[seq] = -2; + ep->estimate[seq].level = -1; + ep->estimate[seq].dumpdate = (char *)0; + ep->estimate[seq].nsize = (gint64)-3; + ep->estimate[seq].csize = (gint64)-3; + ep->estimate[seq].guessed = 0; return; } - ep->level[seq] = lev; + ep->estimate[seq].level = lev; - ep->dumpdate[seq] = stralloc(get_dumpdate(info,lev)); - malloc_mark(ep->dumpdate[seq]); + ep->estimate[seq].dumpdate = stralloc(get_dumpdate(info,lev)); - ep->est_size[seq] = -2; + ep->estimate[seq].nsize = (gint64)-3; + ep->estimate[seq].csize = (gint64)-3; + ep->estimate[seq].guessed = 0; return; } static void -setup_estimate(dp) - disk_t *dp; +setup_estimate( + disk_t *dp) { est_t *ep; - info_t info; + info_t *info; int i; + char *qname; + int overwrite_runs; assert(dp && dp->host); - fprintf(stderr, "%s: time %s: setting up estimates for %s:%s\n", + + qname = quote_string(dp->name); + g_fprintf(stderr, _("%s: time %s: setting up estimates for %s:%s\n"), get_pname(), walltime_str(curclock()), - dp->host->hostname, dp->name); + dp->host->hostname, qname); /* get current information about disk */ - if(get_info(dp->host->hostname, dp->name, &info)) { + info = g_new0(info_t, 1); + if(get_info(dp->host->hostname, dp->name, info)) { /* no record for this disk, make a note of it */ - log_add(L_INFO, "Adding new disk %s:%s.", dp->host->hostname, dp->name); + log_add(L_INFO, _("Adding new disk %s:%s."), dp->host->hostname, qname); + } + + if (dp->data_path == DATA_PATH_DIRECTTCP) { + if (dp->compress != COMP_NONE) { + log_add(L_FAIL, _("%s %s %s 0 [Can't compress directtcp data-path]"), + dp->host->hostname, qname, planner_timestamp); + g_fprintf(stderr,_("%s:%s lev 0 skipped can't compress directtcp data-path\n"), + dp->host->hostname, qname); + amfree(qname); + return; + } + if (dp->encrypt != ENCRYPT_NONE) { + log_add(L_FAIL, _("%s %s %s 0 [Can't encrypt directtcp data-path]"), + dp->host->hostname, qname, planner_timestamp); + g_fprintf(stderr,_("%s:%s lev 0 skipped can't encrypt directtcp data-path\n"), + dp->host->hostname, qname); + amfree(qname); + return; + } + if (dp->to_holdingdisk == HOLD_REQUIRED) { + log_add(L_FAIL, _("%s %s %s 0 [Holding disk can't be use for directtcp data-path]"), + dp->host->hostname, qname, planner_timestamp); + g_fprintf(stderr,_("%s:%s lev 0 skipped Holding disk can't be use for directtcp data-path\n"), + dp->host->hostname, qname); + amfree(qname); + return; + } else if (dp->to_holdingdisk == HOLD_AUTO) { + g_fprintf(stderr,_("%s:%s Disabling holding disk\n"), + dp->host->hostname, qname); + dp->to_holdingdisk = HOLD_NEVER; + } } /* setup working data struct for disk */ - ep = alloc(sizeof(est_t)); - malloc_mark(ep); + ep = alloc(SIZEOF(est_t)); dp->up = (void *) ep; + ep->info = info; ep->state = DISK_READY; - ep->dump_size = -1; ep->dump_priority = dp->priority; ep->errstr = 0; ep->promote = 0; + ep->post_dle = 0; + ep->degr_mesg = NULL; + ep->dump_est = &default_one_est; + ep->degr_est = &default_one_est; /* calculated fields */ - if (ISSET(info.command, FORCE_FULL)) { + if (ISSET(info->command, FORCE_FULL)) { /* force a level 0, kind of like a new disk */ if(dp->strategy == DS_NOFULL) { /* @@ -662,59 +845,84 @@ setup_estimate(dp) * hosed when that tape gets re-used next. Disallow this for * now. */ - log_add(L_ERROR, - "Cannot force full dump of %s:%s with no-full option.", - dp->host->hostname, dp->name); + log_add(L_WARNING, + _("Cannot force full dump of %s:%s with no-full option."), + dp->host->hostname, qname); /* clear force command */ - CLR(info.command, FORCE_FULL); - if(put_info(dp->host->hostname, dp->name, &info)) - error("could not put info record for %s:%s: %s", - dp->host->hostname, dp->name, strerror(errno)); - ep->last_level = last_level(&info); - ep->next_level0 = next_level0(dp, &info); - } - else { + CLR(info->command, FORCE_FULL); + ep->last_level = last_level(info); + ep->next_level0 = next_level0(dp, info); + } else if (dp->strategy == DS_INCRONLY) { + log_add(L_WARNING, + _("Cannot force full dump of %s:%s with incronly option."), + dp->host->hostname, qname); + + /* clear force command */ + CLR(info->command, FORCE_FULL); + ep->last_level = last_level(info); + ep->next_level0 = next_level0(dp, info); + } else { + ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode"); ep->last_level = -1; ep->next_level0 = -conf_dumpcycle; - log_add(L_INFO, "Forcing full dump of %s:%s as directed.", - dp->host->hostname, dp->name); + log_add(L_INFO, _("Forcing full dump of %s:%s as directed."), + dp->host->hostname, qname); } } else if(dp->strategy == DS_NOFULL) { /* force estimate of level 1 */ ep->last_level = 1; - ep->next_level0 = next_level0(dp, &info); + ep->next_level0 = next_level0(dp, info); } else { - ep->last_level = last_level(&info); - ep->next_level0 = next_level0(dp, &info); + ep->last_level = last_level(info); + ep->next_level0 = next_level0(dp, info); } /* adjust priority levels */ + /* warn if dump will be overwritten */ + if (ep->last_level > -1 && strlen(info->inf[0].label) > 0) { + overwrite_runs = when_overwrite(info->inf[0].label); + if(overwrite_runs == 0) { + log_add(L_WARNING, _("Last full dump of %s:%s " + "on tape %s overwritten on this run."), + dp->host->hostname, qname, info->inf[0].label); + } else if(overwrite_runs <= RUNS_REDZONE) { + log_add(L_WARNING, + plural(_("Last full dump of %s:%s on tape %s overwritten in %d run."), + _("Last full dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs), + dp->host->hostname, qname, info->inf[0].label, + overwrite_runs); + } + } + + /* warn if last level 1 will be overwritten */ + if (ep->last_level > 1 && strlen(info->inf[1].label) > 0) { + overwrite_runs = when_overwrite(info->inf[1].label); + if(overwrite_runs == 0) { + log_add(L_WARNING, _("Last level 1 dump of %s:%s " + "on tape %s overwritten on this run, resetting to level 1"), + dp->host->hostname, qname, info->inf[1].label); + ep->last_level = 0; + } else if(overwrite_runs <= RUNS_REDZONE) { + log_add(L_WARNING, + plural(_("Last level 1 dump of %s:%s on tape %s overwritten in %d run."), + _("Last level 1 dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs), + dp->host->hostname, qname, info->inf[1].label, + overwrite_runs); + } + } + if(ep->next_level0 < 0) { - fprintf(stderr,"%s:%s overdue %d day%s for level 0\n", - dp->host->hostname, dp->name, - - ep->next_level0, ((- ep->next_level0) == 1) ? "" : "s"); + g_fprintf(stderr,plural(_("%s:%s overdue %d day for level 0\n"), + _("%s:%s overdue %d days for level 0\n"), + (-ep->next_level0)), + dp->host->hostname, qname, (-ep->next_level0)); ep->dump_priority -= ep->next_level0; - /* warn if dump will be overwritten */ - if(ep->last_level > -1) { - int overwrite_runs = when_overwrite(info.inf[0].label); - if(overwrite_runs == 0) { - log_add(L_WARNING, - "Last full dump of %s:%s on tape %s overwritten on this run.", - dp->host->hostname, dp->name, info.inf[0].label); - } - else if(overwrite_runs < RUNS_REDZONE) { - log_add(L_WARNING, - "Last full dump of %s:%s on tape %s overwritten in %d run%s.", - dp->host->hostname, dp->name, info.inf[0].label, - overwrite_runs, overwrite_runs == 1? "" : "s"); - } - } } - else if (ISSET(info.command, FORCE_FULL)) + else if (ISSET(info->command, FORCE_FULL)) ep->dump_priority += 1; /* else XXX bump up the priority of incrementals that failed last night */ @@ -723,25 +931,28 @@ setup_estimate(dp) if(dp->skip_full && dp->strategy != DS_NOINC) { if(ep->next_level0 <= 0) { /* update the date field */ - info.inf[0].date = today; - CLR(info.command, FORCE_FULL); + info->inf[0].date = today; + CLR(info->command, FORCE_FULL); ep->next_level0 += conf_dumpcycle; ep->last_level = 0; - if(put_info(dp->host->hostname, dp->name, &info)) - error("could not put info record for %s:%s: %s", - dp->host->hostname, dp->name, strerror(errno)); - log_add(L_INFO, "Skipping full dump of %s:%s today.", - dp->host->hostname, dp->name); - fprintf(stderr,"%s:%s lev 0 skipped due to skip-full flag\n", - dp->host->hostname, dp->name); + if(put_info(dp->host->hostname, dp->name, info)) { + error(_("could not put info record for %s:%s: %s"), + dp->host->hostname, qname, strerror(errno)); + /*NOTREACHED*/ + } + log_add(L_INFO, _("Skipping full dump of %s:%s today."), + dp->host->hostname, qname); + g_fprintf(stderr,_("%s:%s lev 0 skipped due to skip-full flag\n"), + dp->host->hostname, qname); /* don't enqueue the disk */ - askfor(ep, 0, -1, &info); - askfor(ep, 1, -1, &info); - askfor(ep, 2, -1, &info); - fprintf(stderr, "%s: SKIPPED %s %s 0 [skip-full]\n", - get_pname(), dp->host->hostname, dp->name); - log_add(L_SUCCESS, "%s %s %s 0 [skipped: skip-full]", - dp->host->hostname, dp->name, datestamp); + askfor(ep, 0, -1, info); + askfor(ep, 1, -1, info); + askfor(ep, 2, -1, info); + g_fprintf(stderr, _("%s: SKIPPED %s %s 0 [skip-full]\n"), + get_pname(), dp->host->hostname, qname); + log_add(L_SUCCESS, _("%s %s %s 0 [skipped: skip-full]"), + dp->host->hostname, qname, planner_timestamp); + amfree(qname); return; } @@ -751,59 +962,62 @@ setup_estimate(dp) } if(ep->next_level0 == 1) { - log_add(L_WARNING, "Skipping full dump of %s:%s tomorrow.", - dp->host->hostname, dp->name); + log_add(L_WARNING, _("Skipping full dump of %s:%s tomorrow."), + dp->host->hostname, qname); } } - if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info.command, FORCE_FULL)) { + if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info->command, FORCE_FULL)) { /* don't enqueue the disk */ - askfor(ep, 0, -1, &info); - askfor(ep, 1, -1, &info); - askfor(ep, 2, -1, &info); - log_add(L_FAIL, "%s %s 19000101 1 [Skipping incronly because no full dump were done]", - dp->host->hostname, dp->name); - fprintf(stderr,"%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n", - dp->host->hostname, dp->name); + askfor(ep, 0, -1, info); + askfor(ep, 1, -1, info); + askfor(ep, 2, -1, info); + log_add(L_FAIL, _("%s %s 19000101 1 [Skipping incronly because no full dump were done]"), + dp->host->hostname, qname); + g_fprintf(stderr,_("%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n"), + dp->host->hostname, qname); + amfree(qname); return; } /* handle "skip-incr" type archives */ if(dp->skip_incr && ep->next_level0 > 0) { - fprintf(stderr,"%s:%s lev 1 skipped due to skip-incr flag\n", - dp->host->hostname, dp->name); + g_fprintf(stderr,_("%s:%s lev 1 skipped due to skip-incr flag\n"), + dp->host->hostname, qname); /* don't enqueue the disk */ - askfor(ep, 0, -1, &info); - askfor(ep, 1, -1, &info); - askfor(ep, 2, -1, &info); + askfor(ep, 0, -1, info); + askfor(ep, 1, -1, info); + askfor(ep, 2, -1, info); - fprintf(stderr, "%s: SKIPPED %s %s 1 [skip-incr]\n", - get_pname(), dp->host->hostname, dp->name); + g_fprintf(stderr, _("%s: SKIPPED %s %s 1 [skip-incr]\n"), + get_pname(), dp->host->hostname, qname); - log_add(L_SUCCESS, "%s %s %s 1 [skipped: skip-incr]", - dp->host->hostname, dp->name, datestamp); + log_add(L_SUCCESS, _("%s %s %s 1 [skipped: skip-incr]"), + dp->host->hostname, qname, planner_timestamp); + amfree(qname); return; } if( ep->last_level == -1 && ep->next_level0 > 0 && dp->strategy != DS_NOFULL && dp->strategy != DS_INCRONLY && conf_reserve == 100) { - log_add(L_WARNING, - "%s:%s mismatch: no tapelist record, but curinfo next_level0: %d.", - dp->host->hostname, dp->name, ep->next_level0); + log_add(L_WARNING, _("%s:%s mismatch: no tapelist record, " + "but curinfo next_level0: %d."), + dp->host->hostname, qname, ep->next_level0); ep->next_level0 = 0; } - if(ep->last_level == 0) ep->level_days = 0; - else ep->level_days = runs_at(&info, ep->last_level); - ep->last_lev0size = info.inf[0].csize; + //if(ep->last_level == 0) ep->level_days = 0; + //else ep->level_days = runs_at(info, ep->last_level); + ep->level_days = runs_at(info, ep->last_level); + ep->last_lev0size = info->inf[0].csize; - ep->fullrate = perf_average(info.full.rate, 0.0); - ep->incrrate = perf_average(info.incr.rate, 0.0); + ep->fullrate = perf_average(info->full.rate, 0.0); + ep->incrrate = perf_average(info->incr.rate, 0.0); - ep->fullcomp = perf_average(info.full.comp, dp->comprate[0]); - ep->incrcomp = perf_average(info.incr.comp, dp->comprate[1]); + ep->fullcomp = perf_average(info->full.comp, dp->comprate[0]); + ep->incrcomp = perf_average(info->incr.comp, dp->comprate[1]); /* determine which estimates to get */ @@ -811,27 +1025,31 @@ setup_estimate(dp) if (dp->strategy == DS_NOINC || (!dp->skip_full && - (!ISSET(info.command, FORCE_BUMP) || + (!ISSET(info->command, FORCE_BUMP) || dp->skip_incr || ep->last_level == -1))) { - if(info.command & FORCE_BUMP && ep->last_level == -1) { + if(ISSET(info->command, FORCE_BUMP) && ep->last_level == -1) { log_add(L_INFO, - "Remove force-bump command of %s:%s because it's a new disk.", - dp->host->hostname, dp->name); + _("Remove force-bump command of %s:%s because it's a new disk."), + dp->host->hostname, qname); } switch (dp->strategy) { case DS_STANDARD: case DS_NOINC: - askfor(ep, i++, 0, &info); + askfor(ep, i++, 0, info); + if (ep->last_level == -1) + ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode"); + else + ep->degr_mesg = _("Skipping: strategy NOINC can't be dumped in degraded mode"); if(dp->skip_full) { - log_add(L_INFO, - "Ignoring skip_full for %s:%s because the strategy is NOINC.", - dp->host->hostname, dp->name); + log_add(L_INFO, _("Ignoring skip-full for %s:%s " + "because the strategy is NOINC."), + dp->host->hostname, qname); } - if(info.command & FORCE_BUMP) { + if(ISSET(info->command, FORCE_BUMP)) { log_add(L_INFO, - "Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC.", - dp->host->hostname, dp->name); + _("Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC."), + dp->host->hostname, qname); } break; @@ -840,16 +1058,18 @@ setup_estimate(dp) break; case DS_INCRONLY: - if (ISSET(info.command, FORCE_FULL)) - askfor(ep, i++, 0, &info); + if (ISSET(info->command, FORCE_FULL)) + ep->last_level = 0; break; } } if(!dp->skip_incr && !(dp->strategy == DS_NOINC)) { if(ep->last_level == -1) { /* a new disk */ + if (ep->degr_mesg == NULL) + ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode"); if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) { - askfor(ep, i++, 1, &info); + askfor(ep, i++, 1, info); } else { assert(!dp->skip_full); /* should be handled above */ } @@ -858,21 +1078,23 @@ setup_estimate(dp) curr_level = ep->last_level; - if (ISSET(info.command, FORCE_NO_BUMP)) { + if (ISSET(info->command, FORCE_NO_BUMP)) { if(curr_level > 0) { /* level 0 already asked for */ - askfor(ep, i++, curr_level, &info); + askfor(ep, i++, curr_level, info); } - log_add(L_INFO,"Preventing bump of %s:%s as directed.", - dp->host->hostname, dp->name); - } else if (ISSET(info.command, FORCE_BUMP) + log_add(L_INFO,_("Preventing bump of %s:%s as directed."), + dp->host->hostname, qname); + ep->degr_mesg = _("Skipping: force-no-bump disk can't be dumped in degraded mode"); + } else if (ISSET(info->command, FORCE_BUMP) && curr_level + 1 < DUMP_LEVELS) { - askfor(ep, i++, curr_level+1, &info); - log_add(L_INFO,"Bumping of %s:%s at level %d as directed.", - dp->host->hostname, dp->name, curr_level+1); + askfor(ep, i++, curr_level+1, info); + log_add(L_INFO,_("Bumping of %s:%s at level %d as directed."), + dp->host->hostname, qname, curr_level+1); + ep->degr_mesg = _("Skipping: force-bump disk can't be dumped in degraded mode"); } else if (curr_level == 0) { - askfor(ep, i++, 1, &info); + askfor(ep, i++, 1, info); } else { - askfor(ep, i++, curr_level, &info); + askfor(ep, i++, curr_level, info); /* * If last time we dumped less than the threshold, then this * time we will too, OR the extra size will be charged to both @@ -880,81 +1102,95 @@ setup_estimate(dp) * if we haven't been at this level 2 days, or the dump failed * last night, we can't bump. */ - if((info.inf[curr_level].size == 0 || /* no data, try it anyway */ - (((info.inf[curr_level].size > bump_thresh(curr_level, info.inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult))) + if((info->inf[curr_level].size == (gint64)0 || /* no data, try it anyway */ + (((info->inf[curr_level].size > bump_thresh(curr_level, info->inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult))) && ep->level_days >= dp->bumpdays)) && curr_level + 1 < DUMP_LEVELS) { - askfor(ep, i++, curr_level+1, &info); + askfor(ep, i++, curr_level+1, info); } - } + } } } - while(i < MAX_LEVELS) /* mark end of estimates */ - askfor(ep, i++, -1, &info); + while(i < MAX_LEVELS) /* mark end of estimates */ + askfor(ep, i++, -1, info); /* debug output */ - fprintf(stderr, "setup_estimate: %s:%s: command %d, options: %s last_level %d next_level0 %d level_days %d getting estimates %d (%ld) %d (%ld) %d (%ld)\n", - dp->host->hostname, dp->name, info.command, + g_fprintf(stderr, _("setup_estimate: %s:%s: command %u, options: %s " + "last_level %d next_level0 %d level_days %d getting estimates " + "%d (%lld) %d (%lld) %d (%lld)\n"), + dp->host->hostname, qname, info->command, dp->strategy == DS_NOFULL ? "no-full" : dp->strategy == DS_INCRONLY ? "incr-only" : dp->skip_full ? "skip-full" : dp->skip_incr ? "skip-incr" : "none", ep->last_level, ep->next_level0, ep->level_days, - ep->level[0], ep->est_size[0], - ep->level[1], ep->est_size[1], - ep->level[2], ep->est_size[2]); + ep->estimate[0].level, (long long)ep->estimate[0].nsize, + ep->estimate[1].level, (long long)ep->estimate[1].nsize, + ep->estimate[2].level, (long long)ep->estimate[2].nsize); - assert(ep->level[0] != -1); + assert(ep->estimate[0].level != -1); enqueue_disk(&startq, dp); + amfree(qname); } -static int when_overwrite(label) -char *label; +static int when_overwrite( + char *label) { tape_t *tp; + int runtapes; + + runtapes = conf_runtapes; + if(runtapes == 0) runtapes = 1; if((tp = lookup_tapelabel(label)) == NULL) return 1; /* "shouldn't happen", but trigger warning message */ - else if(!reusable_tape(tp)) + else if(tp->reuse == 0) return 1024; else if(lookup_nb_tape() > conf_tapecycle) - return (lookup_nb_tape() - tp->position) / conf_runtapes; + return (lookup_nb_tape() - tp->position) / runtapes; else - return (conf_tapecycle - tp->position) / conf_runtapes; + return (conf_tapecycle - tp->position) / runtapes; } /* Return the estimated size for a particular dump */ -static long est_size(dp, level) -disk_t *dp; -int level; +static one_est_t * +est_for_level( + disk_t *dp, + int level) { int i; - for(i = 0; i < MAX_LEVELS; i++) { - if(level == est(dp)->level[i]) - return est(dp)->est_size[i]; + if (level < 0 || level >= DUMP_LEVELS) + return &default_one_est; + + for (i = 0; i < MAX_LEVELS; i++) { + if (level == est(dp)->estimate[i].level) { + if (est(dp)->estimate[i].csize <= -1) { + est_csize(dp, &est(dp)->estimate[i]); + } + return &est(dp)->estimate[i]; + } } - return -1; + return &default_one_est; } /* Return the estimated on-tape size of a particular dump */ -static long est_tape_size(dp, level) -disk_t *dp; -int level; +static void +est_csize( + disk_t *dp, + one_est_t *one_est) { - long size; + gint64 size = one_est->nsize; double ratio; - size = est_size(dp, level); - - if(size == -1) return size; - - if(dp->compress == COMP_NONE) - return size; + if (dp->compress == COMP_NONE) { + one_est->csize = one_est->nsize; + return; + } - if(level == 0) ratio = est(dp)->fullcomp; + if (one_est->level == 0) ratio = est(dp)->fullcomp; else ratio = est(dp)->incrcomp; /* @@ -966,26 +1202,38 @@ int level; * (RUG@USM.Uni-Muenchen.DE) */ - if(ratio > 1.1) ratio = 1.1; + if (ratio > 1.1) ratio = 1.1; - size *= ratio; + size = (gint64)((double)size * ratio); /* * Ratio can be very small in some error situations, so make sure * size goes back greater than zero. It may not be right, but * indicates we did get an estimate. */ - if(size <= 0) { - size = 1; + if (size <= (gint64)0) { + size = (gint64)1; } - return size; + one_est->csize = size; +} + +static gint64 est_tape_size( + disk_t *dp, + int level) +{ + one_est_t *dump_est; + + dump_est = est_for_level(dp, level); + if (dump_est->level >= 0 && dump_est->csize <= -1) + est_csize(dp, dump_est); + return dump_est->csize; } /* what was the level of the last successful dump to tape? */ -static int last_level(info) -info_t *info; +static int last_level( + info_t *info) { int min_pos, min_level, i; time_t lev0_date, last_date; @@ -1026,9 +1274,9 @@ info_t *info; /* when is next level 0 due? 0 = today, 1 = tomorrow, etc*/ static int -next_level0(dp, info) - disk_t *dp; - info_t *info; +next_level0( + disk_t *dp, + info_t *info) { if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) return 1; /* fake it */ @@ -1041,50 +1289,52 @@ next_level0(dp, info) } /* how many runs at current level? */ -static int runs_at(info, lev) -info_t *info; -int lev; +static int runs_at( + info_t *info, + int lev) { tape_t *cur_tape, *old_tape; int last, nb_runs; last = last_level(info); if(lev != last) return 0; - if(lev == 0) return 1; - if(info->consecutive_runs != -1) return info->consecutive_runs; + if(lev == 0) return 1; /* to keep compatibility with old infofile */ cur_tape = lookup_tapelabel(info->inf[lev].label); old_tape = lookup_tapelabel(info->inf[lev-1].label); if(cur_tape == NULL || old_tape == NULL) return 0; - nb_runs = (old_tape->position - cur_tape->position) / conf_runtapes; + if(conf_runtapes == 0) + nb_runs = (old_tape->position - cur_tape->position) / 1; + else + nb_runs = (old_tape->position - cur_tape->position) / conf_runtapes; info->consecutive_runs = nb_runs; return nb_runs; } -static long bump_thresh(level, size_level_0, bumppercent, bumpsize, bumpmult) -int level; -long size_level_0; -int bumppercent; -int bumpsize; -double bumpmult; +static gint64 bump_thresh( + int level, + gint64 size_level_0, + int bumppercent, + gint64 bumpsize, + double bumpmult) { double bump; - if(bumppercent != 0 && size_level_0 > 1024) { - bump = (size_level_0 * bumppercent)/100.0; + if ((bumppercent != 0) && (size_level_0 > (gint64)1024)) { + bump = ((double)size_level_0 * (double)bumppercent) / 100.0; } else { - bump = bumpsize; + bump = (double)bumpsize; } while(--level) bump = bump * bumpmult; - return (long)bump; + return (gint64)bump; } @@ -1095,15 +1345,15 @@ double bumpmult; * */ -static void getsize P((am_host_t *hostp)); -static disk_t *lookup_hostdisk P((am_host_t *hp, char *str)); -static void handle_result P((void *datap, pkt_t *pkt, security_handle_t *sech)); +static void getsize(am_host_t *hostp); +static disk_t *lookup_hostdisk(am_host_t *hp, char *str); +static void handle_result(void *datap, pkt_t *pkt, security_handle_t *sech); -static void get_estimates P((void)) +static void get_estimates(void) { am_host_t *hostp; - disk_t *dp; + disk_t *dp, *dp1; int something_started; something_started = 1; @@ -1113,6 +1363,14 @@ static void get_estimates P((void)) hostp = dp->host; if(hostp->up == HOST_READY) { something_started = 1; + run_server_host_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, + get_config_name(), hostp); + for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) { + if (dp1->todo) + run_server_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, + get_config_name(), dp1, + est(dp1)->estimate[0].level); + } getsize(hostp); protocol_check(); /* @@ -1127,83 +1385,58 @@ static void get_estimates P((void)) while(!empty(waitq)) { disk_t *dp = dequeue_disk(&waitq); - est(dp)->errstr = "hmm, disk was stranded on waitq"; + est(dp)->errstr = _("hmm, disk was stranded on waitq"); enqueue_disk(&failq, dp); } while(!empty(pestq)) { disk_t *dp = dequeue_disk(&pestq); - - if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < 0) { - if(est(dp)->est_size[0] == -1) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[0]); - } - else { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d timed out.", - dp->host->hostname, dp->name, - est(dp)->level[0]); - } - est(dp)->level[0] = -1; - } - - if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < 0) { - if(est(dp)->est_size[1] == -1) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[1]); - } - else { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d timed out.", - dp->host->hostname, dp->name, - est(dp)->level[1]); - } - est(dp)->level[1] = -1; - } - - if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < 0) { - if(est(dp)->est_size[2] == -1) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[2]); - } - else { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d timed out.", - dp->host->hostname, dp->name, - est(dp)->level[2]); - } - est(dp)->level[2] = -1; + char * qname = quote_string(dp->name); + int i; + + for (i=0; i < MAX_LEVELS; i++) { + if (est(dp)->estimate[i].level != -1 && + est(dp)->estimate[i].nsize < (gint64)0) { + if (est(dp)->estimate[i].nsize == (gint64)-3) { + log_add(L_WARNING, + _("disk %s:%s, estimate of level %d timed out."), + dp->host->hostname, qname, est(dp)->estimate[i].level); + } + est(dp)->estimate[i].level = -1; + } } - if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > 0) || - (est(dp)->level[1] != -1 && est(dp)->est_size[1] > 0) || - (est(dp)->level[2] != -1 && est(dp)->est_size[2] > 0)) { + if ((est(dp)->estimate[0].level != -1 && + est(dp)->estimate[0].nsize > (gint64)0) || + (est(dp)->estimate[1].level != -1 && + est(dp)->estimate[1].nsize > (gint64)0) || + (est(dp)->estimate[2].level != -1 && + est(dp)->estimate[2].nsize > (gint64)0)) { enqueue_disk(&estq, dp); } else { - est(dp)->errstr = vstralloc("disk ", dp->name, - ", all estimate timed out", NULL); + est(dp)->errstr = vstralloc("disk ", qname, + _(", all estimate timed out"), NULL); enqueue_disk(&failq, dp); } + amfree(qname); } } -static void getsize(hostp) -am_host_t *hostp; +static void getsize( + am_host_t *hostp) { - char number[NUM_STR_SIZE], *req; - disk_t *dp; - int i, estimates, timeout, req_len; - const security_driver_t *secdrv; - char *dumper; - char *calcsize; + char number[NUM_STR_SIZE], *req; + disk_t * dp; + int i; + time_t estimates, timeout; + size_t req_len; + const security_driver_t *secdrv; + char * calcsize; + char * qname, *b64disk = NULL; + char * qdevice, *b64device = NULL; + estimate_t estimate; + estimatelist_t el; assert(hostp->disks != NULL); @@ -1228,8 +1461,10 @@ am_host_t *hostp; fe_req_options_hostname); int has_maxdumps = am_has_feature(hostp->features, fe_req_options_maxdumps); + int has_config = am_has_feature(hostp->features, + fe_req_options_config); - snprintf(number, sizeof(number), "%d", hostp->maxdumps); + g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps); req = vstralloc("SERVICE ", "sendsize", "\n", "OPTIONS ", has_features ? "features=" : "", @@ -1241,213 +1476,285 @@ am_host_t *hostp; has_hostname ? "hostname=" : "", has_hostname ? hostp->hostname : "", has_hostname ? ";" : "", + has_config ? "config=" : "", + has_config ? get_config_name() : "", + has_config ? ";" : "", "\n", NULL); req_len = strlen(req); - req_len += 128; /* room for SECURITY ... */ + req_len += 128; /* room for SECURITY ... */ estimates = 0; for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { char *s = NULL; - int s_len = 0; + char *es; + size_t s_len = 0; + GPtrArray *errarray; if(dp->todo == 0) continue; if(est(dp)->state != DISK_READY) continue; est(dp)->got_estimate = 0; - if(est(dp)->level[0] == -1) { + if (est(dp)->estimate[0].level == -1) { est(dp)->state = DISK_DONE; continue; } - if(dp->estimate == ES_CLIENT || - dp->estimate == ES_CALCSIZE) { - nb_client++; - - for(i = 0; i < MAX_LEVELS; i++) { - char *l; - char *exclude1 = ""; - char *exclude2 = ""; - char *excludefree = NULL; - char spindle[NUM_STR_SIZE]; - char level[NUM_STR_SIZE]; - int lev = est(dp)->level[i]; - - if(lev == -1) break; + qname = quote_string(dp->name); - snprintf(level, sizeof(level), "%d", lev); - snprintf(spindle, sizeof(spindle), "%d", dp->spindle); - if(am_has_feature(hostp->features,fe_sendsize_req_options)){ - exclude1 = " OPTIONS |"; - exclude2 = optionstr(dp, hostp->features, NULL); - excludefree = exclude2; - } - else { - if(dp->exclude_file && - dp->exclude_file->nb_element == 1) { - exclude1 = " exclude-file="; - exclude2 = dp->exclude_file->first->name; - } - else if(dp->exclude_list && - dp->exclude_list->nb_element == 1) { - exclude1 = " exclude-list="; - exclude2 = dp->exclude_list->first->name; - } - } - - if(dp->estimate == ES_CALCSIZE && - !am_has_feature(hostp->features, fe_calcsize_estimate)) { - log_add(L_WARNING,"%s:%s does not support CALCSIZE for estimate, using CLIENT.\n", - hostp->hostname, dp->name); - dp->estimate = ES_CLIENT; - } - if(dp->estimate == ES_CLIENT) - calcsize = ""; - else - calcsize = "CALCSIZE "; - - if(strncmp(dp->program,"DUMP",4) == 0 || - strncmp(dp->program,"GNUTAR",6) == 0) { - dumper = ""; - } else { - dumper = "DUMPER "; - } - l = vstralloc(calcsize, - dumper, - dp->program, - " ", dp->name, - " ", dp->device ? dp->device : "", - " ", level, - " ", est(dp)->dumpdate[i], - " ", spindle, - " ", exclude1, exclude2, - "\n", - NULL); - strappend(s, l); - s_len += strlen(l); - amfree(l); - amfree(excludefree); + errarray = validate_optionstr(dp); + if (errarray->len > 0) { + guint i; + for (i=0; i < errarray->len; i++) { + log_add(L_FAIL, _("%s %s %s 0 [%s]"), + dp->host->hostname, qname, + planner_timestamp, + (char *)g_ptr_array_index(errarray, i)); } - /* - * Allow 2X for err response. - */ - if(req_len + s_len > MAX_PACKET / 2) { - amfree(s); + amfree(qname); + est(dp)->state = DISK_DONE; + continue; + } + + b64disk = amxml_format_tag("disk", dp->name); + qdevice = quote_string(dp->device); + estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data); + if (dp->device) + b64device = amxml_format_tag("diskdevice", dp->device); + + estimate = ES_CLIENT; + for (el = dp->estimatelist; el != NULL; el = el->next) { + estimate = (estimate_t)GPOINTER_TO_INT(el->data); + if (estimate == ES_SERVER) break; - } - if (s != NULL) { - estimates += i; - strappend(req, s); - req_len += s_len; - amfree(s); - } - est(dp)->state = DISK_ACTIVE; - remove_disk(&startq, dp); } - else if (dp->estimate == ES_SERVER) { + if (estimate == ES_SERVER) { info_t info; nb_server++; get_info(dp->host->hostname, dp->name, &info); for(i = 0; i < MAX_LEVELS; i++) { - int j; - int lev = est(dp)->level[i]; + int lev = est(dp)->estimate[i].level; if(lev == -1) break; - if(lev == 0) { /* use latest level 0, should do extrapolation */ - long est_size = 0; - int nb_est = 0; - - for(j=NB_HISTORY-2;j>=0;j--) { - if(info.history[j].level == 0) { - if(info.history[j].size < 0) continue; - est_size = info.history[j].size; - nb_est++; - } - } - if(nb_est > 0) { - est(dp)->est_size[i] = est_size; - } - else if(info.inf[lev].size > 1000) { /* stats */ - est(dp)->est_size[i] = info.inf[lev].size; - } - else { - est(dp)->est_size[i] = 1000000; + server_estimate(dp, i, &info, lev); + } + g_fprintf(stderr,_("%s time %s: got result for host %s disk %s:"), + get_pname(), walltime_str(curclock()), + dp->host->hostname, qname); + g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"), + est(dp)->estimate[0].level, + (long long)est(dp)->estimate[0].nsize, + est(dp)->estimate[1].level, + (long long)est(dp)->estimate[1].nsize, + est(dp)->estimate[2].level, + (long long)est(dp)->estimate[2].nsize); + if (!am_has_feature(hostp->features, fe_xml_estimate)) { + est(dp)->state = DISK_DONE; + remove_disk(&startq, dp); + enqueue_disk(&estq, dp); + } + } + + estimate = ES_SERVER; + for (el = dp->estimatelist; el != NULL; el = el->next) { + estimate = (estimate_t)GPOINTER_TO_INT(el->data); + if (estimate == ES_CLIENT || estimate == ES_CALCSIZE) + break; + } + if (estimate == ES_CLIENT || + estimate == ES_CALCSIZE || + (am_has_feature(hostp->features, fe_req_xml) && + am_has_feature(hostp->features, fe_xml_estimate))) { + nb_client++; + i = 0; + + if (am_has_feature(hostp->features, fe_req_xml)) { + char *levelstr = NULL; + char *spindlestr = NULL; + char level[NUM_STR_SIZE]; + char spindle[NUM_STR_SIZE]; + char *o; + char *l; + info_t info; + + get_info(dp->host->hostname, dp->name, &info); + for(i = 0; i < MAX_LEVELS; i++) { + char *server; + int lev = est(dp)->estimate[i].level; + if (lev == -1) break; + g_snprintf(level, SIZEOF(level), "%d", lev); + if (am_has_feature(hostp->features, fe_xml_level_server) && + server_can_do_estimate(dp, &info, lev)) { + server = "YES"; + } else { + server = ""; } + vstrextend(&levelstr, " ", + level, server, + "\n", NULL); } - else if(lev == est(dp)->last_level) { - /* means of all X day at the same level */ - #define NB_DAY 30 - int nb_day = 0; - long est_size_day[NB_DAY]; - int nb_est_day[NB_DAY]; - for(j=0;jspindle); + spindlestr = vstralloc(" ", + spindle, + "\n", NULL); + o = xml_optionstr(dp, 0); + + if (strcmp(dp->program,"DUMP") == 0 || + strcmp(dp->program,"GNUTAR") == 0) { + l = vstralloc("\n", + " ", + dp->program, + "\n", NULL); + } else { + l = vstralloc("\n", + " APPLICATION\n", + NULL); + if (dp->application) { + application_t *application; + char *xml_app; + + application = lookup_application(dp->application); + g_assert(application != NULL); + xml_app = xml_application(dp, application, + hostp->features); + vstrextend(&l, xml_app, NULL); + amfree(xml_app); } + } - for(j=NB_HISTORY-2;j>=0;j--) { - if(info.history[j].level <= 0) continue; - if(info.history[j].size < 0) continue; - if(info.history[j].level==info.history[j+1].level) { - if(nb_day estimatelist, hostp->features); + vstrextend(&l, es, "\n", NULL); + amfree(es); + vstrextend(&l, " ", b64disk, "\n", NULL); + if (dp->device) + vstrextend(&l, " ", b64device, "\n", NULL); + vstrextend(&l, levelstr, spindlestr, o, "\n", NULL); + strappend(s, l); + s_len += strlen(l); + amfree(l); + amfree(levelstr); + amfree(spindlestr); + amfree(o); + } else if (strcmp(dp->program,"DUMP") != 0 && + strcmp(dp->program,"GNUTAR") != 0) { + est(dp)->errstr = newvstrallocf(est(dp)->errstr, + _("does not support application-api")); + } else { + for(i = 0; i < MAX_LEVELS; i++) { + char *l; + char *exclude1 = ""; + char *exclude2 = ""; + char *excludefree = NULL; + char *include1 = ""; + char *include2 = ""; + char *includefree = NULL; + char spindle[NUM_STR_SIZE]; + char level[NUM_STR_SIZE]; + int lev = est(dp)->estimate[i].level; + + if(lev == -1) break; + + g_snprintf(level, SIZEOF(level), "%d", lev); + g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle); + if (am_has_feature(hostp->features, + fe_sendsize_req_options)){ + exclude1 = " OPTIONS |"; + exclude2 = optionstr(dp); + if ( exclude2 == NULL ) { + error(_("problem with option string, check the dumptype definition.\n")); + } + excludefree = exclude2; + includefree = NULL; + } else { + if (dp->exclude_file && + dp->exclude_file->nb_element == 1) { + exclude1 = " exclude-file="; + exclude2 = quote_string( + dp->exclude_file->first->name); + excludefree = exclude2; + } + else if (dp->exclude_list && + dp->exclude_list->nb_element == 1) { + exclude1 = " exclude-list="; + exclude2 = quote_string( + dp->exclude_list->first->name); + excludefree = exclude2; } - else { - nb_day=0; + if (dp->include_file && + dp->include_file->nb_element == 1) { + include1 = " include-file="; + include2 = quote_string( + dp->include_file->first->name); + includefree = include2; + } + else if (dp->include_list && + dp->include_list->nb_element == 1) { + include1 = " include-list="; + include2 = quote_string( + dp->include_list->first->name); + includefree = include2; } } - nb_day = info.consecutive_runs + 1; - if(nb_day > NB_DAY-1) nb_day = NB_DAY-1; - - while(nb_day > 0 && nb_est_day[nb_day] == 0) nb_day--; - if(nb_est_day[nb_day] > 0) { - est(dp)->est_size[i] = - est_size_day[nb_day] / nb_est_day[nb_day]; - } - else if(info.inf[lev].size > 1000) { /* stats */ - est(dp)->est_size[i] = info.inf[lev].size; - } - else { - est(dp)->est_size[i] = 10000; + if (estimate == ES_CALCSIZE && + !am_has_feature(hostp->features, + fe_calcsize_estimate)) { + log_add(L_WARNING, + _("%s:%s does not support CALCSIZE for estimate, using CLIENT.\n"), + hostp->hostname, qname); + estimate = ES_CLIENT; } + if(estimate == ES_CLIENT) + calcsize = ""; + else + calcsize = "CALCSIZE "; + + l = vstralloc(calcsize, + dp->program, + " ", qname, + " ", dp->device ? qdevice : "", + " ", level, + " ", est(dp)->estimate[i].dumpdate, + " ", spindle, + " ", exclude1, exclude2, + ((includefree != NULL) ? " " : ""), + include1, include2, + "\n", + NULL); + strappend(s, l); + s_len += strlen(l); + amfree(l); + amfree(includefree); + amfree(excludefree); } - else if(lev == est(dp)->last_level + 1) { - /* means of all first day at a new level */ - long est_size = 0; - int nb_est = 0; - - for(j=NB_HISTORY-2;j>=0;j--) { - if(info.history[j].level <= 0) continue; - if(info.history[j].size < 0) continue; - if(info.history[j].level == info.history[j+1].level + 1 ) { - est_size += info.history[j].size; - nb_est++; - } - } - if(nb_est > 0) { - est(dp)->est_size[i] = est_size / nb_est; - } - else if(info.inf[lev].size > 1000) { /* stats */ - est(dp)->est_size[i] = info.inf[lev].size; - } - else { - est(dp)->est_size[i] = 100000; - } + } + if (s != NULL) { + estimates += i; + strappend(req, s); + req_len += s_len; + amfree(s); + if (est(dp)->state == DISK_DONE) { + remove_disk(&estq, dp); + est(dp)->state = DISK_PARTIALY_DONE; + enqueue_disk(&pestq, dp); + } else { + remove_disk(&startq, dp); + est(dp)->state = DISK_ACTIVE; } - } - fprintf(stderr,"%s time %s: got result for host %s disk %s:", - get_pname(), walltime_str(curclock()), - dp->host->hostname, dp->name); - fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n", - est(dp)->level[0], est(dp)->est_size[0], - est(dp)->level[1], est(dp)->est_size[1], - est(dp)->level[2], est(dp)->est_size[2]); - est(dp)->state = DISK_DONE; - remove_disk(&startq, dp); - enqueue_disk(&estq, dp); + } else if (est(dp)->state != DISK_DONE) { + remove_disk(&startq, dp); + est(dp)->state = DISK_DONE; + if (est(dp)->errstr == NULL) { + est(dp)->errstr = vstrallocf( + _("Can't request estimate")); + } + enqueue_disk(&failq, dp); + } } + amfree(b64disk); + amfree(b64device); + amfree(qname); + amfree(qdevice); } if(estimates == 0) { @@ -1471,13 +1778,18 @@ am_host_t *hostp; * We use ctimeout for the "noop" request because it should be * very fast and etimeout has other side effects. */ - timeout = getconf_int(CNF_CTIMEOUT); + timeout = (time_t)getconf_int(CNF_CTIMEOUT); } - secdrv = security_getdriver(hostp->disks->security_driver); + dbprintf(_("send request:\n----\n%s\n----\n\n"), req); + secdrv = security_getdriver(hostp->disks->auth); if (secdrv == NULL) { - error("could not find security driver '%s' for host '%s'", - hostp->disks->security_driver, hostp->hostname); + hostp->up = HOST_DONE; + log_add(L_ERROR, + _("Could not find security driver '%s' for host '%s'"), + hostp->disks->auth, hostp->hostname); + amfree(req); + return; } hostp->up = HOST_ACTIVE; @@ -1491,14 +1803,15 @@ am_host_t *hostp; } } - protocol_sendreq(hostp->hostname, secdrv, generic_get_security_conf, + protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, req, timeout, handle_result, hostp); + amfree(req); } -static disk_t *lookup_hostdisk(hp, str) -am_host_t *hp; -char *str; +static disk_t *lookup_hostdisk( + /*@keep@*/ am_host_t *hp, + char *str) { disk_t *dp; @@ -1509,16 +1822,16 @@ char *str; } -static void handle_result(datap, pkt, sech) -void *datap; -pkt_t *pkt; -security_handle_t *sech; +static void handle_result( + void *datap, + pkt_t *pkt, + security_handle_t *sech) { int level, i; - long size; + gint64 size; disk_t *dp; am_host_t *hostp; - char *msgdisk=NULL, *msgdisk_undo=NULL, msgdisk_undo_ch = '\0'; + char *msg, msg_undo; char *remoterr, *errbuf = NULL; char *s; char *t; @@ -1526,21 +1839,26 @@ security_handle_t *sech; char *line; int ch; int tch; + char *qname; + char *disk; + long long size_; hostp = (am_host_t *)datap; hostp->up = HOST_READY; if (pkt == NULL) { - errbuf = vstralloc("Request to ", hostp->hostname, " failed: ", - security_geterror(sech), NULL); + if (strcmp(security_geterror(sech), "timeout waiting for REP") == 0) { + errbuf = vstrallocf("Some estimate timeout on %s, using server estimate if possible", hostp->hostname); + } else { + errbuf = vstrallocf(_("Request to %s failed: %s"), + hostp->hostname, security_geterror(sech)); + } goto error_return; } if (pkt->type == P_NAK) { -#define sc "ERROR " - if(strncmp(pkt->body, sc, sizeof(sc)-1) == 0) { - s = pkt->body + sizeof(sc)-1; + s = pkt->body; + if(strncmp_const_skip(s, "ERROR ", s, ch) == 0) { ch = *s++; -#undef sc } else { goto NAK_parse_failed; } @@ -1552,57 +1870,48 @@ security_handle_t *sech; *s = '\0'; } if (strcmp(remoterr, "unknown service: noop") != 0 - && strcmp(remoterr, "noop: invalid service") != 0) { + && strcmp(remoterr, "noop: invalid service") != 0) { errbuf = vstralloc(hostp->hostname, " NAK: ", remoterr, NULL); if(s) *s = '\n'; goto error_return; } } - msgdisk_undo = NULL; + dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body); s = pkt->body; ch = *s++; while(ch) { line = s - 1; - skip_line(s, ch); - if (s[-2] == '\n') { - s[-2] = '\0'; - } - -#define sc "OPTIONS " - if(strncmp(line, sc, sizeof(sc)-1) == 0) { -#undef sc -#define sc "features=" - t = strstr(line, sc); - if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) { - t += sizeof(sc)-1; -#undef sc + if(strncmp_const(line, "OPTIONS ") == 0) { + t = strstr(line, "features="); + if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) { + char *u = strchr(t, ';'); + if (u) + *u = '\0'; + t += SIZEOF("features=")-1; am_release_feature_set(hostp->features); if((hostp->features = am_string_to_feature(t)) == NULL) { - errbuf = vstralloc(hostp->hostname, - ": bad features value: ", - line, - "\n", - NULL); + errbuf = vstrallocf(hostp->hostname, + _(": bad features value: %s\n"), line); goto error_return; } + if (u) + *u = ';'; } - + skip_quoted_line(s, ch); continue; } -#define sc "ERROR " - if(strncmp(line, sc, sizeof(sc)-1) == 0) { - t = line + sizeof(sc)-1; - tch = t[-1]; -#undef sc - + t = line; + if ((strncmp_const_skip(t, "ERROR ", t, tch) == 0) || + (strncmp_const_skip(t, "WARNING ", t, tch) == 0)) { fp = t - 1; skip_whitespace(t, tch); if (tch == '\n') { t[-1] = '\0'; } + /* * If the "error" is that the "noop" service is unknown, it * just means the client is "old" (does not support the servie). @@ -1611,50 +1920,89 @@ security_handle_t *sech; if(hostp->features == NULL && pkt->type == P_NAK && (strcmp(t - 1, "unknown service: noop") == 0 - || strcmp(t - 1, "noop: invalid service") == 0)) { + || strcmp(t - 1, "noop: invalid service") == 0)) { + skip_quoted_line(s, ch); continue; - } else { - errbuf = vstralloc(hostp->hostname, - (pkt->type == P_NAK) ? "NAK " : "", - ": ", - fp, - NULL); - goto error_return; } + t = strchr(t,'\n'); + if (t) /* truncate after the first line */ + *t = '\0'; + errbuf = vstralloc(hostp->hostname, + (pkt->type == P_NAK) ? "NAK " : "", + ": ", + fp, + NULL); + goto error_return; } - msgdisk = t = line; - tch = *t++; - skip_non_whitespace(t, tch); - msgdisk_undo = t - 1; - msgdisk_undo_ch = *msgdisk_undo; - *msgdisk_undo = '\0'; + msg = t = line; + tch = *(t++); + skip_quoted_string(t, tch); + t[-1] = '\0'; + disk = unquote_string(msg); skip_whitespace(t, tch); - if (sscanf(t - 1, "%d SIZE %ld", &level, &size) != 2) { + + if (sscanf(t - 1, "%d", &level) != 1) { goto bad_msg; } - dp = lookup_hostdisk(hostp, msgdisk); - - *msgdisk_undo = msgdisk_undo_ch; /* for error message */ - msgdisk_undo = NULL; + skip_integer(t, tch); + skip_whitespace(t, tch); + dp = lookup_hostdisk(hostp, disk); if(dp == NULL) { - log_add(L_ERROR, "%s: invalid reply from sendsize: `%s'\n", + log_add(L_ERROR, _("%s: invalid reply from sendsize: `%s'\n"), hostp->hostname, line); + goto bad_msg; + } + + size = (gint64)-1; + if (strncmp_const(t-1,"SIZE ") == 0) { + if (sscanf(t - 1, "SIZE %lld", &size_) != 1) { + goto bad_msg; + } + size = (gint64)size_; + } else if ((strncmp_const(t-1,"ERROR ") == 0) || + (strncmp_const(t-1,"WARNING ") == 0)) { + skip_non_whitespace(t, tch); + skip_whitespace(t, tch); + msg = t-1; + skip_quoted_string(t,tch); + msg_undo = t[-1]; + t[-1] = '\0'; + if (pkt->type == P_REP && !est(dp)->errstr) { + est(dp)->errstr = unquote_string(msg); + } + t[-1] = msg_undo; } else { - for(i = 0; i < MAX_LEVELS; i++) { - if(est(dp)->level[i] == level) { - est(dp)->est_size[i] = size; - break; + goto bad_msg; + } + + amfree(disk); + + for (i = 0; i < MAX_LEVELS; i++) { + if (est(dp)->estimate[i].level == level) { + if (size == (gint64)-2) { + est(dp)->estimate[i].nsize = -1; /* remove estimate */ + est(dp)->estimate[i].guessed = 0; + } else if (size > (gint64)-1) { + /* take the size returned by the client */ + est(dp)->estimate[i].nsize = size; + est(dp)->estimate[i].guessed = 0; } + break; } - if(i == MAX_LEVELS) { - goto bad_msg; /* this est wasn't requested */ - } - est(dp)->got_estimate++; } + if (i == MAX_LEVELS && level > 0) { + /* client always report level 0 for some error */ + goto bad_msg; /* this est wasn't requested */ + } + est(dp)->got_estimate++; + + s = t; + ch = tch; + skip_quoted_line(s, ch); } if(hostp->up == HOST_READY && hostp->features == NULL) { @@ -1662,11 +2010,11 @@ security_handle_t *sech; * The client does not support the features list, so give it an * empty one. */ - dbprintf(("%s: no feature set from host %s\n", - debug_prefix_time(NULL), hostp->hostname)); + dbprintf(_("no feature set from host %s\n"), hostp->hostname); hostp->features = am_set_default_feature_set(); } + security_close_connection(sech, hostp->hostname); /* XXX what about disks that only got some estimates... do we care? */ /* XXX amanda 2.1 treated that case as a bad msg */ @@ -1690,112 +2038,126 @@ security_handle_t *sech; est(dp)->state = DISK_PARTIALY_DONE; } - if(est(dp)->level[0] == -1) continue; /* ignore this disk */ + if (est(dp)->estimate[0].level == -1) continue; /* ignore this disk */ + qname = quote_string(dp->name); if(pkt->type == P_PREP) { - fprintf(stderr,"%s: time %s: got partial result for host %s disk %s:", + g_fprintf(stderr,_("%s: time %s: got partial result for host %s disk %s:"), get_pname(), walltime_str(curclock()), - dp->host->hostname, dp->name); - fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n", - est(dp)->level[0], est(dp)->est_size[0], - est(dp)->level[1], est(dp)->est_size[1], - est(dp)->level[2], est(dp)->est_size[2]); + dp->host->hostname, qname); + g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"), + est(dp)->estimate[0].level, + (long long)est(dp)->estimate[0].nsize, + est(dp)->estimate[1].level, + (long long)est(dp)->estimate[1].nsize, + est(dp)->estimate[2].level, + (long long)est(dp)->estimate[2].nsize); enqueue_disk(&pestq, dp); } else if(pkt->type == P_REP) { - fprintf(stderr,"%s: time %s: got result for host %s disk %s:", + g_fprintf(stderr,_("%s: time %s: got result for host %s disk %s:"), get_pname(), walltime_str(curclock()), - dp->host->hostname, dp->name); - fprintf(stderr," %d -> %ldK, %d -> %ldK, %d -> %ldK\n", - est(dp)->level[0], est(dp)->est_size[0], - est(dp)->level[1], est(dp)->est_size[1], - est(dp)->level[2], est(dp)->est_size[2]); - if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > 0) || - (est(dp)->level[1] != -1 && est(dp)->est_size[1] > 0) || - (est(dp)->level[2] != -1 && est(dp)->est_size[2] > 0)) { - - if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < 0) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[2]); - est(dp)->level[2] = -1; - } - if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < 0) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[1]); - est(dp)->level[1] = -1; - } - if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < 0) { - log_add(L_WARNING, - "disk %s:%s, estimate of level %d failed.", - dp->host->hostname, dp->name, - est(dp)->level[0]); - est(dp)->level[0] = -1; + dp->host->hostname, qname); + g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"), + est(dp)->estimate[0].level, + (long long)est(dp)->estimate[0].nsize, + est(dp)->estimate[1].level, + (long long)est(dp)->estimate[1].nsize, + est(dp)->estimate[2].level, + (long long)est(dp)->estimate[2].nsize); + if ((est(dp)->estimate[0].level != -1 && + est(dp)->estimate[0].nsize > (gint64)0) || + (est(dp)->estimate[1].level != -1 && + est(dp)->estimate[1].nsize > (gint64)0) || + (est(dp)->estimate[2].level != -1 && + est(dp)->estimate[2].nsize > (gint64)0)) { + + for (i=MAX_LEVELS-1; i >=0; i--) { + if (est(dp)->estimate[i].level != -1 && + est(dp)->estimate[i].nsize < (gint64)0) { + est(dp)->estimate[i].level = -1; + } } enqueue_disk(&estq, dp); } else { enqueue_disk(&failq, dp); - if(est(dp)->got_estimate) { - est(dp)->errstr = vstralloc("disk ", dp->name, - ", all estimate failed", NULL); + if(est(dp)->got_estimate && !est(dp)->errstr) { + est(dp)->errstr = vstrallocf("disk %s, all estimate failed", + qname); } else { - fprintf(stderr, "error result for host %s disk %s: missing estimate\n", - dp->host->hostname, dp->name); - est(dp)->errstr = vstralloc("missing result for ", dp->name, - " in ", dp->host->hostname, - " response", - NULL); + g_fprintf(stderr, + _("error result for host %s disk %s: missing estimate\n"), + dp->host->hostname, qname); + if (est(dp)->errstr == NULL) { + est(dp)->errstr = vstrallocf(_("missing result for %s in %s response"), + qname, dp->host->hostname); + } } } + hostp->up = HOST_DONE; + } + if (est(dp)->post_dle == 0 && + (pkt->type == P_REP || + ((est(dp)->estimate[0].level == -1 || + est(dp)->estimate[0].nsize > (gint64)0) && + (est(dp)->estimate[1].level == -1 || + est(dp)->estimate[1].nsize > (gint64)0) && + (est(dp)->estimate[2].level == -1 || + est(dp)->estimate[2].nsize > (gint64)0)))) { + run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, + get_config_name(), dp, + est(dp)->estimate[0].level); + est(dp)->post_dle = 1; + } + amfree(qname); + } + + if(hostp->up == HOST_DONE) { + if (pkt->type == P_REP) { + run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, + get_config_name(), hostp); } } + getsize(hostp); + /* try to clean up any defunct processes, since Amanda doesn't wait() for + them explicitly */ + while(waitpid(-1, NULL, WNOHANG)> 0); return; NAK_parse_failed: - /* msgdisk_undo is always NULL */ - /* if(msgdisk_undo) { */ - /* *msgdisk_undo = msgdisk_undo_ch; */ - /* msgdisk_undo = NULL; */ - /* } */ - errbuf = stralloc2(hostp->hostname, " NAK: [NAK parse failed]"); - fprintf(stderr, "got strange nak from %s:\n----\n%s----\n\n", + errbuf = vstrallocf(_("%s NAK: [NAK parse failed]"), hostp->hostname); + g_fprintf(stderr, _("got strange nak from %s:\n----\n%s----\n\n"), hostp->hostname, pkt->body); goto error_return; bad_msg: - - if(msgdisk_undo) { - *msgdisk_undo = msgdisk_undo_ch; - msgdisk_undo = NULL; - } - fprintf(stderr,"got a bad message, stopped at:\n"); - fprintf(stderr,"----\n%s----\n\n", line); - errbuf = stralloc2("badly formatted response from ", hostp->hostname); - /* fall through to ... */ + g_fprintf(stderr,_("got a bad message, stopped at:\n")); + /*@ignore@*/ + g_fprintf(stderr,_("----\n%s----\n\n"), line); + errbuf = stralloc2(_("badly formatted response from "), hostp->hostname); + /*@end@*/ error_return: - i = 0; for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { - if(est(dp)->state != DISK_ACTIVE) continue; - est(dp)->state = DISK_DONE; - if(est(dp)->state == DISK_ACTIVE) { - est(dp)->state = DISK_DONE; - remove_disk(&waitq, dp); - enqueue_disk(&failq, dp); - i++; + if (dp->todo) { + if(est(dp)->state == DISK_ACTIVE) { + qname = quote_string(dp->name); + est(dp)->state = DISK_DONE; + remove_disk(&waitq, dp); + enqueue_disk(&failq, dp); + i++; - est(dp)->errstr = stralloc(errbuf); - fprintf(stderr, "error result for host %s disk %s: %s\n", - dp->host->hostname, dp->name, errbuf); + est(dp)->errstr = stralloc(errbuf); + g_fprintf(stderr, _("error result for host %s disk %s: %s\n"), + dp->host->hostname, qname, errbuf); + amfree(qname); + } } } if(i == 0) { @@ -1804,9 +2166,25 @@ security_handle_t *sech; * reported. */ log_add(L_ERROR, "%s", errbuf); + for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) { + if (dp->todo) { + qname = quote_string(dp->name); + est(dp)->state = DISK_DONE; + remove_disk(&waitq, dp); + enqueue_disk(&failq, dp); + + est(dp)->errstr = g_strdup(errbuf); + g_fprintf(stderr, _("error result for host %s disk %s: %s\n"), + dp->host->hostname, qname, errbuf); + amfree(qname); + } + } } hostp->up = HOST_DONE; amfree(errbuf); + /* try to clean up any defunct processes, since Amanda doesn't wait() for + them explicitly */ + while(waitpid(-1, NULL, WNOHANG)> 0); } @@ -1818,235 +2196,271 @@ security_handle_t *sech; * */ -static int schedule_order P((disk_t *a, disk_t *b)); /* subroutines */ -static int pick_inclevel P((disk_t *dp)); +static int schedule_order(disk_t *a, disk_t *b); /* subroutines */ +static one_est_t *pick_inclevel(disk_t *dp); -static void analyze_estimate(dp) -disk_t *dp; +static void analyze_estimate( + disk_t *dp) { est_t *ep; info_t info; int have_info = 0; + char *qname = quote_string(dp->name); ep = est(dp); - fprintf(stderr, "pondering %s:%s... ", - dp->host->hostname, dp->name); - fprintf(stderr, "next_level0 %d last_level %d ", + g_fprintf(stderr, _("pondering %s:%s... "), + dp->host->hostname, qname); + g_fprintf(stderr, _("next_level0 %d last_level %d "), ep->next_level0, ep->last_level); if(get_info(dp->host->hostname, dp->name, &info) == 0) { have_info = 1; } - ep->degr_level = -1; - ep->degr_size = -1; + ep->degr_est = &default_one_est; - if(ep->next_level0 <= 0 - || (have_info && ep->last_level == 0 && (info.command & FORCE_NO_BUMP))) { - if(ep->next_level0 <= 0) { - fprintf(stderr,"(due for level 0) "); - } - ep->dump_level = 0; - ep->dump_size = est_tape_size(dp, 0); - if(ep->dump_size <= 0) { - fprintf(stderr, - "(no estimate for level 0, picking an incr level)\n"); - ep->dump_level = pick_inclevel(dp); - ep->dump_size = est_tape_size(dp, ep->dump_level); - - if(ep->dump_size == -1) { - ep->dump_level = ep->dump_level + 1; - ep->dump_size = est_tape_size(dp, ep->dump_level); + if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0 + && (ISSET(info.command, FORCE_NO_BUMP)))) { + if (ep->next_level0 <= 0) { + g_fprintf(stderr,_("(due for level 0) ")); + } + ep->dump_est = est_for_level(dp, 0); + if (ep->dump_est->csize <= (gint64)0) { + g_fprintf(stderr, + _("(no estimate for level 0, picking an incr level)\n")); + ep->dump_est = pick_inclevel(dp); + + if (ep->dump_est->nsize == (gint64)-1) { + ep->dump_est = est_for_level(dp, ep->dump_est->level + 1); } } else { - total_lev0 += (double) ep->dump_size; + total_lev0 += (double) ep->dump_est->csize; if(ep->last_level == -1 || dp->skip_incr) { - fprintf(stderr,"(%s disk, can't switch to degraded mode)\n", - dp->skip_incr? "skip-incr":"new"); - ep->degr_level = -1; - ep->degr_size = -1; + g_fprintf(stderr,_("(%s disk, can't switch to degraded mode)\n"), + dp->skip_incr? "skip-incr":_("new")); + if (dp->skip_incr && ep->degr_mesg == NULL) { + ep->degr_mesg = _("Skpping: skip-incr disk can't be dumped in degraded mode"); + } + ep->degr_est = &default_one_est; } else { /* fill in degraded mode info */ - fprintf(stderr,"(picking inclevel for degraded mode)"); - ep->degr_level = pick_inclevel(dp); - ep->degr_size = est_tape_size(dp, ep->degr_level); - if(ep->degr_size == -1) { - ep->degr_level = ep->degr_level + 1; - ep->degr_size = est_tape_size(dp, ep->degr_level); + g_fprintf(stderr,_("(picking inclevel for degraded mode)")); + ep->degr_est = pick_inclevel(dp); + if (ep->degr_est->level >= 0 && + ep->degr_est->csize == (gint64)-1) { + ep->degr_est = est_for_level(dp, ep->degr_est->level + 1); } - if(ep->degr_size == -1) { - fprintf(stderr,"(no inc estimate)"); - ep->degr_level = -1; + if (ep->degr_est->csize == (gint64)-1) { + g_fprintf(stderr,_("(no inc estimate)")); + if (ep->degr_mesg == NULL) + ep->degr_mesg = _("Skipping: an incremental estimate could not be performed, so disk cannot be dumped in degraded mode"); + ep->degr_est = &default_one_est; } - fprintf(stderr,"\n"); + g_fprintf(stderr,"\n"); } } } else { - fprintf(stderr,"(not due for a full dump, picking an incr level)\n"); + g_fprintf(stderr,_("(not due for a full dump, picking an incr level)\n")); /* XXX - if this returns -1 may be we should force a total? */ - ep->dump_level = pick_inclevel(dp); - ep->dump_size = est_tape_size(dp, ep->dump_level); + ep->dump_est = pick_inclevel(dp); - if(ep->dump_size == -1) { - ep->dump_level = ep->last_level; - ep->dump_size = est_tape_size(dp, ep->dump_level); + if (ep->dump_est->csize == (gint64)-1) { + ep->dump_est = est_for_level(dp, ep->last_level); + } + if (ep->dump_est->csize == (gint64)-1) { + ep->dump_est = est_for_level(dp, ep->last_level + 1); } - if(ep->dump_size == -1) { - ep->dump_level = ep->last_level + 1; - ep->dump_size = est_tape_size(dp, ep->dump_level); + if (ep->dump_est->csize == (gint64)-1) { + ep->dump_est = est_for_level(dp, 0); } - if(ep->dump_size == -1) { - ep->dump_level = 0; - ep->dump_size = est_tape_size(dp, ep->dump_level); + if (ep->degr_mesg == NULL) { + ep->degr_mesg = _("Skipping: a full is not planned, so can't dump in degraded mode"); } } - fprintf(stderr," curr level %d size %ld ", ep->dump_level, ep->dump_size); + if (ep->dump_est->level < 0) { + int i; + char *q = quote_string("no estimate"); + + g_fprintf(stderr,_(" no valid estimate\n")); + for(i=0; iestimate[i].level >= 0) { + g_fprintf(stderr,(" level: %d nsize: %lld csize: %lld\n"), + est(dp)->estimate[i].level, + (long long)est(dp)->estimate[i].nsize, + (long long)est(dp)->estimate[i].csize); + } + } + log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname, + planner_timestamp, q); + amfree(q); + } + + g_fprintf(stderr,_(" curr level %d nsize %lld csize %lld "), + ep->dump_est->level, (long long)ep->dump_est->nsize, + (long long)ep->dump_est->csize); insert_disk(&schedq, dp, schedule_order); - total_size += tt_blocksize_kb + ep->dump_size + tape_mark; + total_size += (gint64)tt_blocksize_kb + ep->dump_est->csize + tape_mark; /* update the balanced size */ if(!(dp->skip_full || dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY)) { - long lev0size; + gint64 lev0size; lev0size = est_tape_size(dp, 0); - if(lev0size == -1) lev0size = ep->last_lev0size; + if(lev0size == (gint64)-1) lev0size = ep->last_lev0size; + + if (dp->strategy == DS_NOINC) { + balanced_size += (double)lev0size; + } else if (dp->dumpcycle == 0) { + balanced_size += (double)(lev0size * conf_dumpcycle / (gint64)runs_per_cycle); + } else if (dp->dumpcycle != conf_dumpcycle) { + balanced_size += (double)(lev0size * (conf_dumpcycle / dp->dumpcycle) / (gint64)runs_per_cycle); + } else { + balanced_size += (double)(lev0size / (gint64)runs_per_cycle); + } + } + + g_fprintf(stderr,_("total size %lld total_lev0 %1.0lf balanced-lev0size %1.0lf\n"), + (long long)total_size, total_lev0, balanced_size); - balanced_size += lev0size / runs_per_cycle; + /* Log errstr even if the estimate succeeded */ + /* It can be an error from a script */ + if (est(dp)->errstr) { + char *qerrstr = quote_string(est(dp)->errstr); + /* Log only a warning if a server estimate is available */ + if (est(dp)->estimate[0].nsize > 0 || + est(dp)->estimate[1].nsize > 0 || + est(dp)->estimate[2].nsize > 0) { + log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname, + planner_timestamp, qerrstr); + } else { + log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, + planner_timestamp, qerrstr); + } + amfree(qerrstr); } - fprintf(stderr,"total size " AM64_FMT " total_lev0 %1.0f balanced-lev0size %1.0f\n", - total_size, total_lev0, balanced_size); + amfree(qname); } -static void handle_failed(dp) -disk_t *dp; +static void handle_failed( + disk_t *dp) { - char *errstr; - -/* - * From George Scott : - * -------- - * If a machine is down when the planner is run it guesses from historical - * data what the size of tonights dump is likely to be and schedules a - * dump anyway. The dumper then usually discovers that that machine is - * still down and ends up with a half full tape. Unfortunately the - * planner had to delay another dump because it thought that the tape was - * full. The fix here is for the planner to ignore unavailable machines - * rather than ignore the fact that they are unavailable. - * -------- - */ + char *errstr, *errstr1, *qerrstr; + char *qname = quote_string(dp->name); -#ifdef old_behavior - if(est(dp)->last_level != -1) { - log_add(L_WARNING, - "Could not get estimate for %s:%s, using historical data.", - dp->host->hostname, dp->name); - analyze_estimate(dp); - return; - } -#endif + errstr = est(dp)->errstr? est(dp)->errstr : _("hmm, no error indicator!"); + errstr1 = vstralloc("[",errstr,"]", NULL); + qerrstr = quote_string(errstr1); + amfree(errstr1); - errstr = est(dp)->errstr? est(dp)->errstr : "hmm, no error indicator!"; + g_fprintf(stderr, _("%s: FAILED %s %s %s 0 %s\n"), + get_pname(), dp->host->hostname, qname, planner_timestamp, qerrstr); - fprintf(stderr, "%s: FAILED %s %s %s 0 [%s]\n", - get_pname(), dp->host->hostname, dp->name, datestamp, errstr); - - log_add(L_FAIL, "%s %s %s 0 [%s]", dp->host->hostname, dp->name, - datestamp, errstr); + log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, + planner_timestamp, qerrstr); + amfree(qerrstr); + amfree(qname); /* XXX - memory leak with *dp */ } -static int schedule_order(a, b) -disk_t *a, *b; /* * insert-sort by decreasing priority, then * by decreasing size within priority levels. */ + +static int schedule_order( + disk_t *a, + disk_t *b) { int diff; - long ldiff; + gint64 ldiff; diff = est(b)->dump_priority - est(a)->dump_priority; if(diff != 0) return diff; - ldiff = est(b)->dump_size - est(a)->dump_size; - if(ldiff < 0) return -1; /* XXX - there has to be a better way to dothis */ - if(ldiff > 0) return 1; + ldiff = est(b)->dump_est->csize - est(a)->dump_est->csize; + if(ldiff < (gint64)0) return -1; /* XXX - there has to be a better way to dothis */ + if(ldiff > (gint64)0) return 1; return 0; } -static int pick_inclevel(dp) -disk_t *dp; +static one_est_t *pick_inclevel( + disk_t *dp) { - int base_level, bump_level; - long base_size, bump_size; - long thresh; + one_est_t *level0_est, *base_est, *bump_est; + gint64 thresh; + char *qname; - base_level = est(dp)->last_level; + level0_est = est_for_level(dp, 0); + base_est = est_for_level(dp, est(dp)->last_level); /* if last night was level 0, do level 1 tonight, no ifs or buts */ - if(base_level == 0) { - fprintf(stderr," picklev: last night 0, so tonight level 1\n"); - return 1; + if (base_est->level == 0) { + g_fprintf(stderr,_(" picklev: last night 0, so tonight level 1\n")); + return est_for_level(dp, 1); } /* if no-full option set, always do level 1 */ if(dp->strategy == DS_NOFULL) { - fprintf(stderr," picklev: no-full set, so always level 1\n"); - return 1; + g_fprintf(stderr,_(" picklev: no-full set, so always level 1\n")); + return est_for_level(dp, 1); } - base_size = est_size(dp, base_level); - /* if we didn't get an estimate, we can't do an inc */ - if(base_size == -1) { - base_size = est_size(dp, base_level+1); - if(base_size > 0) /* FORCE_BUMP */ - return base_level+1; - fprintf(stderr," picklev: no estimate for level %d, so no incs\n", base_level); - return base_level; + if (base_est->nsize == (gint64)-1) { + bump_est = est_for_level(dp, est(dp)->last_level + 1); + if (bump_est->nsize > (gint64)0) { /* FORCE_BUMP */ + g_fprintf(stderr,_(" picklev: bumping to level %d\n"), bump_est->level); + return bump_est; + } + g_fprintf(stderr,_(" picklev: no estimate for level %d, so no incs\n"), base_est->level); + return base_est; } - thresh = bump_thresh(base_level, est_size(dp, 0), dp->bumppercent, dp->bumpsize, dp->bumpmult); + thresh = bump_thresh(base_est->level, level0_est->nsize, dp->bumppercent, + dp->bumpsize, dp->bumpmult); - fprintf(stderr, - " pick: size %ld level %d days %d (thresh %ldK, %d days)\n", - base_size, base_level, est(dp)->level_days, - thresh, dp->bumpdays); + g_fprintf(stderr, + _(" pick: size %lld level %d days %d (thresh %lldK, %d days)\n"), + (long long)base_est->nsize, base_est->level, est(dp)->level_days, + (long long)thresh, dp->bumpdays); - if(base_level == 9 + if(base_est->level == (DUMP_LEVELS - 1) || est(dp)->level_days < dp->bumpdays - || base_size <= thresh) - return base_level; + || base_est->nsize <= thresh) + return base_est; - bump_level = base_level + 1; - bump_size = est_size(dp, bump_level); + bump_est = est_for_level(dp, base_est->level + 1); - if(bump_size == -1) return base_level; + if (bump_est->nsize == (gint64)-1) + return base_est; - fprintf(stderr, " pick: next size %ld... ", bump_size); + g_fprintf(stderr, _(" pick: next size %lld... "), + (long long)bump_est->nsize); - if(base_size - bump_size < thresh) { - fprintf(stderr, "not bumped\n"); - return base_level; + if (base_est->nsize - bump_est->nsize < thresh) { + g_fprintf(stderr, _("not bumped\n")); + return base_est; } - fprintf(stderr, "BUMPED\n"); - log_add(L_INFO, "Incremental of %s:%s bumped to level %d.", - dp->host->hostname, dp->name, bump_level); + qname = quote_string(dp->name); + g_fprintf(stderr, _("BUMPED\n")); + log_add(L_INFO, _("Incremental of %s:%s bumped to level %d."), + dp->host->hostname, qname, bump_est->level); + amfree(qname); - return bump_level; + return bump_est; } @@ -2076,19 +2490,28 @@ disk_t *dp; ** more balanced cycle. */ -static void delay_one_dump P((disk_t *dp, int delete, ...)); +static void delay_one_dump(disk_t *dp, int delete, ...); +static int promote_highest_priority_incremental(void); +static int promote_hills(void); -static void delay_dumps P((void)) /* delay any dumps that will not fit */ +static void delay_dumps(void) { - disk_t *dp, *ndp, *preserve; - bi_t *bi, *nbi; - am64_t new_total; /* New total_size */ - char est_kb[20]; /* Text formatted dump size */ - int nb_forced_level_0; - info_t info; - int delete; - char *message; + disk_t * dp; + disk_t * ndp; + disk_t * preserve; + disk_t * delayed_dp; + bi_t * bi; + bi_t * nbi; + gint64 new_total; /* New total_size */ + char est_kb[20]; /* Text formatted dump size */ + int nb_forced_level_0; + info_t info; + int delete; + char * message; + gint64 full_size; + time_t timestamps; + int priority; biq.head = biq.tail = NULL; @@ -2105,47 +2528,64 @@ static void delay_dumps P((void)) for(dp = schedq.head; dp != NULL; dp = ndp) { int avail_tapes = 1; - if (dp->tape_splitsize > 0) + if (dp->splitsize > (gint64)0 || dp->allow_split) avail_tapes = conf_runtapes; ndp = dp->next; /* remove_disk zaps this */ - if (est(dp)->dump_size == -1 || - est(dp)->dump_size <= tape->length * avail_tapes) { + full_size = est_tape_size(dp, 0); + if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) { + char *qname = quote_string(dp->name); + if (conf_runtapes > 1 && dp->splitsize == (gint64)0) { + log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space" + ", you could define a splitsize"), + dp->host->hostname, qname, + (long long)full_size); + } else { + log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"), + dp->host->hostname, qname, + (long long)full_size); + } + amfree(qname); + } + + if (est(dp)->dump_est->csize == (gint64)-1 || + est(dp)->dump_est->csize <= tapetype_get_length(tape) * (gint64)avail_tapes) { continue; } - /* Format dumpsize for messages */ - snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size); + /* Format dumpsize for messages */ + g_snprintf(est_kb, 20, "%lld KB,", + (long long)est(dp)->dump_est->csize); - if(est(dp)->dump_level == 0) { + if(est(dp)->dump_est->level == 0) { if(dp->skip_incr) { delete = 1; - message = "but cannot incremental dump skip-incr disk"; + message = _("but cannot incremental dump skip-incr disk"); } else if(est(dp)->last_level < 0) { delete = 1; - message = "but cannot incremental dump new disk"; + message = _("but cannot incremental dump new disk"); } - else if(est(dp)->degr_level < 0) { + else if(est(dp)->degr_est->level < 0) { delete = 1; - message = "but no incremental estimate"; + message = _("but no incremental estimate"); } - else if (est(dp)->degr_size > tape->length) { + else if (est(dp)->degr_est->csize > tapetype_get_length(tape)) { delete = 1; - message = "incremental dump also larger than tape"; + message = _("incremental dump also larger than tape"); } else { delete = 0; - message = "full dump delayed"; + message = _("full dump delayed, doing incremental"); } } else { delete = 1; - message = "skipping incremental"; + message = _("skipping incremental"); } - delay_one_dump(dp, delete, "dump larger than available tape space,", est_kb, - message, NULL); + delay_one_dump(dp, delete, _("dump larger than available tape space,"), + est_kb, message, NULL); } /* @@ -2163,50 +2603,71 @@ static void delay_dumps P((void)) nb_forced_level_0 = 0; preserve = NULL; - for(dp = schedq.head; dp != NULL && preserve == NULL; dp = dp->next) - if(est(dp)->dump_level == 0) - preserve = dp; + timestamps = 2147483647; + priority = 0; + for(dp = schedq.head; dp != NULL; dp = dp->next) { + if (est(dp)->dump_est->level == 0) { + if (!preserve || + est(dp)->dump_priority > priority || + (est(dp)->dump_priority == priority && + est(dp)->info->inf[0].date < timestamps)) { + priority = est(dp)->dump_priority; + timestamps = est(dp)->info->inf[0].date; + preserve = dp; + } + } + } /* 2.a. Do not delay forced full */ - for(dp = schedq.tail; + delayed_dp = NULL; + do { + delayed_dp = 0; + timestamps = 0; + for(dp = schedq.tail; dp != NULL && total_size > tape_length; dp = ndp) { - ndp = dp->prev; - - if(est(dp)->dump_level != 0) continue; + ndp = dp->prev; - get_info(dp->host->hostname, dp->name, &info); - if(info.command & FORCE_FULL) { - nb_forced_level_0 += 1; - preserve = dp; - continue; - } + if(est(dp)->dump_est->level != 0) continue; - if(dp != preserve) { + get_info(dp->host->hostname, dp->name, &info); + if(ISSET(info.command, FORCE_FULL)) { + nb_forced_level_0 += 1; + preserve = dp; + continue; + } - /* Format dumpsize for messages */ - snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size); + if (dp != preserve && + est(dp)->info->inf[0].date > timestamps) { + delayed_dp = dp; + timestamps = est(dp)->info->inf[0].date; + } + } + if (delayed_dp) { + /* Format dumpsize for messages */ + g_snprintf(est_kb, 20, "%lld KB,", + (long long)est(delayed_dp)->dump_est->csize); - if(dp->skip_incr) { + if(delayed_dp->skip_incr) { delete = 1; - message = "but cannot incremental dump skip-incr disk"; + message = _("but cannot incremental dump skip-incr disk"); } - else if(est(dp)->last_level < 0) { + else if(est(delayed_dp)->last_level < 0) { delete = 1; - message = "but cannot incremental dump new disk"; + message = _("but cannot incremental dump new disk"); } - else if(est(dp)->degr_level < 0) { + else if(est(delayed_dp)->degr_est->level < 0) { delete = 1; - message = "but no incremental estimate"; + message = _("but no incremental estimate"); } else { delete = 0; - message = "full dump delayed"; + message = _("full dump delayed, doing incremental"); } - delay_one_dump(dp, delete, "dumps too big,", est_kb, + delay_one_dump(delayed_dp, delete, _("dumps too big,"), est_kb, message, NULL); } - } + } while (delayed_dp); /* 2.b. Delay forced full if needed */ if(nb_forced_level_0 > 0 && total_size > tape_length) { @@ -2215,28 +2676,29 @@ static void delay_dumps P((void)) dp = ndp) { ndp = dp->prev; - if(est(dp)->dump_level == 0 && dp != preserve) { + if(est(dp)->dump_est->level == 0 && dp != preserve) { /* Format dumpsize for messages */ - snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size); + g_snprintf(est_kb, 20, "%lld KB,", + (long long)est(dp)->dump_est->csize); if(dp->skip_incr) { delete = 1; - message = "but cannot incremental dump skip-incr disk"; + message = _("but cannot incremental dump skip-incr disk"); } else if(est(dp)->last_level < 0) { delete = 1; - message = "but cannot incremental dump new disk"; + message = _("but cannot incremental dump new disk"); } - else if(est(dp)->degr_level < 0) { + else if(est(dp)->degr_est->level < 0) { delete = 1; - message = "but no incremental estimate"; + message = _("but no incremental estimate"); } else { delete = 0; - message = "full dump delayed"; + message = _("full dump delayed"); } - delay_one_dump(dp, delete, "dumps too big,", est_kb, + delay_one_dump(dp, delete, _("dumps too big,"), est_kb, message, NULL); } } @@ -2255,15 +2717,16 @@ static void delay_dumps P((void)) dp = ndp) { ndp = dp->prev; - if(est(dp)->dump_level != 0) { + if(est(dp)->dump_est->level != 0) { - /* Format dumpsize for messages */ - snprintf(est_kb, 20, "%ld KB,", est(dp)->dump_size); + /* Format dumpsize for messages */ + g_snprintf(est_kb, 20, "%lld KB,", + (long long)est(dp)->dump_est->csize); delay_one_dump(dp, 1, - "dumps way too big,", + _("dumps way too big,"), est_kb, - "must skip incremental dumps", + _("must skip incremental dumps"), NULL); } } @@ -2277,29 +2740,31 @@ static void delay_dumps P((void)) ** in but why complicate the code? */ - for(bi = biq.tail; bi != NULL; bi = nbi) { +/*@i@*/ for(bi = biq.tail; bi != NULL; bi = nbi) { int avail_tapes = 1; nbi = bi->prev; dp = bi->dp; - if(dp->tape_splitsize > 0) avail_tapes = conf_runtapes; - - if(bi->deleted) - new_total = total_size + tt_blocksize_kb + bi->size + tape_mark; - else - new_total = total_size - est(dp)->dump_size + bi->size; + if(dp->splitsize > (gint64)0) + avail_tapes = conf_runtapes; - if(new_total <= tape_length && bi->size < tape->length * avail_tapes) { + if(bi->deleted) { + new_total = total_size + (gint64)tt_blocksize_kb + + bi->csize + (gint64)tape_mark; + } else { + new_total = total_size - est(dp)->dump_est->csize + bi->csize; + } + if((new_total <= tape_length) && + (bi->csize < (tapetype_get_length(tape) * (gint64)avail_tapes))) { /* reinstate it */ total_size = new_total; if(bi->deleted) { if(bi->level == 0) { - total_lev0 += (double) bi->size; + total_lev0 += (double) bi->csize; } insert_disk(&schedq, dp, schedule_order); } else { - est(dp)->dump_level = bi->level; - est(dp)->dump_size = bi->size; + est(dp)->dump_est = est_for_level(dp, bi->level); } /* Keep it clean */ @@ -2311,6 +2776,7 @@ static void delay_dumps P((void)) biq.head = bi->next; else (bi->prev)->next = bi->next; + amfree(bi->errstr); amfree(bi); } @@ -2324,26 +2790,26 @@ static void delay_dumps P((void)) ** now. */ - for(bi = biq.head; bi != NULL; bi = nbi) { +/*@i@*/ for(bi = biq.head; bi != NULL; bi = nbi) { nbi = bi->next; - if(bi->deleted) { - fprintf(stderr, "%s: FAILED %s\n", get_pname(), bi->errstr); + g_fprintf(stderr, "%s: FAILED %s\n", get_pname(), bi->errstr); log_add(L_FAIL, "%s", bi->errstr); } else { dp = bi->dp; - fprintf(stderr, " delay: %s now at level %d\n", - bi->errstr, est(dp)->dump_level); + g_fprintf(stderr, _(" delay: %s now at level %d\n"), + bi->errstr, est(dp)->dump_est->level); log_add(L_INFO, "%s", bi->errstr); } - - /* Clean up - dont be too fancy! */ + /*@ignore@*/ amfree(bi->errstr); amfree(bi); + /*@end@*/ } - fprintf(stderr, " delay: Total size now " AM64_FMT ".\n", total_size); + g_fprintf(stderr, _(" delay: Total size now %lld.\n"), + (long long)total_size); return; } @@ -2353,24 +2819,27 @@ static void delay_dumps P((void)) * Remove a dump or modify it from full to incremental. * Keep track of it on the bi q in case we can add it back later. */ -arglist_function1(static void delay_one_dump, - disk_t *, dp, - int, delete) +arglist_function1( + static void delay_one_dump, + disk_t *, dp, + int, delete) { bi_t *bi; va_list argp; char level_str[NUM_STR_SIZE]; char *sep; char *next; + char *qname = quote_string(dp->name); + char *errstr, *qerrstr; arglist_start(argp, delete); - total_size -= tt_blocksize_kb + est(dp)->dump_size + tape_mark; - if(est(dp)->dump_level == 0) { - total_lev0 -= (double) est(dp)->dump_size; + total_size -= (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark; + if(est(dp)->dump_est->level == 0) { + total_lev0 -= (double) est(dp)->dump_est->csize; } - bi = alloc(sizeof(bi_t)); + bi = alloc(SIZEOF(bi_t)); bi->next = NULL; bi->prev = biq.tail; if(biq.tail == NULL) @@ -2381,42 +2850,48 @@ arglist_function1(static void delay_one_dump, bi->deleted = delete; bi->dp = dp; - bi->level = est(dp)->dump_level; - bi->size = est(dp)->dump_size; + bi->level = est(dp)->dump_est->level; + bi->nsize = est(dp)->dump_est->nsize; + bi->csize = est(dp)->dump_est->csize; - snprintf(level_str, sizeof(level_str), "%d", est(dp)->dump_level); + g_snprintf(level_str, SIZEOF(level_str), "%d", est(dp)->dump_est->level); bi->errstr = vstralloc(dp->host->hostname, - " ", dp->name, - " ", datestamp ? datestamp : "?", + " ", qname, + " ", planner_timestamp ? planner_timestamp : "?", " ", level_str, NULL); - sep = " ["; + errstr = NULL; + sep = "["; while ((next = arglist_val(argp, char *)) != NULL) { - bi->errstr = newvstralloc(bi->errstr, bi->errstr, sep, next, NULL); + vstrextend(&errstr, sep, next, NULL); sep = " "; } - strappend(bi->errstr, "]"); + strappend(errstr, "]"); + qerrstr = quote_string(errstr); + vstrextend(&bi->errstr, " ", qerrstr, NULL); + amfree(errstr); + amfree(qerrstr); arglist_end(argp); if (delete) { remove_disk(&schedq, dp); } else { - est(dp)->dump_level = est(dp)->degr_level; - est(dp)->dump_size = est(dp)->degr_size; - total_size += tt_blocksize_kb + est(dp)->dump_size + tape_mark; + est(dp)->dump_est = est(dp)->degr_est; + total_size += (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark; } - + amfree(qname); return; } -static int promote_highest_priority_incremental P((void)) +static int promote_highest_priority_incremental(void) { disk_t *dp, *dp1, *dp_promote; - long new_size, new_total, new_lev0; + gint64 new_total, new_lev0; int check_days; int nb_today, nb_same_day, nb_today2; int nb_disk_today, nb_disk_same_day; + char *qname; /* * return 1 if did so; must update total_size correctly; must not @@ -2425,10 +2900,10 @@ static int promote_highest_priority_incremental P((void)) dp_promote = NULL; for(dp = schedq.head; dp != NULL; dp = dp->next) { - + one_est_t *level0_est = est_for_level(dp, 0); est(dp)->promote = -1000; - if(est_size(dp,0) <= 0) + if (level0_est->nsize <= (gint64)0) continue; if(est(dp)->next_level0 <= 0) @@ -2437,21 +2912,20 @@ static int promote_highest_priority_incremental P((void)) if(est(dp)->next_level0 > dp->maxpromoteday) continue; - new_size = est_tape_size(dp, 0); - new_total = total_size - est(dp)->dump_size + new_size; - new_lev0 = total_lev0 + new_size; + new_total = total_size - est(dp)->dump_est->csize + level0_est->csize; + new_lev0 = (gint64)total_lev0 + level0_est->csize; nb_today = 0; nb_same_day = 0; nb_disk_today = 0; nb_disk_same_day = 0; - for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) { - if(est(dp1)->dump_level == 0) + for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) { + if(est(dp1)->dump_est->level == 0) nb_disk_today++; else if(est(dp1)->next_level0 == est(dp)->next_level0) nb_disk_same_day++; if(strcmp(dp->host->hostname, dp1->host->hostname) == 0) { - if(est(dp1)->dump_level == 0) + if(est(dp1)->dump_est->level == 0) nb_today++; else if(est(dp1)->next_level0 == est(dp)->next_level0) nb_same_day++; @@ -2459,18 +2933,22 @@ static int promote_highest_priority_incremental P((void)) } /* do not promote if overflow tape */ - if(new_total > tape_length) continue; + if(new_total > tape_length) + continue; /* do not promote if overflow balanced size and something today */ /* promote if nothing today */ - if(new_lev0 > balanced_size+balance_threshold && nb_disk_today > 0) + if((new_lev0 > (gint64)(balanced_size + balance_threshold)) && + (nb_disk_today > 0)) continue; /* do not promote if only one disk due that day and nothing today */ - if(nb_disk_same_day == 1 && nb_disk_today == 0) continue; + if(nb_disk_same_day == 1 && nb_disk_today == 0) + continue; nb_today2 = nb_today*nb_today; - if(nb_today == 0 && nb_same_day > 1) nb_same_day++; + if(nb_today == 0 && nb_same_day > 1) + nb_same_day++; if(nb_same_day >= nb_today2) { est(dp)->promote = ((nb_same_day - nb_today2)*(nb_same_day - nb_today2)) + @@ -2481,61 +2959,65 @@ static int promote_highest_priority_incremental P((void)) conf_dumpcycle - est(dp)->next_level0; } - if(!dp_promote || est(dp_promote)->promote < est(dp)->promote) { + qname = quote_string(dp->name); + if(!dp_promote || est(dp_promote)->promote < est(dp)->promote) { dp_promote = dp; - fprintf(stderr," try %s:%s %d %d %d = %d\n", - dp->host->hostname, dp->name, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote); + g_fprintf(stderr," try %s:%s %d %d %d = %d\n", + dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote); } - else { - fprintf(stderr,"no try %s:%s %d %d %d = %d\n", - dp->host->hostname, dp->name, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote); + else { + g_fprintf(stderr,"no try %s:%s %d %d %d = %d\n", + dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote); } + amfree(qname); } if(dp_promote) { + one_est_t *level0_est; dp = dp_promote; + level0_est = est_for_level(dp, 0); - new_size = est_tape_size(dp, 0); - new_total = total_size - est(dp)->dump_size + new_size; - new_lev0 = total_lev0 + new_size; + qname = quote_string(dp->name); + new_total = total_size - est(dp)->dump_est->csize + level0_est->csize; + new_lev0 = (gint64)total_lev0 + level0_est->csize; total_size = new_total; - total_lev0 = new_lev0; + total_lev0 = (double)new_lev0; check_days = est(dp)->next_level0; - est(dp)->degr_level = est(dp)->dump_level; - est(dp)->degr_size = est(dp)->dump_size; - est(dp)->dump_level = 0; - est(dp)->dump_size = new_size; + est(dp)->degr_est = est(dp)->dump_est; + est(dp)->dump_est = level0_est; est(dp)->next_level0 = 0; - fprintf(stderr, - " promote: moving %s:%s up, total_lev0 %1.0f, total_size " AM64_FMT "\n", - dp->host->hostname, dp->name, - total_lev0, total_size); + g_fprintf(stderr, + _(" promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"), + dp->host->hostname, qname, + total_lev0, (long long)total_size); log_add(L_INFO, - "Full dump of %s:%s promoted from %d day%s ahead.", - dp->host->hostname, dp->name, - check_days, (check_days == 1) ? "" : "s"); + plural(_("Full dump of %s:%s promoted from %d day ahead."), + _("Full dump of %s:%s promoted from %d days ahead."), + check_days), + dp->host->hostname, qname, check_days); + amfree(qname); return 1; } return 0; } -static int promote_hills P((void)) +static int promote_hills(void) { disk_t *dp; struct balance_stats { int disks; - long size; + gint64 size; } *sp = NULL; int days; int hill_days = 0; - long hill_size; - long new_size; - long new_total; + gint64 hill_size; + gint64 new_total; int my_dumpcycle; + char *qname; /* If we are already doing a level 0 don't bother */ if(total_lev0 > 0) @@ -2546,13 +3028,16 @@ static int promote_hills P((void)) if(my_dumpcycle > 10000) my_dumpcycle = 10000; sp = (struct balance_stats *) - alloc(sizeof(struct balance_stats) * my_dumpcycle); + alloc(SIZEOF(struct balance_stats) * my_dumpcycle); - for(days = 0; days < my_dumpcycle; days++) - sp[days].disks = sp[days].size = 0; + for(days = 0; days < my_dumpcycle; days++) { + sp[days].disks = 0; + sp[days].size = (gint64)0; + } for(dp = schedq.head; dp != NULL; dp = dp->next) { - days = est(dp)->next_level0; /* This is > 0 by definition */ + days = est(dp)->next_level0; + if (days < 0) days = 0; if(daysskip_full && dp->strategy != DS_NOFULL && dp->strategy != DS_INCRONLY) { sp[days].disks++; @@ -2563,7 +3048,7 @@ static int promote_hills P((void)) /* Search for a suitable big hill and cut it down */ while(1) { /* Find the tallest hill */ - hill_size = 0; + hill_size = (gint64)0; for(days = 0; days < my_dumpcycle; days++) { if(sp[days].disks > 1 && sp[days].size > hill_size) { hill_size = sp[days].size; @@ -2571,39 +3056,43 @@ static int promote_hills P((void)) } } - if(hill_size <= 0) break; /* no suitable hills */ + if(hill_size <= (gint64)0) break; /* no suitable hills */ /* Find all the dumps in that hill and try and remove one */ for(dp = schedq.head; dp != NULL; dp = dp->next) { + one_est_t *level0_est; if(est(dp)->next_level0 != hill_days || est(dp)->next_level0 > dp->maxpromoteday || dp->skip_full || dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) continue; - new_size = est_tape_size(dp, 0); - new_total = total_size - est(dp)->dump_size + new_size; + level0_est = est_for_level(dp, 0); + if (level0_est->nsize <= (gint64)0) + continue; + new_total = total_size - est(dp)->dump_est->csize + level0_est->csize; if(new_total > tape_length) continue; /* We found a disk we can promote */ + qname = quote_string(dp->name); total_size = new_total; - total_lev0 += new_size; - est(dp)->degr_level = est(dp)->dump_level; - est(dp)->degr_size = est(dp)->dump_size; - est(dp)->dump_level = 0; + total_lev0 += (double)level0_est->csize; + est(dp)->degr_est = est(dp)->dump_est; + est(dp)->dump_est = level0_est; est(dp)->next_level0 = 0; - est(dp)->dump_size = new_size; - fprintf(stderr, - " promote: moving %s:%s up, total_lev0 %1.0f, total_size " AM64_FMT "\n", - dp->host->hostname, dp->name, - total_lev0, total_size); + g_fprintf(stderr, + _(" promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"), + dp->host->hostname, qname, + total_lev0, (long long)total_size); log_add(L_INFO, - "Full dump of %s:%s specially promoted from %d day%s ahead.", - dp->host->hostname, dp->name, - hill_days, (hill_days == 1) ? "" : "s"); + plural(_("Full dump of %s:%s specially promoted from %d day ahead."), + _("Full dump of %s:%s specially promoted from %d days ahead."), + hill_days), + dp->host->hostname, qname, hill_days); + amfree(qname); amfree(sp); return 1; } @@ -2621,94 +3110,142 @@ static int promote_hills P((void)) * * XXX - memory leak - we shouldn't just throw away *dp */ -static void output_scheduleline(dp) - disk_t *dp; +static void output_scheduleline( + disk_t *dp) { est_t *ep; - long dump_time = 0, degr_time = 0; + time_t dump_time = 0, degr_time = 0; + double dump_kps = 0, degr_kps = 0; char *schedline = NULL, *degr_str = NULL; char dump_priority_str[NUM_STR_SIZE]; char dump_level_str[NUM_STR_SIZE]; - char dump_size_str[NUM_STR_SIZE]; + char dump_nsize_str[NUM_STR_SIZE]; + char dump_csize_str[NUM_STR_SIZE]; char dump_time_str[NUM_STR_SIZE]; + char dump_kps_str[NUM_STR_SIZE]; char degr_level_str[NUM_STR_SIZE]; - char degr_size_str[NUM_STR_SIZE]; + char degr_nsize_str[NUM_STR_SIZE]; + char degr_csize_str[NUM_STR_SIZE]; char degr_time_str[NUM_STR_SIZE]; + char degr_kps_str[NUM_STR_SIZE]; char *dump_date, *degr_date; char *features; - int i; + char *qname = quote_string(dp->name); ep = est(dp); - if(ep->dump_size == -1) { + if(ep->dump_est->csize == (gint64)-1) { /* no estimate, fail the disk */ - fprintf(stderr, - "%s: FAILED %s %s %s %d [no estimate]\n", + g_fprintf(stderr, + _("%s: FAILED %s %s %s %d \"[no estimate]\"\n"), get_pname(), - dp->host->hostname, dp->name, datestamp, ep->dump_level); - log_add(L_FAIL, "%s %s %s %d [no estimate]", - dp->host->hostname, dp->name, datestamp, ep->dump_level); + dp->host->hostname, qname, planner_timestamp, ep->dump_est->level); + log_add(L_FAIL, _("%s %s %s %d [no estimate]"), + dp->host->hostname, qname, planner_timestamp, ep->dump_est->level); + amfree(qname); return; } - dump_date = degr_date = (char *)0; - for(i = 0; i < MAX_LEVELS; i++) { - if(ep->dump_level == ep->level[i]) - dump_date = ep->dumpdate[i]; - if(ep->degr_level == ep->level[i]) - degr_date = ep->dumpdate[i]; - } + dump_date = ep->dump_est->dumpdate; + degr_date = ep->degr_est->dumpdate; #define fix_rate(rate) (rate < 1.0 ? DEFAULT_DUMPRATE : rate) - if(ep->dump_level == 0) { - dump_time = ep->dump_size / fix_rate(ep->fullrate); + if(ep->dump_est->level == 0) { + dump_kps = fix_rate(ep->fullrate); + dump_time = (time_t)((double)ep->dump_est->csize / dump_kps); - if(ep->degr_size != -1) { - degr_time = ep->degr_size / fix_rate(ep->incrrate); + if(ep->degr_est->csize != (gint64)-1) { + degr_kps = fix_rate(ep->incrrate); + degr_time = (time_t)((double)ep->degr_est->csize / degr_kps); } } else { - dump_time = ep->dump_size / fix_rate(ep->incrrate); - } - - if(ep->dump_level == 0 && ep->degr_size != -1) { - snprintf(degr_level_str, sizeof(degr_level_str), - "%d", ep->degr_level); - snprintf(degr_size_str, sizeof(degr_size_str), - "%ld", ep->degr_size); - snprintf(degr_time_str, sizeof(degr_time_str), - "%ld", degr_time); + dump_kps = fix_rate(ep->incrrate); + dump_time = (time_t)((double)ep->dump_est->csize / dump_kps); + } + + if(ep->dump_est->level == 0 && ep->degr_est->csize != (gint64)-1) { + g_snprintf(degr_level_str, sizeof(degr_level_str), + "%d", ep->degr_est->level); + g_snprintf(degr_nsize_str, sizeof(degr_nsize_str), + "%lld", (long long)ep->degr_est->nsize); + g_snprintf(degr_csize_str, sizeof(degr_csize_str), + "%lld", (long long)ep->degr_est->csize); + g_snprintf(degr_time_str, sizeof(degr_time_str), + "%lld", (long long)degr_time); + g_snprintf(degr_kps_str, sizeof(degr_kps_str), + "%.0lf", degr_kps); degr_str = vstralloc(" ", degr_level_str, " ", degr_date, - " ", degr_size_str, + " ", degr_nsize_str, + " ", degr_csize_str, " ", degr_time_str, + " ", degr_kps_str, NULL); + } else { + char *degr_mesg; + if (ep->degr_mesg) { + degr_mesg = quote_string(ep->degr_mesg); + } else { + degr_mesg = quote_string(_("Skipping: cannot dump in degraded mode for unknown reason")); + } + degr_str = vstralloc(" ", degr_mesg, NULL); + amfree(degr_mesg); } - snprintf(dump_priority_str, sizeof(dump_priority_str), + g_snprintf(dump_priority_str, SIZEOF(dump_priority_str), "%d", ep->dump_priority); - snprintf(dump_level_str, sizeof(dump_level_str), - "%d", ep->dump_level); - snprintf(dump_size_str, sizeof(dump_size_str), - "%ld", ep->dump_size); - snprintf(dump_time_str, sizeof(dump_time_str), - "%ld", dump_time); + g_snprintf(dump_level_str, SIZEOF(dump_level_str), + "%d", ep->dump_est->level); + g_snprintf(dump_nsize_str, sizeof(dump_nsize_str), + "%lld", (long long)ep->dump_est->nsize); + g_snprintf(dump_csize_str, sizeof(dump_csize_str), + "%lld", (long long)ep->dump_est->csize); + g_snprintf(dump_time_str, sizeof(dump_time_str), + "%lld", (long long)dump_time); + g_snprintf(dump_kps_str, sizeof(dump_kps_str), + "%.0lf", dump_kps); features = am_feature_to_string(dp->host->features); schedline = vstralloc("DUMP ",dp->host->hostname, " ", features, - " ", dp->name, - " ", datestamp, + " ", qname, + " ", planner_timestamp, " ", dump_priority_str, " ", dump_level_str, " ", dump_date, - " ", dump_size_str, + " ", dump_nsize_str, + " ", dump_csize_str, " ", dump_time_str, + " ", dump_kps_str, degr_str ? degr_str : "", "\n", NULL); + if (est(dp)->dump_est->guessed == 1) { + log_add(L_WARNING, _("WARNING: no history available for %s:%s; guessing that size will be %lld KB\n"), dp->host->hostname, qname, (long long)est(dp)->dump_est->csize); + } fputs(schedline, stdout); fputs(schedline, stderr); amfree(features); amfree(schedline); amfree(degr_str); + amfree(qname); +} + +static void +server_estimate( + disk_t *dp, + int i, + info_t *info, + int level) +{ + int stats; + gint64 size; + + size = internal_server_estimate(dp, info, level, &stats); + + est(dp)->dump_est = &est(dp)->estimate[i]; + est(dp)->estimate[i].nsize = size; + if (stats == 0) { + est(dp)->estimate[i].guessed = 1; + } }