/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
- * Copyright (c) 1991-2000 University of Maryland at College Park
+ * Copyright (c) 1991-1998 University of Maryland at College Park
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* file named AUTHORS, in the root directory of this distribution.
*/
/*
- * $Id: driver.c,v 1.58.2.31.2.8.2.20.2.16 2005/09/20 21:31:52 jrjackson Exp $
+ * $Id: driver.c 6512 2007-05-24 17:00:24Z ian $
*
* controlling process for the Amanda backup system
*/
*/
#include "amanda.h"
+#include "find.h"
#include "clock.h"
#include "conffile.h"
#include "diskfile.h"
+#include "event.h"
#include "holding.h"
#include "infofile.h"
#include "logfile.h"
-#include "statfs.h"
-#include "version.h"
+#include "fsusage.h"
#include "driverio.h"
#include "server_util.h"
-
-disklist_t waitq, runq, tapeq, roomq;
-int pending_aborts, inside_dump_to_tape;
-disk_t *taper_disk;
-int degraded_mode;
-unsigned long reserved_space;
-unsigned long total_disksize;
-char *dumper_program;
-int inparallel;
-int nodump = 0;
-unsigned long tape_length, tape_left = 0;
-int conf_taperalgo;
-am_host_t *flushhost = NULL;
-
-int client_constrained P((disk_t *dp));
-int sort_by_priority_reversed P((disk_t *a, disk_t *b));
-int sort_by_time P((disk_t *a, disk_t *b));
-int start_some_dumps P((disklist_t *rq));
-void dump_schedule P((disklist_t *qp, char *str));
-void start_degraded_mode P((disklist_t *queuep));
-void handle_taper_result P((void));
-dumper_t *idle_dumper P((void));
-int some_dumps_in_progress P((void));
-int num_busy_dumpers P((void));
-dumper_t *lookup_dumper P((int fd));
-void handle_dumper_result P((int fd));
-void read_flush P((disklist_t *tapeqp));
-void read_schedule P((disklist_t *waitqp, disklist_t *runqp));
-int free_kps P((interface_t *ip));
-void interface_state P((char *time_str));
-void allocate_bandwidth P((interface_t *ip, int kps));
-void deallocate_bandwidth P((interface_t *ip, int kps));
-unsigned long free_space P((void));
-assignedhd_t **find_diskspace P((unsigned long size, int *cur_idle, assignedhd_t *preferred));
-char *diskname2filename P((char *dname));
-int assign_holdingdisk P((assignedhd_t **holdp, disk_t *diskp));
-static void adjust_diskspace P((disk_t *diskp, cmd_t cmd));
-static void delete_diskspace P((disk_t *diskp));
-assignedhd_t **build_diskspace P((char *destname));
-void holdingdisk_state P((char *time_str));
-int dump_to_tape P((disk_t *dp));
-int queue_length P((disklist_t q));
-void short_dump_state P((void));
-void dump_state P((char *str));
-void startaflush P((void));
-int main P((int main_argc, char **main_argv));
-
+#include "timestamp.h"
+
+#define driver_debug(i, ...) do { \
+ if ((i) <= debug_driver) { \
+ dbprintf(__VA_ARGS__); \
+ } \
+} while (0)
+
+#define hold_debug(i, ...) do { \
+ if ((i) <= debug_holding) { \
+ dbprintf(__VA_ARGS__); \
+ } \
+} while (0)
+
+static disklist_t waitq; // dle waiting estimate result
+static disklist_t runq; // dle waiting to be dumped to holding disk
+static disklist_t directq; // dle waiting to be dumped directly to tape
+static disklist_t tapeq; // dle on holding disk waiting to be written
+ // to tape
+static disklist_t roomq; // dle waiting for more space on holding disk
+static int pending_aborts;
+static int degraded_mode;
+static off_t reserved_space;
+static off_t total_disksize;
+static char *dumper_program;
+static char *chunker_program;
+static int inparallel;
+static int nodump = 0;
+static off_t tape_length = (off_t)0;
+static int current_tape = 0;
+static int conf_taperalgo;
+static int conf_taper_parallel_write;
+static int conf_runtapes;
+static time_t sleep_time;
static int idle_reason;
-char *datestamp;
-char *timestamp;
-
-char *idle_strings[] = {
+static char *driver_timestamp;
+static char *hd_driver_timestamp;
+static am_host_t *flushhost = NULL;
+static int need_degraded=0;
+static holdalloc_t *holdalloc;
+static int num_holdalloc;
+static event_handle_t *dumpers_ev_time = NULL;
+static event_handle_t *flush_ev_read = NULL;
+static event_handle_t *schedule_ev_read = NULL;
+static int conf_flush_threshold_dumped;
+static int conf_flush_threshold_scheduled;
+static int conf_taperflush;
+static off_t flush_threshold_dumped;
+static off_t flush_threshold_scheduled;
+static off_t taperflush;
+static int schedule_done; // 1 if we don't wait for a
+ // schedule from the planner
+static int force_flush; // All dump are terminated, we
+ // must now respect taper_flush
+static int taper_nb_scan_volume = 0;
+static int nb_sent_new_tape = 0;
+static int taper_started = 0;
+static taper_t *last_started_taper;
+
+static int wait_children(int count);
+static void wait_for_children(void);
+static void allocate_bandwidth(netif_t *ip, unsigned long kps);
+static int assign_holdingdisk(assignedhd_t **holdp, disk_t *diskp);
+static void adjust_diskspace(disk_t *diskp, cmd_t cmd);
+static void delete_diskspace(disk_t *diskp);
+static assignedhd_t **build_diskspace(char *destname);
+static int client_constrained(disk_t *dp);
+static void deallocate_bandwidth(netif_t *ip, unsigned long kps);
+static void dump_schedule(disklist_t *qp, char *str);
+static assignedhd_t **find_diskspace(off_t size, int *cur_idle,
+ assignedhd_t *preferred);
+static unsigned long free_kps(netif_t *ip);
+static off_t free_space(void);
+static void dumper_chunker_result(disk_t *dp);
+static void dumper_taper_result(disk_t *dp);
+static void file_taper_result(disk_t *dp);
+static void handle_dumper_result(void *);
+static void handle_chunker_result(void *);
+static void handle_dumpers_time(void *);
+static void handle_taper_result(void *);
+
+static void holdingdisk_state(char *time_str);
+static taper_t *idle_taper(void);
+static taper_t *taper_from_name(char *name);
+static void interface_state(char *time_str);
+static int queue_length(disklist_t q);
+static void read_flush(void *cookie);
+static void read_schedule(void *cookie);
+static void short_dump_state(void);
+static void startaflush(void);
+static void start_degraded_mode(disklist_t *queuep);
+static void start_some_dumps(disklist_t *rq);
+static void continue_port_dumps(void);
+static void update_failed_dump(disk_t *);
+static int all_taper_idle(void);
+
+typedef enum {
+ TAPE_ACTION_NO_ACTION = 0,
+ TAPE_ACTION_SCAN = (1 << 0),
+ TAPE_ACTION_NEW_TAPE = (1 << 1),
+ TAPE_ACTION_NO_NEW_TAPE = (1 << 2),
+ TAPE_ACTION_START_A_FLUSH = (1 << 3),
+ TAPE_ACTION_START_A_FLUSH_FIT = (1 << 4),
+ TAPE_ACTION_MOVE = (1 << 5)
+} TapeAction;
+
+static TapeAction tape_action(taper_t *taper, char **why_no_new_tape);
+
+static const char *idle_strings[] = {
#define NOT_IDLE 0
- "not-idle",
-#define IDLE_START_WAIT 1
- "start-wait",
-#define IDLE_NO_DUMPERS 2
- "no-dumpers",
+ T_("not-idle"),
+#define IDLE_NO_DUMPERS 1
+ T_("no-dumpers"),
+#define IDLE_START_WAIT 2
+ T_("start-wait"),
#define IDLE_NO_HOLD 3
- "no-hold",
+ T_("no-hold"),
#define IDLE_CLIENT_CONSTRAINED 4
- "client-constrained",
-#define IDLE_NO_DISKSPACE 5
- "no-diskspace",
-#define IDLE_TOO_LARGE 6
- "file-too-large",
-#define IDLE_NO_BANDWIDTH 7
- "no-bandwidth",
-#define IDLE_TAPER_WAIT 8
- "taper-wait",
+ T_("client-constrained"),
+#define IDLE_NO_BANDWIDTH 5
+ T_("no-bandwidth"),
+#define IDLE_NO_DISKSPACE 6
+ T_("no-diskspace")
};
-#define SLEEP_MAX (24*3600)
-struct timeval sleep_time = { SLEEP_MAX, 0 };
-/* enabled if any disks are in start-wait: */
-int any_delayed_disk = 0;
-
-int main(main_argc, main_argv)
- int main_argc;
- char **main_argv;
+int
+main(
+ int argc,
+ char ** argv)
{
- disklist_t *origqp;
+ disklist_t origq;
disk_t *diskp;
- fd_set selectset;
- int fd, dsk;
+ int dsk;
dumper_t *dumper;
char *newdir = NULL;
- generic_fs_stats_t fs;
+ struct fs_usage fsusage;
holdingdisk_t *hdp;
- unsigned long malloc_hist_1, malloc_size_1;
- unsigned long malloc_hist_2, malloc_size_2;
+ identlist_t il;
unsigned long reserve = 100;
- char *conffile;
char *conf_diskfile;
- cmd_t cmd;
- int result_argc;
- char *result_argv[MAX_ARGS+1];
+ char **result_argv = NULL;
char *taper_program;
- amwait_t retstat;
char *conf_tapetype;
tapetype_t *tape;
+ char *line;
+ char hostname[1025];
+ intmax_t kb_avail;
+ config_overrides_t *cfg_ovr = NULL;
+ char *cfg_opt = NULL;
+ holdalloc_t *ha, *ha_last;
+ find_result_t *holding_files;
+ disklist_t holding_disklist = { NULL, NULL };
+ int no_taper = FALSE;
+
+ /*
+ * Configure program for internationalization:
+ * 1) Only set the message locale for now.
+ * 2) Set textdomain for all amanda related programs to "amanda"
+ * We don't want to be forced to support dozens of message catalogs.
+ */
+ setlocale(LC_MESSAGES, "C");
+ textdomain("amanda");
safe_fd(-1, 0);
- setvbuf(stdout, (char *)NULL, _IOLBF, 0);
- setvbuf(stderr, (char *)NULL, _IOLBF, 0);
+ setvbuf(stdout, (char *)NULL, (int)_IOLBF, 0);
+ setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
set_pname("driver");
- signal(SIGPIPE, SIG_IGN);
+ dbopen(DBG_SUBDIR_SERVER);
- malloc_size_1 = malloc_inuse(&malloc_hist_1);
+ atexit(wait_for_children);
+
+ /* Don't die when child closes pipe */
+ signal(SIGPIPE, SIG_IGN);
- erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
- set_logerror(logerror);
+ add_amanda_log_handler(amanda_log_stderr);
+ add_amanda_log_handler(amanda_log_trace_log);
startclock();
- FD_ZERO(&readset);
- printf("%s: pid %ld executable %s version %s\n",
- get_pname(), (long) getpid(), main_argv[0], version());
+ cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
- if (main_argc > 1) {
- config_name = stralloc(main_argv[1]);
- config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
- if(main_argc > 2) {
- if(strncmp(main_argv[2], "nodump", 6) == 0) {
- nodump = 1;
- }
- }
+ if (argc > 1)
+ cfg_opt = argv[1];
+ set_config_overrides(cfg_ovr);
+ config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
- } else {
- char my_cwd[STR_SIZE];
+ conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+ read_diskfile(conf_diskfile, &origq);
+ disable_skip_disk(&origq);
+ amfree(conf_diskfile);
- 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);
+ if (config_errors(NULL) >= CFGERR_WARNINGS) {
+ config_print_errors();
+ if (config_errors(NULL) >= CFGERR_ERRORS) {
+ g_critical(_("errors processing config file"));
}
}
- safe_cd();
+ log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+ g_printf(_("%s: pid %ld executable %s version %s\n"),
+ get_pname(), (long) getpid(), argv[0], VERSION);
- conffile = stralloc2(config_dir, CONFFILE_NAME);
- if(read_conffile(conffile)) {
- error("errors processing config file \"%s\"", conffile);
+ if(argc > 2) {
+ if(strcmp(argv[2], "nodump") == 0) {
+ nodump = 1;
+ argv++;
+ argc--;
+ }
}
- amfree(conffile);
-
- amfree(datestamp);
- datestamp = construct_datestamp(NULL);
- timestamp = construct_timestamp(NULL);
- log_add(L_START,"date %s", datestamp);
- taper_program = vstralloc(libexecdir, "/", "taper", versionsuffix(), NULL);
- dumper_program = vstralloc(libexecdir, "/", "dumper", versionsuffix(),
- NULL);
+ if (argc > 2) {
+ if (strcmp(argv[2], "--no-taper") == 0) {
+ no_taper = TRUE;
+ argv++;
+ argc--;
+ }
+ }
- conf_taperalgo = getconf_int(CNF_TAPERALGO);
- conf_tapetype = getconf_str(CNF_TAPETYPE);
- tape = lookup_tapetype(conf_tapetype);
- tape_length = tape->length;
- printf("driver: tape size %ld\n", tape_length);
+ safe_cd(); /* do this *after* config_init */
- /* taper takes a while to get going, so start it up right away */
+ check_running_as(RUNNING_AS_DUMPUSER);
- init_driverio();
- startup_tape_process(taper_program);
- taper_cmd(START_TAPER, datestamp, NULL, 0, NULL);
+ dbrename(get_config_name(), DBG_SUBDIR_SERVER);
- /* start initializing: read in databases */
+ /* load DLEs from the holding disk, in case there's anything to flush there */
+ search_holding_disk(&holding_files, &holding_disklist);
+ /* note that the dumps are added to the global disklist, so we need not consult
+ * holding_files or holding_disklist after this */
- conf_diskfile = getconf_str(CNF_DISKFILE);
- if (*conf_diskfile == '/') {
- conf_diskfile = stralloc(conf_diskfile);
- } else {
- conf_diskfile = stralloc2(config_dir, conf_diskfile);
+ amfree(driver_timestamp);
+ /* read timestamp from stdin */
+ while ((line = agets(stdin)) != NULL) {
+ if (line[0] != '\0')
+ break;
+ amfree(line);
}
- if((origqp = read_diskfile(conf_diskfile)) == NULL) {
- error("could not load disklist \"%s\"", conf_diskfile);
+ if ( line == NULL ) {
+ error(_("Did not get DATE line from planner"));
+ /*NOTREACHED*/
+ }
+ driver_timestamp = alloc(15);
+ strncpy(driver_timestamp, &line[5], 14);
+ driver_timestamp[14] = '\0';
+ amfree(line);
+ log_add(L_START,_("date %s"), driver_timestamp);
+
+ gethostname(hostname, SIZEOF(hostname));
+ log_add(L_STATS,_("hostname %s"), hostname);
+
+ /* check that we don't do many dump in a day and usetimestamps is off */
+ if(strlen(driver_timestamp) == 8) {
+ if (!nodump) {
+ char *conf_logdir = getconf_str(CNF_LOGDIR);
+ char *logfile = vstralloc(conf_logdir, "/log.",
+ driver_timestamp, ".0", NULL);
+ char *oldlogfile = vstralloc(conf_logdir, "/oldlog/log.",
+ driver_timestamp, ".0", NULL);
+ if(access(logfile, F_OK) == 0 || access(oldlogfile, F_OK) == 0) {
+ log_add(L_WARNING, _("WARNING: This is not the first amdump run today. Enable the usetimestamps option in the configuration file if you want to run amdump more than once per calendar day."));
+ }
+ amfree(oldlogfile);
+ amfree(logfile);
+ }
+ hd_driver_timestamp = get_timestamp_from_time(0);
+ }
+ else {
+ hd_driver_timestamp = stralloc(driver_timestamp);
}
- amfree(conf_diskfile);
- /* set up any configuration-dependent variables */
+ taper_program = vstralloc(amlibexecdir, "/", "taper", NULL);
+ dumper_program = vstralloc(amlibexecdir, "/", "dumper", NULL);
+ chunker_program = vstralloc(amlibexecdir, "/", "chunker", NULL);
- inparallel = getconf_int(CNF_INPARALLEL);
+ conf_taperalgo = getconf_taperalgo(CNF_TAPERALGO);
+ conf_taper_parallel_write = getconf_int(CNF_TAPER_PARALLEL_WRITE);
+ conf_tapetype = getconf_str(CNF_TAPETYPE);
+ conf_runtapes = getconf_int(CNF_RUNTAPES);
+ if (conf_taper_parallel_write > conf_runtapes) {
+ conf_taper_parallel_write = conf_runtapes;
+ }
+ tape = lookup_tapetype(conf_tapetype);
+ tape_length = tapetype_get_length(tape);
+ g_printf("driver: tape size %lld\n", (long long)tape_length);
+ conf_flush_threshold_dumped = getconf_int(CNF_FLUSH_THRESHOLD_DUMPED);
+ conf_flush_threshold_scheduled = getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED);
+ conf_taperflush = getconf_int(CNF_TAPERFLUSH);
+
+ flush_threshold_dumped = (conf_flush_threshold_dumped * tape_length) / 100;
+ flush_threshold_scheduled = (conf_flush_threshold_scheduled * tape_length) / 100;
+ taperflush = (conf_taperflush *tape_length) / 100;
- reserve = getconf_int(CNF_RESERVE);
+ driver_debug(1, _("flush-threshold-dumped: %lld\n"), (long long)flush_threshold_dumped);
+ driver_debug(1, _("flush-threshold-scheduled: %lld\n"), (long long)flush_threshold_scheduled);
+ driver_debug(1, _("taperflush: %lld\n"), (long long)taperflush);
+
+ /* set up any configuration-dependent variables */
- total_disksize = 0;
- for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
- hdp->up = (void *)alloc(sizeof(holdalloc_t));
- holdalloc(hdp)->allocated_dumpers = 0;
- holdalloc(hdp)->allocated_space = 0L;
+ inparallel = getconf_int(CNF_INPARALLEL);
- if(get_fs_stats(hdp->diskdir, &fs) == -1
- || access(hdp->diskdir, W_OK) == -1) {
- log_add(L_WARNING, "WARNING: ignoring holding disk %s: %s\n",
- hdp->diskdir, strerror(errno));
- hdp->disksize = 0L;
+ reserve = (unsigned long)getconf_int(CNF_RESERVE);
+
+ total_disksize = (off_t)0;
+ ha_last = NULL;
+ num_holdalloc = 0;
+ for (il = getconf_identlist(CNF_HOLDINGDISK), dsk = 0;
+ il != NULL;
+ il = il->next, dsk++) {
+ hdp = lookup_holdingdisk(il->data);
+ ha = alloc(SIZEOF(holdalloc_t));
+ num_holdalloc++;
+
+ /* link the list in the same order as getconf_holdingdisks's results */
+ ha->next = NULL;
+ if (ha_last == NULL)
+ holdalloc = ha;
+ else
+ ha_last->next = ha;
+ ha_last = ha;
+
+ ha->hdisk = hdp;
+ ha->allocated_dumpers = 0;
+ ha->allocated_space = (off_t)0;
+ ha->disksize = holdingdisk_get_disksize(hdp);
+
+ /* get disk size */
+ if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1
+ || access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
+ log_add(L_WARNING, _("WARNING: ignoring holding disk %s: %s\n"),
+ holdingdisk_get_diskdir(hdp), strerror(errno));
+ ha->disksize = 0L;
continue;
}
- if(fs.avail != -1) {
- if(hdp->disksize > 0) {
- if(hdp->disksize > fs.avail) {
- log_add(L_WARNING,
- "WARNING: %s: %ld KB requested, but only %ld KB available.",
- hdp->diskdir, hdp->disksize, fs.avail);
- hdp->disksize = fs.avail;
- }
- }
- else if(fs.avail + hdp->disksize < 0) {
+ /* do the division first to avoid potential integer overflow */
+ if (fsusage.fsu_bavail_top_bit_set)
+ kb_avail = 0;
+ else
+ kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
+
+ if(ha->disksize > (off_t)0) {
+ if(ha->disksize > kb_avail) {
log_add(L_WARNING,
- "WARNING: %s: not %ld KB free.",
- hdp->diskdir, -hdp->disksize);
- hdp->disksize = 0L;
- continue;
+ _("WARNING: %s: %lld KB requested, "
+ "but only %lld KB available."),
+ holdingdisk_get_diskdir(hdp),
+ (long long)ha->disksize,
+ (long long)kb_avail);
+ ha->disksize = kb_avail;
}
- else
- hdp->disksize += fs.avail;
}
+ /* ha->disksize is negative; use all but that amount */
+ else if(kb_avail < -ha->disksize) {
+ log_add(L_WARNING,
+ _("WARNING: %s: not %lld KB free."),
+ holdingdisk_get_diskdir(hdp),
+ (long long)-ha->disksize);
+ ha->disksize = (off_t)0;
+ continue;
+ }
+ else
+ ha->disksize += kb_avail;
- printf("driver: adding holding disk %d dir %s size %ld chunksize %ld\n",
- dsk, hdp->diskdir, hdp->disksize, hdp->chunksize);
+ g_printf(_("driver: adding holding disk %d dir %s size %lld chunksize %lld\n"),
+ dsk, holdingdisk_get_diskdir(hdp),
+ (long long)ha->disksize,
+ (long long)(holdingdisk_get_chunksize(hdp)));
newdir = newvstralloc(newdir,
- hdp->diskdir, "/", timestamp,
+ holdingdisk_get_diskdir(hdp), "/", hd_driver_timestamp,
NULL);
- if(!mkholdingdir(newdir)) {
- hdp->disksize = 0L;
+ if(!mkholdingdir(newdir)) {
+ ha->disksize = (off_t)0;
}
- total_disksize += hdp->disksize;
+ total_disksize += ha->disksize;
}
- reserved_space = total_disksize * (reserve / 100.0);
+ reserved_space = total_disksize * (off_t)(reserve / 100);
- printf("reserving %ld out of %ld for degraded-mode dumps\n",
- reserved_space, free_space());
+ g_printf(_("reserving %lld out of %lld for degraded-mode dumps\n"),
+ (long long)reserved_space, (long long)free_space());
amfree(newdir);
if(inparallel > MAX_DUMPERS) inparallel = MAX_DUMPERS;
- /* fire up the dumpers now while we are waiting */
+ /* taper takes a while to get going, so start it up right away */
+
+ init_driverio();
+ startup_tape_process(taper_program, conf_taper_parallel_write, no_taper);
- if(!nodump) startup_dump_processes(dumper_program, inparallel);
+ /* fire up the dumpers now while we are waiting */
+ if(!nodump) startup_dump_processes(dumper_program, inparallel, driver_timestamp);
/*
* Read schedule from stdin. Usually, this is a pipe from planner,
* in parallel with the planner.
*/
- waitq = *origqp;
- tapeq.head = tapeq.tail = NULL;
- roomq.head = roomq.tail = NULL;
- runq.head = runq.tail = NULL;
+ runq.head = NULL;
+ runq.tail = NULL;
+ directq.head = NULL;
+ directq.tail = NULL;
+ waitq = origq;
+ tapeq.head = NULL;
+ tapeq.tail = NULL;
+ roomq.head = NULL;
+ roomq.tail = NULL;
+ taper_nb_wait_reply = 0;
+
+ need_degraded = 0;
+ if (no_taper || conf_runtapes <= 0) {
+ taper_started = 1; /* we'll pretend the taper started and failed immediately */
+ need_degraded = 1;
+ } else {
+ tapetable[0].state = TAPER_STATE_INIT;
+ taper_nb_wait_reply++;
+ taper_nb_scan_volume++;
+ taper_ev_read = event_register(taper_fd, EV_READFD,
+ handle_taper_result, NULL);
+ taper_cmd(START_TAPER, NULL, tapetable[0].name, 0, driver_timestamp);
+ }
- read_flush(&tapeq);
+ flush_ev_read = event_register((event_id_t)0, EV_READFD, read_flush, NULL);
- log_add(L_STATS, "startup time %s", walltime_str(curclock()));
+ log_add(L_STATS, _("startup time %s"), walltime_str(curclock()));
- printf("driver: start time %s inparallel %d bandwidth %d diskspace %lu",
- walltime_str(curclock()), inparallel, free_kps((interface_t *)0),
- free_space());
- printf(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n",
- "OBSOLETE", datestamp, taperalgo2str(conf_taperalgo),
+ g_printf(_("driver: start time %s inparallel %d bandwidth %lu diskspace %lld "), walltime_str(curclock()), inparallel,
+ free_kps(NULL), (long long)free_space());
+ g_printf(_(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n"),
+ "OBSOLETE", driver_timestamp, taperalgo2str(conf_taperalgo),
getconf_str(CNF_DUMPORDER));
fflush(stdout);
- /* Let's see if the tape is ready */
-
- cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
-
- if(cmd != TAPER_OK) {
- /* no tape, go into degraded mode: dump to holding disk */
- start_degraded_mode(&runq);
- FD_CLR(taper,&readset);
- }
+ schedule_done = nodump;
+ force_flush = 0;
- short_dump_state(); /* for amstatus */
-
- tape_left = tape_length;
- taper_busy = 0;
- taper_disk = NULL;
+ short_dump_state();
+ event_loop(0);
- /* Start autoflush while waiting for dump schedule */
- if(!nodump) {
- /* Start any autoflush tape writes */
- if (!empty(tapeq)) {
- startaflush();
- short_dump_state(); /* for amstatus */
+ force_flush = 1;
- /* Process taper results until the schedule arrives */
- while (1) {
- FD_ZERO(&selectset);
- FD_SET(0, &selectset);
- FD_SET(taper, &selectset);
+ /* mv runq to directq */
+ while (!empty(runq)) {
+ diskp = dequeue_disk(&runq);
+ headqueue_disk(&directq, diskp);
+ }
- if(select(taper+1, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL,
- &sleep_time) == -1)
- error("select: %s", strerror(errno));
- if (FD_ISSET(0, &selectset)) break; /* schedule arrived */
- if (FD_ISSET(taper, &selectset)) handle_taper_result();
- short_dump_state(); /* for amstatus */
+ /* handle any remaining dumps by dumping directly to tape, if possible */
+ while(!empty(directq) && taper_fd > 0) {
+ time_t sleep_time = 100000000;
+ disk_t *sleep_diskp = NULL;
+ time_t now = time(0);
+
+ /* Find one we can do immediately or the sonner */
+ for (diskp = directq.head; diskp != NULL; diskp = diskp->next) {
+ if (diskp->to_holdingdisk == HOLD_REQUIRED ||
+ degraded_mode) {
+ sleep_time = 0;
+ sleep_diskp = diskp;
+ } else if (diskp->host->start_t - now < sleep_time &&
+ diskp->start_t -now < sleep_time) {
+ if (diskp->host->start_t > diskp->start_t)
+ sleep_time = diskp->host->start_t - now;
+ else
+ sleep_time = diskp->start_t - now;
+ sleep_diskp = diskp;
}
-
}
+ diskp = sleep_diskp;
+ if (sleep_time > 0)
+ sleep(sleep_time);
+ remove_disk(&directq, diskp);
- /* Read the dump schedule */
- read_schedule(&waitq, &runq);
+ if (diskp->to_holdingdisk == HOLD_REQUIRED) {
+ char *qname = quote_string(diskp->name);
+ log_add(L_FAIL, "%s %s %s %d [%s]",
+ diskp->host->hostname, qname, sched(diskp)->datestamp,
+ sched(diskp)->level,
+ _("can't dump required holdingdisk"));
+ amfree(qname);
+ }
+ else if (!degraded_mode) {
+ char *qname = quote_string(diskp->name);
+ log_add(L_FAIL, "%s %s %s %d [%s]",
+ diskp->host->hostname, qname, sched(diskp)->datestamp,
+ sched(diskp)->level,
+ _("can't dump in degraded mode"));
+ amfree(qname);
+ }
+ else {
+ char *qname = quote_string(diskp->name);
+ log_add(L_FAIL, "%s %s %s %d [%s]",
+ diskp->host->hostname, qname, sched(diskp)->datestamp,
+ sched(diskp)->level,
+ num_holdalloc == 0 ?
+ _("can't do degraded dump without holding disk") :
+ diskp->to_holdingdisk != HOLD_NEVER ?
+ _("out of holding space in degraded mode") :
+ _("can't dump 'holdingdisk never' dle in degraded mode"));
+ amfree(qname);
+ }
}
- /* Start any needed flushes */
+ /* fill up the tape or start new one for taperflush */
startaflush();
+ event_loop(0);
- while(start_some_dumps(&runq) || some_dumps_in_progress() ||
- any_delayed_disk) {
- short_dump_state();
-
- /* wait for results */
-
- memcpy(&selectset, &readset, sizeof(fd_set));
- if(select(maxfd+1, (SELECT_ARG_TYPE *)(&selectset),
- NULL, NULL, &sleep_time) == -1)
- error("select: %s", strerror(errno));
+ short_dump_state(); /* for amstatus */
- /* handle any results that have come in */
+ g_printf(_("driver: QUITTING time %s telling children to quit\n"),
+ walltime_str(curclock()));
+ fflush(stdout);
- for(fd = 0; fd <= maxfd; fd++) {
- /*
- * The first pass through the following loop, we have
- * data ready for areads (called by getresult, called by
- * handle_.*_result). But that may read more than one record,
- * so we need to keep processing as long as areads has data.
- * We will get control back after each record and the buffer
- * will go empty (indicated by areads_dataready(fd) == 0)
- * after the last one available has been processed.
- */
- while(FD_ISSET(fd, &selectset) || areads_dataready(fd) > 0) {
- if(fd == taper) handle_taper_result();
- else handle_dumper_result(fd);
- FD_CLR(fd, &selectset);
- }
+ if(!nodump) {
+ for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
+ if(dumper->fd >= 0)
+ dumper_cmd(dumper, QUIT, NULL, NULL);
}
+ }
+ if(taper_fd >= 0) {
+ taper_cmd(QUIT, NULL, NULL, 0, NULL);
}
- /* handle any remaining dumps by dumping directly to tape, if possible */
+ /* wait for all to die */
+ wait_children(600);
- while(!empty(runq)) {
- diskp = dequeue_disk(&runq);
- if(!degraded_mode) {
- int rc = dump_to_tape(diskp);
- if(rc == 1)
- log_add(L_INFO,
- "%s %s %d [dump to tape failed, will try again]",
- diskp->host->hostname,
- diskp->name,
- sched(diskp)->level);
- else if(rc == 2)
- log_add(L_FAIL, "%s %s %s %d [dump to tape failed]",
- diskp->host->hostname,
- diskp->name,
- sched(diskp)->datestamp,
- sched(diskp)->level);
- }
- else
- log_add(L_FAIL, "%s %s %s %d [%s]",
- diskp->host->hostname, diskp->name,
- sched(diskp)->datestamp, sched(diskp)->level,
- diskp->no_hold ?
- "can't dump no-hold disk in degraded mode" :
- "no more holding disk space");
- }
+ /* cleanup */
+ holding_cleanup(NULL, NULL);
- short_dump_state(); /* for amstatus */
+ amfree(newdir);
- printf("driver: QUITTING time %s telling children to quit\n",
- walltime_str(curclock()));
+ check_unfree_serial();
+ g_printf(_("driver: FINISHED time %s\n"), walltime_str(curclock()));
fflush(stdout);
+ log_add(L_FINISH,_("date %s time %s"), driver_timestamp, walltime_str(curclock()));
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ amfree(driver_timestamp);
+
+ amfree(dumper_program);
+ amfree(taper_program);
+ if (result_argv)
+ g_strfreev(result_argv);
+
+ dbclose();
+
+ return 0;
+}
+
+/* sleep up to count seconds, and wait for terminating child process */
+/* if sleep is negative, this function will not timeout */
+/* exit once all child process are finished or the timout expired */
+/* return 0 if no more children to wait */
+/* return 1 if some children are still alive */
+static int
+wait_children(int count)
+{
+ pid_t pid;
+ amwait_t retstat;
+ char *who;
+ char *what;
+ int code=0;
+ dumper_t *dumper;
+ int wait_errno;
+
+ do {
+ do {
+ pid = waitpid((pid_t)-1, &retstat, WNOHANG);
+ wait_errno = errno;
+ if (pid > 0) {
+ what = NULL;
+ if (! WIFEXITED(retstat)) {
+ what = _("signal");
+ code = WTERMSIG(retstat);
+ } else if (WEXITSTATUS(retstat) != 0) {
+ what = _("code");
+ code = WEXITSTATUS(retstat);
+ }
+ who = NULL;
+ for (dumper = dmptable; dumper < dmptable + inparallel;
+ dumper++) {
+ if (pid == dumper->pid) {
+ who = stralloc(dumper->name);
+ dumper->pid = -1;
+ break;
+ }
+ if (dumper->chunker && pid == dumper->chunker->pid) {
+ who = stralloc(dumper->chunker->name);
+ dumper->chunker->pid = -1;
+ break;
+ }
+ }
+ if (who == NULL && pid == taper_pid) {
+ who = stralloc("taper");
+ taper_pid = -1;
+ }
+ if(what != NULL && who == NULL) {
+ who = stralloc("unknown");
+ }
+ if(who && what) {
+ log_add(L_WARNING, _("%s pid %u exited with %s %d\n"), who,
+ (unsigned)pid, what, code);
+ g_printf(_("driver: %s pid %u exited with %s %d\n"), who,
+ (unsigned)pid, what, code);
+ }
+ amfree(who);
+ }
+ } while (pid > 0 || wait_errno == EINTR);
+ if (errno != ECHILD)
+ sleep(1);
+ if (count > 0)
+ count--;
+ } while ((errno != ECHILD) && (count != 0));
+ return (errno != ECHILD);
+}
+
+static void
+kill_children(int signal)
+{
+ dumper_t *dumper;
if(!nodump) {
- for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
- dumper_cmd(dumper, QUIT, NULL);
- }
+ for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
+ if (!dumper->down && dumper->pid > 1) {
+ g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
+ dumper->name, (unsigned)dumper->pid);
+ if (kill(dumper->pid, signal) == -1 && errno == ESRCH) {
+ if (dumper->chunker)
+ dumper->chunker->pid = 0;
+ }
+ if (dumper->chunker && dumper->chunker->pid > 1) {
+ g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
+ dumper->chunker->name,
+ (unsigned)dumper->chunker->pid);
+ if (kill(dumper->chunker->pid, signal) == -1 &&
+ errno == ESRCH)
+ dumper->chunker->pid = 0;
+ }
+ }
+ }
}
- if(taper >= 0) {
- taper_cmd(QUIT, NULL, NULL, 0, NULL);
+ if(taper_pid > 1) {
+ g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
+ "taper", (unsigned)taper_pid);
+ if (kill(taper_pid, signal) == -1 && errno == ESRCH)
+ taper_pid = 0;
}
+}
- /* wait for all to die */
+static void
+wait_for_children(void)
+{
+ dumper_t *dumper;
- while(1) {
- char number[NUM_STR_SIZE];
- pid_t pid;
- char *who;
- char *what;
- int code=0;
-
- if((pid = wait(&retstat)) == -1) {
- if(errno == EINTR) continue;
- else break;
- }
- what = NULL;
- if(! WIFEXITED(retstat)) {
- what = "signal";
- code = WTERMSIG(retstat);
- } else if(WEXITSTATUS(retstat) != 0) {
- what = "code";
- code = WEXITSTATUS(retstat);
- }
- who = NULL;
+ if(!nodump) {
for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
- if(pid == dumper->pid) {
- who = stralloc(dumper->name);
- break;
+ if (dumper->pid > 1 && dumper->fd >= 0) {
+ dumper_cmd(dumper, QUIT, NULL, NULL);
+ if (dumper->chunker && dumper->chunker->pid > 1 &&
+ dumper->chunker->fd >= 0)
+ chunker_cmd(dumper->chunker, QUIT, NULL, NULL);
}
}
- if(who == NULL && pid == taper_pid) {
- who = stralloc("taper");
- }
- if(what != NULL && who == NULL) {
- ap_snprintf(number, sizeof(number), "%ld", (long)pid);
- who = stralloc2("unknown pid ", number);
- }
- if(who && what) {
- log_add(L_WARNING, "%s exited with %s %d\n", who, what, code);
- printf("driver: %s exited with %s %d\n", who, what, code);
- }
- amfree(who);
}
- for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
- amfree(dumper->name);
+ if(taper_pid > 1 && taper_fd > 0) {
+ taper_cmd(QUIT, NULL, NULL, 0, NULL);
}
- for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
- cleanup_holdingdisk(hdp->diskdir, 0);
- amfree(hdp->up);
- }
- amfree(newdir);
+ if(wait_children(60) == 0)
+ return;
- printf("driver: FINISHED time %s\n", walltime_str(curclock()));
- fflush(stdout);
- log_add(L_FINISH,"date %s time %s", datestamp, walltime_str(curclock()));
- amfree(datestamp);
- amfree(timestamp);
+ kill_children(SIGHUP);
+ if(wait_children(60) == 0)
+ return;
- amfree(dumper_program);
- amfree(taper_program);
- amfree(config_dir);
- amfree(config_name);
+ kill_children(SIGKILL);
+ if(wait_children(-1) == 0)
+ return;
- 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);
- }
+static void startaflush_tape(taper_t *taper);
- return 0;
+static void
+startaflush(void)
+{
+ taper_t *taper;
+
+ for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+ taper++) {
+ if (!(taper->state & TAPER_STATE_DONE) &&
+ taper->state & TAPER_STATE_WAIT_FOR_TAPE) {
+ startaflush_tape(taper);
+ }
+ }
+ for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+ taper++) {
+ if (!(taper->state & TAPER_STATE_DONE) &&
+ taper->state & TAPER_STATE_TAPE_REQUESTED) {
+ startaflush_tape(taper);
+ }
+ }
+ for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+ taper++) {
+ if (!(taper->state & TAPER_STATE_DONE) &&
+ taper->state & TAPER_STATE_INIT) {
+ startaflush_tape(taper);
+ }
+ }
+ for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+ taper++) {
+ if (!(taper->state & TAPER_STATE_DONE) &&
+ taper->state & TAPER_STATE_IDLE) {
+ startaflush_tape(taper);
+ }
+ }
}
-void startaflush() {
+static void
+startaflush_tape(
+ taper_t *taper)
+{
disk_t *dp = NULL;
disk_t *fit = NULL;
char *datestamp;
+ off_t extra_tapes_size = 0;
+ off_t taper_left;
+ char *qname;
+ TapeAction result_tape_action;
+ char *why_no_new_tape = NULL;
+ taper_t *taper1;
+
+ result_tape_action = tape_action(taper, &why_no_new_tape);
+
+ if (result_tape_action & TAPE_ACTION_SCAN) {
+ taper->state &= ~TAPER_STATE_TAPE_REQUESTED;
+ taper->state |= TAPER_STATE_WAIT_FOR_TAPE;
+ taper_nb_scan_volume++;
+ taper_cmd(START_SCAN, taper->disk, NULL, 0, NULL);
+ } else if (result_tape_action & TAPE_ACTION_NEW_TAPE) {
+ taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+ taper->state |= TAPER_STATE_WAIT_NEW_TAPE;
+ nb_sent_new_tape++;
+ taper_cmd(NEW_TAPE, taper->disk, NULL, 0, NULL);
+ } else if (result_tape_action & TAPE_ACTION_NO_NEW_TAPE) {
+ taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+ taper_cmd(NO_NEW_TAPE, taper->disk, why_no_new_tape, 0, NULL);
+ taper->state |= TAPER_STATE_DONE;
+ start_degraded_mode(&runq);
+ } else if (result_tape_action & TAPE_ACTION_MOVE) {
+ taper_t *taper1 = idle_taper();
+ if (taper1) {
+ taper->state &= ~TAPER_STATE_TAPE_REQUESTED;
+ taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+ taper_cmd(TAKE_SCRIBE_FROM, taper->disk, taper1->name, 0 , NULL);
+ taper1->state = TAPER_STATE_DEFAULT;
+ taper->state |= TAPER_STATE_TAPE_STARTED;
+ taper->left = taper1->left;
+ if (last_started_taper == taper1) {
+ last_started_taper = taper;
+ }
+ }
+ }
- if(!degraded_mode && !taper_busy && !empty(tapeq)) {
+ if (!degraded_mode &&
+ taper->state & TAPER_STATE_IDLE &&
+ !empty(tapeq) &&
+ (result_tape_action & TAPE_ACTION_START_A_FLUSH ||
+ result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT)) {
+
+ int taperalgo = conf_taperalgo;
+ if (result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+ if (taperalgo == ALGO_FIRST)
+ taperalgo = ALGO_FIRSTFIT;
+ else if (taperalgo == ALGO_LARGEST)
+ taperalgo = ALGO_LARGESTFIT;
+ else if (taperalgo == ALGO_SMALLEST)
+ taperalgo = ALGO_SMALLESTFIT;
+ else if (taperalgo == ALGO_LAST)
+ taperalgo = ALGO_LASTFIT;
+ }
+
+ extra_tapes_size = tape_length * (off_t)(conf_runtapes - current_tape);
+ for (taper1 = tapetable; taper1 < tapetable + conf_taper_parallel_write;
+ taper1++) {
+ if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+ extra_tapes_size += taper1->left;
+ }
+ dp = taper1->disk;
+ if (dp) {
+ extra_tapes_size -= (sched(dp)->act_size - taper1->written);
+ }
+ }
+
+ if (taper->state & TAPER_STATE_TAPE_STARTED) {
+ taper_left = taper->left;
+ } else {
+ taper_left = tape_length;
+ }
+ dp = NULL;
datestamp = sched(tapeq.head)->datestamp;
- switch(conf_taperalgo) {
+ switch(taperalgo) {
case ALGO_FIRST:
dp = dequeue_disk(&tapeq);
break;
- case ALGO_FIRSTFIT:
+ case ALGO_FIRSTFIT:
fit = tapeq.head;
while (fit != NULL) {
- if(sched(fit)->act_size <= tape_left &&
- strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+ if (sched(fit)->act_size <=
+ (fit->splitsize ? extra_tapes_size : taper_left) &&
+ strcmp(sched(fit)->datestamp, datestamp) <= 0) {
dp = fit;
fit = NULL;
}
}
if(dp) remove_disk(&tapeq, dp);
break;
- case ALGO_LARGEST:
+ case ALGO_LARGEST:
fit = dp = tapeq.head;
while (fit != NULL) {
if(sched(fit)->act_size > sched(dp)->act_size &&
}
if(dp) remove_disk(&tapeq, dp);
break;
- case ALGO_LARGESTFIT:
+ case ALGO_LARGESTFIT:
fit = tapeq.head;
while (fit != NULL) {
- if(sched(fit)->act_size <= tape_left &&
+ if(sched(fit)->act_size <=
+ (fit->splitsize ? extra_tapes_size : taper_left) &&
(!dp || sched(fit)->act_size > sched(dp)->act_size) &&
strcmp(sched(fit)->datestamp, datestamp) <= 0) {
dp = fit;
}
if(dp) remove_disk(&tapeq, dp);
break;
- case ALGO_SMALLEST:
+ case ALGO_SMALLEST:
+ fit = dp = tapeq.head;
+ while (fit != NULL) {
+ if (sched(fit)->act_size < sched(dp)->act_size &&
+ strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+ dp = fit;
+ }
+ fit = fit->next;
+ }
+ if(dp) remove_disk(&tapeq, dp);
+ break;
+ case ALGO_SMALLESTFIT:
+ fit = dp = tapeq.head;
+ while (fit != NULL) {
+ if (sched(fit)->act_size <=
+ (fit->splitsize ? extra_tapes_size : taper_left) &&
+ (!dp || sched(fit)->act_size < sched(dp)->act_size) &&
+ strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+ dp = fit;
+ }
+ fit = fit->next;
+ }
+ if(dp) remove_disk(&tapeq, dp);
break;
case ALGO_LAST:
dp = tapeq.tail;
remove_disk(&tapeq, dp);
break;
+ case ALGO_LASTFIT:
+ fit = tapeq.tail;
+ while (fit != NULL) {
+ if (sched(fit)->act_size <=
+ (fit->splitsize ? extra_tapes_size : taper_left) &&
+ (!dp || sched(fit)->act_size < sched(dp)->act_size) &&
+ strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+ dp = fit;
+ }
+ fit = fit->prev;
+ }
+ if(dp) remove_disk(&tapeq, dp);
+ break;
}
- if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
- if(conf_taperalgo != ALGO_SMALLEST) {
- fprintf(stderr,
- "driver: startaflush: Using SMALLEST because nothing fit\n");
+ if (dp) {
+ taper->disk = dp;
+ taper->dumper = NULL;
+ amfree(taper->input_error);
+ amfree(taper->tape_error);
+ taper->result = LAST_TOK;
+ taper->sendresult = 0;
+ amfree(taper->first_label);
+ taper->written = 0;
+ taper->state &= ~TAPER_STATE_IDLE;
+ taper->state |= TAPER_STATE_FILE_TO_TAPE;
+ taper->dumper = NULL;
+ qname = quote_string(dp->name);
+ if (taper_nb_wait_reply == 0) {
+ taper_ev_read = event_register(taper_fd, EV_READFD,
+ handle_taper_result, NULL);
}
- fit = dp = tapeq.head;
- while (fit != NULL) {
- if(sched(fit)->act_size < sched(dp)->act_size &&
- strcmp(sched(fit)->datestamp, datestamp) <= 0) {
- dp = fit;
- }
- fit = fit->next;
- }
- if(dp) remove_disk(&tapeq, dp);
- }
- taper_disk = dp;
- taper_busy = 1;
- taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
- sched(dp)->datestamp);
- fprintf(stderr,"driver: startaflush: %s %s %s %ld %ld\n",
- taperalgo2str(conf_taperalgo), dp->host->hostname,
- dp->name, sched(taper_disk)->act_size, tape_left);
- if(sched(dp)->act_size <= tape_left)
- tape_left -= sched(dp)->act_size;
- else
- tape_left = 0;
+ taper_nb_wait_reply++;
+ sched(dp)->taper = taper;
+ taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
+ sched(dp)->datestamp);
+ g_fprintf(stderr,_("driver: startaflush: %s %s %s %lld %lld\n"),
+ taperalgo2str(taperalgo), dp->host->hostname, qname,
+ (long long)sched(taper->disk)->act_size,
+ (long long)taper->left);
+ amfree(qname);
+ }
+ short_dump_state();
}
}
-
-int client_constrained(dp)
-disk_t *dp;
+static int
+client_constrained(
+ disk_t * dp)
{
disk_t *dp2;
return 0;
}
-int start_some_dumps(rq)
-disklist_t *rq;
+static void
+allow_dump_dle(
+ disk_t *diskp,
+ taper_t *taper,
+ char dumptype,
+ disklist_t *rq,
+ const time_t now,
+ int dumper_to_holding,
+ int *cur_idle,
+ disk_t **delayed_diskp,
+ disk_t **diskp_accept,
+ assignedhd_t ***holdp_accept,
+ off_t extra_tapes_size)
{
- int total, cur_idle;
- disk_t *diskp, *diskp_accept;
- dumper_t *dumper;
+ assignedhd_t **holdp=NULL;
+
+ if (diskp->host->start_t > now) {
+ *cur_idle = max(*cur_idle, IDLE_START_WAIT);
+ if (*delayed_diskp == NULL || sleep_time > diskp->host->start_t) {
+ *delayed_diskp = diskp;
+ sleep_time = diskp->host->start_t;
+ }
+ } else if(diskp->start_t > now) {
+ *cur_idle = max(*cur_idle, IDLE_START_WAIT);
+ if (*delayed_diskp == NULL || sleep_time > diskp->start_t) {
+ *delayed_diskp = diskp;
+ sleep_time = diskp->start_t;
+ }
+ } else if (diskp->host->netif->curusage > 0 &&
+ sched(diskp)->est_kps > free_kps(diskp->host->netif)) {
+ *cur_idle = max(*cur_idle, IDLE_NO_BANDWIDTH);
+ } else if (!taper && sched(diskp)->no_space) {
+ *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+ } else if (!taper && diskp->to_holdingdisk == HOLD_NEVER) {
+ *cur_idle = max(*cur_idle, IDLE_NO_HOLD);
+ } else if (extra_tapes_size && sched(diskp)->est_size > extra_tapes_size) {
+ *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+ /* no tape space */
+ } else if (!taper && (holdp =
+ find_diskspace(sched(diskp)->est_size, cur_idle, NULL)) == NULL) {
+ *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+ if (empty(tapeq) && dumper_to_holding == 0 && rq != &directq) {
+ remove_disk(rq, diskp);
+ if (diskp->to_holdingdisk != HOLD_REQUIRED) {
+ enqueue_disk(&directq, diskp);
+ diskp->to_holdingdisk = HOLD_NEVER;
+ }
+ }
+ } else if (client_constrained(diskp)) {
+ free_assignedhd(holdp);
+ *cur_idle = max(*cur_idle, IDLE_CLIENT_CONSTRAINED);
+ } else {
+
+ /* disk fits, dump it */
+ int accept = !*diskp_accept;
+ if(!accept) {
+ switch(dumptype) {
+ case 's': accept = (sched(diskp)->est_size < sched(*diskp_accept)->est_size);
+ break;
+ case 'S': accept = (sched(diskp)->est_size > sched(*diskp_accept)->est_size);
+ break;
+ case 't': accept = (sched(diskp)->est_time < sched(*diskp_accept)->est_time);
+ break;
+ case 'T': accept = (sched(diskp)->est_time > sched(*diskp_accept)->est_time);
+ break;
+ case 'b': accept = (sched(diskp)->est_kps < sched(*diskp_accept)->est_kps);
+ break;
+ case 'B': accept = (sched(diskp)->est_kps > sched(*diskp_accept)->est_kps);
+ break;
+ default: log_add(L_WARNING, _("Unknown dumporder character \'%c\', using 's'.\n"),
+ dumptype);
+ accept = (sched(diskp)->est_size < sched(*diskp_accept)->est_size);
+ break;
+ }
+ }
+ if(accept) {
+ if( !*diskp_accept || !degraded_mode || diskp->priority >= (*diskp_accept)->priority) {
+ if(*holdp_accept) free_assignedhd(*holdp_accept);
+ *diskp_accept = diskp;
+ *holdp_accept = holdp;
+ }
+ else {
+ free_assignedhd(holdp);
+ }
+ }
+ else {
+ free_assignedhd(holdp);
+ }
+ }
+}
+
+static void
+start_some_dumps(
+ disklist_t *rq)
+{
+ const time_t now = time(NULL);
+ int cur_idle;
+ disk_t *diskp, *delayed_diskp, *diskp_accept;
+ disk_t *dp;
assignedhd_t **holdp=NULL, **holdp_accept;
- time_t now = time(NULL);
+ cmd_t cmd;
+ int result_argc;
+ char **result_argv;
+ chunker_t *chunker;
+ dumper_t *dumper;
+ taper_t *taper;
+ char dumptype;
+ char *dumporder;
+ int dumper_to_holding = 0;
+
+ /* don't start any actual dumps until the taper is started */
+ if (!taper_started) return;
- total = 0;
idle_reason = IDLE_NO_DUMPERS;
- sleep_time.tv_sec = SLEEP_MAX;
- sleep_time.tv_usec = 0;
- any_delayed_disk = 0;
+ sleep_time = 0;
- if(rq->head == NULL) {
- idle_reason = 0;
- return 0;
+ if(dumpers_ev_time != NULL) {
+ event_release(dumpers_ev_time);
+ dumpers_ev_time = NULL;
}
- /*
- * A potential problem with starting from the bottom of the dump time
- * distribution is that a slave host will have both one of the shortest
- * and one of the longest disks, so starting its shortest disk first will
- * tie up the host and eliminate its longest disk from consideration the
- * first pass through. This could cause a big delay in starting that long
- * disk, which could drag out the whole night's dumps.
- *
- * While starting from the top of the dump time distribution solves the
- * above problem, this turns out to be a bad idea, because the big dumps
- * will almost certainly pack the holding disk completely, leaving no
- * room for even one small dump to start. This ends up shutting out the
- * small-end dumpers completely (they stay idle).
- *
- * The introduction of multiple simultaneous dumps to one host alleviates
- * the biggest&smallest dumps problem: both can be started at the
- * beginning.
- */
- for(dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
- if(dumper->busy || dumper->down) continue;
- /* found an idle dumper, now find a disk for it */
- diskp = rq->head;
- diskp_accept = NULL;
- holdp_accept = NULL;
+ for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
+ if (dumper->busy && dumper->dp->to_holdingdisk != HOLD_NEVER) {
+ dumper_to_holding++;
+ }
+ }
+ for (dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
- if(idle_reason == IDLE_NO_DUMPERS)
- idle_reason = NOT_IDLE;
+ if( dumper->busy || dumper->down) {
+ continue;
+ }
- cur_idle = NOT_IDLE;
+ if (dumper->ev_read != NULL) {
+ event_release(dumper->ev_read);
+ dumper->ev_read = NULL;
+ }
- while(diskp) {
- assert(diskp->host != NULL && sched(diskp) != NULL);
+ /*
+ * A potential problem with starting from the bottom of the dump time
+ * distribution is that a slave host will have both one of the shortest
+ * and one of the longest disks, so starting its shortest disk first will
+ * tie up the host and eliminate its longest disk from consideration the
+ * first pass through. This could cause a big delay in starting that long
+ * disk, which could drag out the whole night's dumps.
+ *
+ * While starting from the top of the dump time distribution solves the
+ * above problem, this turns out to be a bad idea, because the big dumps
+ * will almost certainly pack the holding disk completely, leaving no
+ * room for even one small dump to start. This ends up shutting out the
+ * small-end dumpers completely (they stay idle).
+ *
+ * The introduction of multiple simultaneous dumps to one host alleviates
+ * the biggest&smallest dumps problem: both can be started at the
+ * beginning.
+ */
- /* round estimate to next multiple of DISK_BLOCK_KB */
- sched(diskp)->est_size = am_round(sched(diskp)->est_size,
- DISK_BLOCK_KB);
-
- if(diskp->host->start_t > now) {
- cur_idle = max(cur_idle, IDLE_START_WAIT);
- sleep_time.tv_sec = min(diskp->host->start_t - now,
- sleep_time.tv_sec);
- any_delayed_disk = 1;
- }
- else if(diskp->start_t > now) {
- cur_idle = max(cur_idle, IDLE_START_WAIT);
- sleep_time.tv_sec = min(diskp->start_t - now,
- sleep_time.tv_sec);
- any_delayed_disk = 1;
- }
- else if(diskp->host->netif->curusage > 0 &&
- sched(diskp)->est_kps > free_kps(diskp->host->netif))
- cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
- else if(sched(diskp)->no_space)
- cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
- else if((holdp = find_diskspace(sched(diskp)->est_size,&cur_idle,NULL)) == NULL)
- cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
- else if(diskp->no_hold) {
- free_assignedhd(holdp);
- cur_idle = max(cur_idle, IDLE_NO_HOLD);
- } else if(client_constrained(diskp)) {
- free_assignedhd(holdp);
- cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
- } else {
+ diskp_accept = NULL;
+ holdp_accept = NULL;
+ delayed_diskp = NULL;
- /* disk fits, dump it */
- int accept = !diskp_accept;
- if(!accept) {
- char dumptype;
- char *dumporder = getconf_str(CNF_DUMPORDER);
- if(strlen(dumporder) <= (dumper-dmptable)) {
- if(dumper-dmptable < 3)
- dumptype = 't';
- else
- dumptype = 'T';
- }
- else {
- dumptype = dumporder[dumper-dmptable];
- }
- switch(dumptype) {
- case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
- break;
- case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
- break;
- case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
- break;
- case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
- break;
- case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
- break;
- case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
- break;
- default: log_add(L_WARNING, "Unknown dumporder character \'%c\', using 's'.\n",
- dumptype);
- accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
- break;
+ cur_idle = NOT_IDLE;
+
+ dumporder = getconf_str(CNF_DUMPORDER);
+ if(strlen(dumporder) > (size_t)(dumper-dmptable)) {
+ dumptype = dumporder[dumper-dmptable];
+ }
+ else {
+ if(dumper-dmptable < 3)
+ dumptype = 't';
+ else
+ dumptype = 'T';
+ }
+
+ diskp = NULL;
+ taper = NULL;
+ if (!empty(directq)) {
+ taper = idle_taper();
+ if (taper) {
+ TapeAction result_tape_action;
+ char *why_no_new_tape = NULL;
+ result_tape_action = tape_action(taper, &why_no_new_tape);
+ if (result_tape_action & TAPE_ACTION_START_A_FLUSH ||
+ result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+ off_t extra_tapes_size = 0;
+ taper_t *taper1;
+
+ if (result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+ extra_tapes_size = tape_length *
+ (off_t)(conf_runtapes - current_tape);
+ for (taper1 = tapetable;
+ taper1 < tapetable + conf_taper_parallel_write;
+ taper1++) {
+ if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+ extra_tapes_size += taper1->left;
+ }
+ dp = taper1->disk;
+ if (dp) {
+ extra_tapes_size -= (sched(dp)->est_size -
+ taper1->written);
+ }
+ }
}
- }
- if(accept) {
- if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
- if(holdp_accept) free_assignedhd(holdp_accept);
- diskp_accept = diskp;
- holdp_accept = holdp;
+
+ for (diskp = directq.head; diskp != NULL;
+ diskp = diskp->next) {
+ allow_dump_dle(diskp, taper, dumptype, &directq, now,
+ dumper_to_holding, &cur_idle,
+ &delayed_diskp, &diskp_accept,
+ &holdp_accept, extra_tapes_size);
}
- else {
- free_assignedhd(holdp);
+ if (diskp_accept) {
+ diskp = diskp_accept;
+ holdp = holdp_accept;
+ } else {
+ taper = NULL;
}
- }
- else {
- free_assignedhd(holdp);
+ } else {
+ taper = NULL;
}
}
- diskp = diskp->next;
}
- diskp = diskp_accept;
- holdp = holdp_accept;
- if(diskp) {
- cur_idle = NOT_IDLE;
- sched(diskp)->act_size = 0;
+ if (diskp == NULL) {
+ for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
+ assert(diskp->host != NULL && sched(diskp) != NULL);
+
+ allow_dump_dle(diskp, NULL, dumptype, rq, now,
+ dumper_to_holding, &cur_idle, &delayed_diskp,
+ &diskp_accept, &holdp_accept, 0);
+ }
+ diskp = diskp_accept;
+ holdp = holdp_accept;
+ }
+
+ idle_reason = max(idle_reason, cur_idle);
+ if (diskp == NULL && idle_reason == IDLE_NO_DISKSPACE) {
+ /* continue flush waiting for new tape */
+ startaflush();
+ }
+
+ /*
+ * If we have no disk at this point, and there are disks that
+ * are delayed, then schedule a time event to call this dumper
+ * with the disk with the shortest delay.
+ */
+ if (diskp == NULL && delayed_diskp != NULL) {
+ assert(sleep_time > now);
+ sleep_time -= now;
+ dumpers_ev_time = event_register((event_id_t)sleep_time, EV_TIME,
+ handle_dumpers_time, &runq);
+ return;
+ } else if (diskp != NULL && taper == NULL) {
+ sched(diskp)->act_size = (off_t)0;
allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
amfree(holdp);
- diskp->host->inprogress += 1; /* host is now busy */
+ sched(diskp)->destname = newstralloc(sched(diskp)->destname,
+ sched(diskp)->holdp[0]->destname);
+ diskp->host->inprogress++; /* host is now busy */
diskp->inprogress = 1;
sched(diskp)->dumper = dumper;
- sched(diskp)->timestamp = time((time_t *)0);
+ sched(diskp)->timestamp = now;
+ amfree(diskp->dataport_list);
dumper->busy = 1; /* dumper is now busy */
dumper->dp = diskp; /* link disk to dumper */
- total++;
remove_disk(rq, diskp); /* take it off the run queue */
- dumper_cmd(dumper, FILE_DUMP, diskp);
- diskp->host->start_t = time(NULL) + 15;
+
+ sched(diskp)->origsize = (off_t)-1;
+ sched(diskp)->dumpsize = (off_t)-1;
+ sched(diskp)->dumptime = (time_t)0;
+ sched(diskp)->tapetime = (time_t)0;
+ chunker = dumper->chunker = &chktable[dumper - dmptable];
+ chunker->result = LAST_TOK;
+ dumper->result = LAST_TOK;
+ startup_chunk_process(chunker,chunker_program);
+ chunker_cmd(chunker, START, NULL, driver_timestamp);
+ chunker->dumper = dumper;
+ chunker_cmd(chunker, PORT_WRITE, diskp, NULL);
+ cmd = getresult(chunker->fd, 1, &result_argc, &result_argv);
+ if(cmd != PORT) {
+ assignedhd_t **h=NULL;
+ int activehd;
+ char *qname = quote_string(diskp->name);
+
+ g_printf(_("driver: did not get PORT from %s for %s:%s\n"),
+ chunker->name, diskp->host->hostname, qname);
+ amfree(qname);
+ fflush(stdout);
+
+ deallocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
+ h = sched(diskp)->holdp;
+ activehd = sched(diskp)->activehd;
+ h[activehd]->used = 0;
+ h[activehd]->disk->allocated_dumpers--;
+ adjust_diskspace(diskp, DONE);
+ delete_diskspace(diskp);
+ diskp->host->inprogress--;
+ diskp->inprogress = 0;
+ sched(diskp)->dumper = NULL;
+ dumper->busy = 0;
+ dumper->dp = NULL;
+ sched(diskp)->dump_attempted++;
+ free_serial_dp(diskp);
+ if(sched(diskp)->dump_attempted < 2)
+ enqueue_disk(rq, diskp);
+ }
+ else {
+ dumper->ev_read = event_register((event_id_t)dumper->fd, EV_READFD,
+ handle_dumper_result, dumper);
+ chunker->ev_read = event_register((event_id_t)chunker->fd, EV_READFD,
+ handle_chunker_result, chunker);
+ dumper->output_port = atoi(result_argv[1]);
+ amfree(diskp->dataport_list);
+ diskp->dataport_list = stralloc(result_argv[2]);
+
+ if (diskp->host->pre_script == 0) {
+ for (dp=diskp->host->disks; dp != NULL; dp = dp->hostnext) {
+ run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+ get_config_name(), dp, -1);
+ }
+ diskp->host->pre_script = 1;
+ }
+ run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+ get_config_name(), diskp,
+ sched(diskp)->level);
+ dumper_cmd(dumper, PORT_DUMP, diskp, NULL);
+ }
+ diskp->host->start_t = now + 15;
+
+ if (result_argv)
+ g_strfreev(result_argv);
+ short_dump_state();
+ } else if (diskp != NULL && taper != NULL) { /* dump to tape */
+ sched(diskp)->act_size = (off_t)0;
+ allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
+ diskp->host->inprogress++; /* host is now busy */
+ diskp->inprogress = 1;
+ sched(diskp)->dumper = dumper;
+ sched(diskp)->taper = taper;
+ sched(diskp)->timestamp = now;
+ dumper->chunker = NULL;
+ amfree(diskp->dataport_list);
+
+ dumper->busy = 1; /* dumper is now busy */
+ dumper->dp = diskp; /* link disk to dumper */
+ remove_disk(&directq, diskp); /* take it off the direct queue */
+
+ sched(diskp)->origsize = (off_t)-1;
+ sched(diskp)->dumpsize = (off_t)-1;
+ sched(diskp)->dumptime = (time_t)0;
+ sched(diskp)->tapetime = (time_t)0;
+ dumper->result = LAST_TOK;
+ taper->result = LAST_TOK;
+ taper->input_error = NULL;
+ taper->tape_error = NULL;
+ taper->disk = diskp;
+ taper->first_label = NULL;
+ taper->written = 0;
+ taper->dumper = dumper;
+ taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+ taper->state &= ~TAPER_STATE_IDLE;
+ if (taper_nb_wait_reply == 0) {
+ taper_ev_read = event_register(taper_fd, EV_READFD,
+ handle_taper_result, NULL);
+ }
+
+ taper_nb_wait_reply++;
+ taper_cmd(PORT_WRITE, diskp, NULL, sched(diskp)->level,
+ sched(diskp)->datestamp);
+ diskp->host->start_t = now + 15;
+
+ short_dump_state();
}
- idle_reason = max(idle_reason, cur_idle);
}
- return total;
}
-int sort_by_priority_reversed(a, b)
-disk_t *a, *b;
-{
- if(sched(b)->priority - sched(a)->priority != 0)
- return sched(b)->priority - sched(a)->priority;
- else
- return sort_by_time(a, b);
-}
+/*
+ * This gets called when a dumper is delayed for some reason. It may
+ * be because a disk has a delayed start, or amanda is constrained
+ * by network or disk limits.
+ */
-int sort_by_time(a, b)
-disk_t *a, *b;
+static void
+handle_dumpers_time(
+ void * cookie)
{
- long diff;
-
- if ((diff = sched(a)->est_time - sched(b)->est_time) < 0) {
- return -1;
- } else if (diff > 0) {
- return 1;
- } else {
- return 0;
- }
+ disklist_t *runq = cookie;
+ event_release(dumpers_ev_time);
+ dumpers_ev_time = NULL;
+ start_some_dumps(runq);
}
-void dump_schedule(qp, str)
-disklist_t *qp;
-char *str;
+static void
+dump_schedule(
+ disklist_t *qp,
+ char * str)
{
disk_t *dp;
+ char *qname;
- printf("dump of driver schedule %s:\n--------\n", str);
+ g_printf(_("dump of driver schedule %s:\n--------\n"), str);
for(dp = qp->head; dp != NULL; dp = dp->next) {
- printf(" %-20s %-25s lv %d t %5ld s %8lu p %d\n",
- dp->host->hostname, dp->name, sched(dp)->level,
- sched(dp)->est_time, sched(dp)->est_size, sched(dp)->priority);
+ qname = quote_string(dp->name);
+ g_printf(" %-20s %-25s lv %d t %5lu s %lld p %d\n",
+ dp->host->hostname, qname, sched(dp)->level,
+ sched(dp)->est_time,
+ (long long)sched(dp)->est_size, sched(dp)->priority);
+ amfree(qname);
}
- printf("--------\n");
+ g_printf("--------\n");
}
-
-void start_degraded_mode(queuep)
-disklist_t *queuep;
+static void
+start_degraded_mode(
+ /*@keep@*/ disklist_t *queuep)
{
disk_t *dp;
disklist_t newq;
- unsigned long est_full_size;
+ off_t est_full_size;
+ char *qname;
newq.head = newq.tail = 0;
- dump_schedule(queuep, "before start degraded mode");
+ dump_schedule(queuep, _("before start degraded mode"));
- est_full_size = 0;
+ est_full_size = (off_t)0;
while(!empty(*queuep)) {
dp = dequeue_disk(queuep);
+ qname = quote_string(dp->name);
if(sched(dp)->level != 0)
/* go ahead and do the disk as-is */
- insert_disk(&newq, dp, sort_by_priority_reversed);
+ enqueue_disk(&newq, dp);
else {
if (reserved_space + est_full_size + sched(dp)->est_size
<= total_disksize) {
- insert_disk(&newq, dp, sort_by_priority_reversed);
+ enqueue_disk(&newq, dp);
est_full_size += sched(dp)->est_size;
}
else if(sched(dp)->degr_level != -1) {
sched(dp)->level = sched(dp)->degr_level;
sched(dp)->dumpdate = sched(dp)->degr_dumpdate;
- sched(dp)->est_size = sched(dp)->degr_size;
+ sched(dp)->est_nsize = sched(dp)->degr_nsize;
+ sched(dp)->est_csize = sched(dp)->degr_csize;
sched(dp)->est_time = sched(dp)->degr_time;
sched(dp)->est_kps = sched(dp)->degr_kps;
- insert_disk(&newq, dp, sort_by_priority_reversed);
+ enqueue_disk(&newq, dp);
}
else {
- log_add(L_FAIL, "%s %s %s %d [can't switch to incremental dump]",
- dp->host->hostname, dp->name,
- sched(dp)->datestamp, sched(dp)->level);
+ log_add(L_FAIL, "%s %s %s %d [%s]",
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, sched(dp)->degr_mesg);
}
}
+ amfree(qname);
}
- *queuep = newq;
+ /*@i@*/ *queuep = newq;
degraded_mode = 1;
- dump_schedule(queuep, "after start degraded mode");
+ dump_schedule(queuep, _("after start degraded mode"));
}
-void continue_dumps()
+
+static void
+continue_port_dumps(void)
{
-disk_t *dp, *ndp;
-assignedhd_t **h;
-int active_dumpers=0, busy_dumpers=0, i;
-dumper_t *dumper;
+ disk_t *dp, *ndp;
+ assignedhd_t **h;
+ int active_dumpers=0, busy_dumpers=0, i;
+ dumper_t *dumper;
/* First we try to grant diskspace to some dumps waiting for it. */
for( dp = roomq.head; dp; dp = ndp ) {
ndp = dp->next;
/* find last holdingdisk used by this dump */
- for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ );
+ for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ ) {
+ (void)h; /* Quiet lint */
+ }
/* find more space */
- h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size, &active_dumpers, h[i] );
+ h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
+ &active_dumpers, h[i] );
if( h ) {
for(dumper = dmptable; dumper < dmptable + inparallel &&
- dumper->dp != dp; dumper++);
+ dumper->dp != dp; dumper++) {
+ (void)dp; /* Quiet lint */
+ }
assert( dumper < dmptable + inparallel );
sched(dp)->activehd = assign_holdingdisk( h, dp );
- dumper_cmd( dumper, CONTINUE, dp );
+ chunker_cmd( dumper->chunker, CONTINUE, dp, NULL );
amfree(h);
remove_disk( &roomq, dp );
}
* a) diskspace has been allocated for other dumps which are
* still running or already being written to tape
* b) all other dumps have been suspended due to lack of diskspace
- * c) this dump doesn't fit on all the holding disks
* Case a) is not a problem. We just wait for the diskspace to
* be freed by moving the current disk to a queue.
* If case b) occurs, we have a deadlock situation. We select
* a dump from the queue to be aborted and abort it. It will
- * be retried later dumping to disk.
- * If case c) is detected, the dump is aborted. Next time
- * it will be dumped directly to tape. Actually, case c is a special
- * manifestation of case b) where only one dumper is busy.
+ * be retried directly to tape.
*/
- for( dp=NULL, dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
+ for(dp=NULL, dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
if( dumper->busy ) {
busy_dumpers++;
if( !find_disk(&roomq, dumper->dp) ) {
active_dumpers++;
- } else if( !dp || sched(dp)->est_size > sched(dumper->dp)->est_size ) {
+ } else if( !dp ||
+ sched(dp)->est_size > sched(dumper->dp)->est_size ) {
dp = dumper->dp;
}
}
}
- if( !active_dumpers && busy_dumpers > 0 &&
- ((!taper_busy && empty(tapeq)) || degraded_mode) &&
- pending_aborts == 0 ) { /* not case a */
- if( busy_dumpers == 1 ) { /* case c */
- sched(dp)->no_space = 1;
- }
- /* case b */
+ if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) &&
+ ((all_taper_idle() && empty(tapeq)) || degraded_mode) &&
+ pending_aborts == 0 ) { /* case b */
+ sched(dp)->no_space = 1;
/* At this time, dp points to the dump with the smallest est_size.
* We abort that dump, hopefully not wasting too much time retrying it.
*/
remove_disk( &roomq, dp );
- dumper_cmd( sched(dp)->dumper, ABORT, NULL );
+ chunker_cmd(sched(dp)->dumper->chunker, ABORT, NULL, _("Not enough holding disk space"));
+ dumper_cmd( sched(dp)->dumper, ABORT, NULL, _("Not enough holding disk space"));
pending_aborts++;
}
}
-void handle_taper_result()
+
+static void
+handle_taper_result(
+ void *cookie G_GNUC_UNUSED)
{
- disk_t *dp;
- int filenum;
+ disk_t *dp = NULL, *dp1;
+ dumper_t *dumper;
cmd_t cmd;
int result_argc;
- char *result_argv[MAX_ARGS+1];
+ char **result_argv;
+ char *qname, *q;
+ char *s;
+ taper_t *taper = NULL;
+ taper_t *taper1;
+ int i;
+ off_t partsize;
- cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
+ assert(cookie == NULL);
- switch(cmd) {
+ do {
- case DONE: /* DONE <handle> <label> <tape file> <err mess> */
- if(result_argc != 5) {
- error("error: [taper DONE result_argc != 5: %d", result_argc);
- }
+ short_dump_state();
+ taper = NULL;
- dp = serial2disk(result_argv[2]);
- free_serial(result_argv[2]);
+ cmd = getresult(taper_fd, 1, &result_argc, &result_argv);
- filenum = atoi(result_argv[4]);
- update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
+ switch(cmd) {
- delete_diskspace(dp);
+ case TAPER_OK:
+ if(result_argc != 2) {
+ error(_("error: [taper FAILED result_argc != 2: %d"), result_argc);
+ /*NOTREACHED*/
+ }
- printf("driver: finished-cmd time %s taper wrote %s:%s\n",
- walltime_str(curclock()), dp->host->hostname, dp->name);
- fflush(stdout);
+ taper = NULL;
+ taper_started = 1;
+ for (i=0; i < conf_taper_parallel_write; i++) {
+ if (strcmp(tapetable[i].name, result_argv[1]) == 0) {
+ taper= &tapetable[i];
+ }
+ }
+ assert(taper != NULL);
+ taper->left = 0;
+ taper->state &= ~TAPER_STATE_INIT;
+ taper->state |= TAPER_STATE_RESERVATION;
+ taper->state |= TAPER_STATE_IDLE;
+ amfree(taper->first_label);
+ taper_nb_wait_reply--;
+ taper_nb_scan_volume--;
+ last_started_taper = taper;
+ if (taper_nb_wait_reply == 0) {
+ event_release(taper_ev_read);
+ taper_ev_read = NULL;
+ }
+ start_some_dumps(&runq);
+ startaflush();
+ break;
- amfree(sched(dp)->dumpdate);
- amfree(sched(dp)->degr_dumpdate);
- amfree(sched(dp)->datestamp);
- amfree(dp->up);
+ case FAILED: /* FAILED <handle> INPUT-* TAPE-* <input err mesg> <tape err mesg> */
+ if(result_argc != 6) {
+ error(_("error: [taper FAILED result_argc != 6: %d"), result_argc);
+ /*NOTREACHED*/
+ }
- taper_busy = 0;
- taper_disk = NULL;
- startaflush();
- continue_dumps(); /* continue with those dumps waiting for diskspace */
- break;
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ assert(dp == taper->disk);
+ if (!taper->dumper)
+ free_serial(result_argv[1]);
- case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
- if (result_argc < 2) {
- error("error [taper TRYAGAIN result_argc < 2: %d]", result_argc);
- }
- dp = serial2disk(result_argv[2]);
- free_serial(result_argv[2]);
- printf("driver: taper-tryagain time %s disk %s:%s\n",
- walltime_str(curclock()), dp->host->hostname, dp->name);
- fflush(stdout);
+ qname = quote_string(dp->name);
+ g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
+ walltime_str(curclock()), dp->host->hostname, qname);
+ fflush(stdout);
+
+ if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
+ taper->input_error = newstralloc(taper->input_error, result_argv[4]);
+ } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
+ taper->tape_error = newstralloc(taper->tape_error,
+ _("Taper protocol error"));
+ taper->result = FAILED;
+ log_add(L_FAIL, _("%s %s %s %d [%s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->tape_error);
+ amfree(qname);
+ break;
+ }
+ if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+ taper->state &= ~TAPER_STATE_TAPE_STARTED;
+ taper->tape_error = newstralloc(taper->tape_error, result_argv[5]);
+ } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
+ taper->state &= ~TAPER_STATE_TAPE_STARTED;
+ taper->tape_error = newstralloc(taper->tape_error,
+ _("Taper protocol error"));
+ taper->result = FAILED;
+ log_add(L_FAIL, _("%s %s %s %d [%s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->tape_error);
+ amfree(qname);
+ break;
+ }
- /* re-insert into taper queue */
+ amfree(qname);
+ taper->result = cmd;
- if(sched(dp)->attempted) {
- log_add(L_FAIL, "%s %s %d %s [too many taper retries]",
- dp->host->hostname, dp->name, sched(dp)->level,
- sched(dp)->datestamp);
- printf("driver: taper failed %s %s %s, too many taper retry\n", result_argv[2], dp->host->hostname, dp->name);
+ break;
+
+ case PARTIAL: /* PARTIAL <handle> INPUT-* TAPE-* <stat mess> <input err mesg> <tape err mesg>*/
+ case DONE: /* DONE <handle> INPUT-GOOD TAPE-GOOD <stat mess> <input err mesg> <tape err mesg> */
+ if(result_argc != 7) {
+ error(_("error: [taper PARTIAL result_argc != 7: %d"), result_argc);
+ /*NOTREACHED*/
+ }
+
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ assert(dp == taper->disk);
+ if (!taper->dumper)
+ free_serial(result_argv[1]);
+
+ qname = quote_string(dp->name);
+ g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
+ walltime_str(curclock()), dp->host->hostname, qname);
+ fflush(stdout);
+
+ if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
+ taper->input_error = newstralloc(taper->input_error, result_argv[5]);
+ } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
+ taper->tape_error = newstralloc(taper->tape_error,
+ _("Taper protocol error"));
+ taper->result = FAILED;
+ log_add(L_FAIL, _("%s %s %s %d [%s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->tape_error);
+ amfree(qname);
+ break;
+ }
+ if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+ taper->state &= ~TAPER_STATE_TAPE_STARTED;
+ taper->tape_error = newstralloc(taper->tape_error, result_argv[6]);
+ } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
+ taper->state &= ~TAPER_STATE_TAPE_STARTED;
+ taper->tape_error = newstralloc(taper->tape_error,
+ _("Taper protocol error"));
+ taper->result = FAILED;
+ log_add(L_FAIL, _("%s %s %s %d [%s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->tape_error);
+ amfree(qname);
+ break;
+ }
+
+ s = strstr(result_argv[4], " kb ");
+ if (s) {
+ s += 4;
+ sched(dp)->dumpsize = atol(s);
+ }
+
+ taper->result = cmd;
+ amfree(qname);
+
+ break;
+
+ case PARTDONE: /* PARTDONE <handle> <label> <fileno> <kbytes> <stat> */
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ assert(dp == taper->disk);
+ if (result_argc != 6) {
+ error(_("error [taper PARTDONE result_argc != 6: %d]"),
+ result_argc);
+ /*NOTREACHED*/
+ }
+ if (!taper->first_label) {
+ amfree(taper->first_label);
+ taper->first_label = stralloc(result_argv[2]);
+ taper->first_fileno = OFF_T_ATOI(result_argv[3]);
+ }
+ taper->written += OFF_T_ATOI(result_argv[4]);
+ if (taper->written > sched(taper->disk)->act_size)
+ sched(taper->disk)->act_size = taper->written;
+
+ partsize = 0;
+ s = strstr(result_argv[5], " kb ");
+ if (s) {
+ s += 4;
+ partsize = atol(s);
+ }
+ taper->left -= partsize;
+
+ break;
+
+ case REQUEST_NEW_TAPE: /* REQUEST-NEW-TAPE <handle> */
+ if (result_argc != 2) {
+ error(_("error [taper REQUEST_NEW_TAPE result_argc != 2: %d]"),
+ result_argc);
+ /*NOTREACHED*/
+ }
+
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ taper->state &= ~TAPER_STATE_TAPE_STARTED;
+ taper->state |= TAPER_STATE_TAPE_REQUESTED;
+
+ start_some_dumps(&runq);
+ startaflush();
+ break;
+
+ case NEW_TAPE: /* NEW-TAPE <handle> <label> */
+ if (result_argc != 3) {
+ error(_("error [taper NEW_TAPE result_argc != 3: %d]"),
+ result_argc);
+ /*NOTREACHED*/
+ }
+
+ nb_sent_new_tape--;
+ taper_nb_scan_volume--;
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ /* Update our tape counter and reset taper->left */
+ current_tape++;
+ taper->left = tape_length;
+ taper->state &= ~TAPER_STATE_WAIT_NEW_TAPE;
+ taper->state |= TAPER_STATE_TAPE_STARTED;
+ last_started_taper = NULL;
+
+ /* start a new worker */
+ for (i = 0; i < conf_taper_parallel_write ; i++) {
+ taper1 = &tapetable[i];
+ if (need_degraded == 0 &&
+ taper1->state == TAPER_STATE_DEFAULT) {
+ taper1->state = TAPER_STATE_INIT;
+ if (taper_nb_wait_reply == 0) {
+ taper_ev_read = event_register(taper_fd, EV_READFD,
+ handle_taper_result, NULL);
+ }
+ taper_nb_wait_reply++;
+ taper_nb_scan_volume++;
+ taper_cmd(START_TAPER, NULL, taper1->name, 0,
+ driver_timestamp);
+ break;
+ }
+ }
+ break;
+
+ case NO_NEW_TAPE: /* NO-NEW-TAPE <handle> */
+ if (result_argc != 2) {
+ error(_("error [taper NO_NEW_TAPE result_argc != 2: %d]"),
+ result_argc);
+ /*NOTREACHED*/
+ }
+ nb_sent_new_tape--;
+ taper_nb_scan_volume--;
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ taper->state |= TAPER_STATE_DONE;
+ last_started_taper = NULL;
+ start_degraded_mode(&runq);
+ break;
+
+ case DUMPER_STATUS: /* DUMPER-STATUS <handle> */
+ if (result_argc != 2) {
+ error(_("error [taper DUMPER_STATUS result_argc != 2: %d]"),
+ result_argc);
+ /*NOTREACHED*/
+ }
+ dp = serial2disk(result_argv[1]);
+ taper = sched(dp)->taper;
+ if (taper->dumper->result == LAST_TOK) {
+ taper->sendresult = 1;
+ } else {
+ if( taper->dumper->result == DONE) {
+ taper_cmd(DONE, dp, NULL, 0, NULL);
+ } else {
+ taper_cmd(FAILED, dp, NULL, 0, NULL);
+ }
+ }
+ break;
+
+ case TAPE_ERROR: /* TAPE-ERROR <name> <err mess> */
+ taper_started = 1;
+ if (strcmp(result_argv[1], "SETUP") == 0) {
+ taper_nb_wait_reply = 0;
+ taper_nb_scan_volume = 0;
+ } else {
+ taper = taper_from_name(result_argv[1]);
+ taper->state = TAPER_STATE_DONE;
+ fflush(stdout);
+ q = quote_string(result_argv[2]);
+ log_add(L_WARNING, _("Taper error: %s"), q);
+ amfree(q);
+ if (taper) {
+ taper->tape_error = newstralloc(taper->tape_error,
+ result_argv[2]);
+ }
+
+ taper_nb_wait_reply--;
+ taper_nb_scan_volume--;
+ }
+ if (taper_nb_wait_reply == 0) {
+ event_release(taper_ev_read);
+ taper_ev_read = NULL;
+ }
+ need_degraded = 1;
+ if (schedule_done && !degraded_mode) {
+ start_degraded_mode(&runq);
+ }
+ start_some_dumps(&runq);
+ break;
+
+ case PORT: /* PORT <name> <handle> <port> <dataport_list> */
+ dp = serial2disk(result_argv[2]);
+ taper = sched(dp)->taper;
+ dumper = sched(dp)->dumper;
+ dumper->output_port = atoi(result_argv[3]);
+ amfree(dp->dataport_list);
+ dp->dataport_list = stralloc(result_argv[4]);
+
+ amfree(taper->input_error);
+ amfree(taper->tape_error);
+ amfree(taper->first_label);
+ taper->written = 0;
+ taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+
+ if (dp->host->pre_script == 0) {
+ for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+ run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+ get_config_name(), dp1, -1);
+ }
+ dp->host->pre_script = 1;
+ }
+ run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+ get_config_name(), dp,
+ sched(dp)->level);
+ /* tell the dumper to dump to a port */
+ dumper_cmd(dumper, PORT_DUMP, dp, NULL);
+ dp->host->start_t = time(NULL) + 15;
+ amfree(dp->dataport_list);
+
+ taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+
+ dumper->ev_read = event_register(dumper->fd, EV_READFD,
+ handle_dumper_result, dumper);
+ break;
+
+ case BOGUS:
+ log_add(L_WARNING, _("Taper protocol error"));
+ /*
+ * Since we received a taper error, we can't send anything more
+ * to the taper. Go into degraded mode to try to get everthing
+ * onto disk. Later, these dumps can be flushed to a new tape.
+ * The tape queue is zapped so that it appears empty in future
+ * checks. If there are dumps waiting for diskspace to be freed,
+ * cancel one.
+ */
+ if(!nodump) {
+ log_add(L_WARNING,
+ _("going into degraded mode because of taper component error."));
+ }
+
+ for (taper = tapetable;
+ taper < tapetable + conf_taper_parallel_write;
+ taper++) {
+ if (taper && taper->disk && taper->result != LAST_TOK) {
+ taper->tape_error = newstralloc(taper->tape_error,"BOGUS");
+ taper->result = cmd;
+ if (taper->dumper) {
+ if (taper->dumper->result != LAST_TOK) {
+ // Dumper already returned it's result
+ dumper_taper_result(taper->disk);
+ }
+ } else {
+ file_taper_result(taper->disk);
+ }
+ }
+
+ }
+ taper = NULL;
+
+ if(taper_ev_read != NULL) {
+ event_release(taper_ev_read);
+ taper_ev_read = NULL;
+ taper_nb_wait_reply = 0;
+ }
+ start_degraded_mode(&runq);
+ tapeq.head = tapeq.tail = NULL;
+ aclose(taper_fd);
+
+ break;
+
+ default:
+ error(_("driver received unexpected token (%s) from taper"),
+ cmdstr[cmd]);
+ /*NOTREACHED*/
}
- else {
- sched(dp)->attempted++;
- headqueue_disk(&tapeq, dp);
+
+ g_strfreev(result_argv);
+
+ if (taper && taper->disk && taper->result != LAST_TOK) {
+ if(taper->dumper) {
+ if (taper->dumper->result != LAST_TOK) {
+ // Dumper already returned it's result
+ dumper_taper_result(taper->disk);
+ }
+ } else {
+ file_taper_result(taper->disk);
+ }
}
- tape_left = tape_length;
+ } while(areads_dataready(taper_fd));
+ startaflush();
+}
- /* run next thing from queue */
- taper_busy = 0;
- taper_disk = NULL;
- startaflush();
- continue_dumps(); /* continue with those dumps waiting for diskspace */
- break;
+static void
+file_taper_result(
+ disk_t *dp)
+{
+ taper_t *taper;
+ char *qname = quote_string(dp->name);
- case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
- dp = serial2disk(result_argv[2]);
- free_serial(result_argv[2]);
- printf("driver: finished-cmd time %s taper wrote %s:%s\n",
- walltime_str(curclock()), dp->host->hostname, dp->name);
- fflush(stdout);
- /* Note: fall through code... */
+ taper = sched(dp)->taper;
+ if (taper->result == DONE) {
+ update_info_taper(dp, taper->first_label, taper->first_fileno,
+ sched(dp)->level);
+ }
- case BOGUS:
- /*
- * Since we've gotten a tape error, we can't send anything more
- * to the taper. Go into degraded mode to try to get everthing
- * onto disk. Later, these dumps can be flushed to a new tape.
- * The tape queue is zapped so that it appears empty in future
- * checks. If there are dumps waiting for diskspace to be freed,
- * cancel one.
- */
- if(!nodump) {
- log_add(L_WARNING,
- "going into degraded mode because of tape error.");
+ sched(dp)->taper_attempted += 1;
+
+ if (taper->input_error) {
+ g_printf("driver: taper failed %s %s: %s\n",
+ dp->host->hostname, qname, taper->input_error);
+ if (strcmp(sched(dp)->datestamp, driver_timestamp) == 0) {
+ if(sched(dp)->taper_attempted >= 2) {
+ log_add(L_FAIL, _("%s %s %s %d [too many taper retries after holding disk error: %s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->input_error);
+ g_printf("driver: taper failed %s %s, too many taper retry after holding disk error\n",
+ dp->host->hostname, qname);
+ amfree(sched(dp)->destname);
+ amfree(sched(dp)->dumpdate);
+ amfree(sched(dp)->degr_dumpdate);
+ amfree(sched(dp)->degr_mesg);
+ amfree(sched(dp)->datestamp);
+ amfree(dp->up);
+ } else {
+ log_add(L_INFO, _("%s %s %s %d [Will retry dump because of holding disk error: %s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, taper->input_error);
+ g_printf("driver: taper will retry %s %s because of holding disk error\n",
+ dp->host->hostname, qname);
+ if (dp->to_holdingdisk != HOLD_REQUIRED) {
+ dp->to_holdingdisk = HOLD_NEVER;
+ sched(dp)->dump_attempted -= 1;
+ headqueue_disk(&directq, dp);
+ } else {
+ amfree(sched(dp)->destname);
+ amfree(sched(dp)->dumpdate);
+ amfree(sched(dp)->degr_dumpdate);
+ amfree(sched(dp)->degr_mesg);
+ amfree(sched(dp)->datestamp);
+ amfree(dp->up);
+ }
+ }
+ } else {
+ amfree(sched(dp)->destname);
+ amfree(sched(dp)->dumpdate);
+ amfree(sched(dp)->degr_dumpdate);
+ amfree(sched(dp)->degr_mesg);
+ amfree(sched(dp)->datestamp);
+ amfree(dp->up);
+ }
+ } else if (taper->tape_error) {
+ g_printf("driver: taper failed %s %s with tape error: %s\n",
+ dp->host->hostname, qname, taper->tape_error);
+ if(sched(dp)->taper_attempted >= 2) {
+ log_add(L_FAIL, _("%s %s %s %d [too many taper retries]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level);
+ g_printf("driver: taper failed %s %s, too many taper retry\n",
+ dp->host->hostname, qname);
+ amfree(sched(dp)->destname);
+ amfree(sched(dp)->dumpdate);
+ amfree(sched(dp)->degr_dumpdate);
+ amfree(sched(dp)->degr_mesg);
+ amfree(sched(dp)->datestamp);
+ amfree(dp->up);
+ } else {
+ g_printf("driver: taper will retry %s %s\n",
+ dp->host->hostname, qname);
+ /* Re-insert into taper queue. */
+ headqueue_disk(&tapeq, dp);
}
- start_degraded_mode(&runq);
- taper_busy = 0;
- taper_disk = NULL;
- tapeq.head = tapeq.tail = NULL;
- FD_CLR(taper,&readset);
- if(cmd != TAPE_ERROR) aclose(taper);
- continue_dumps();
- break;
- default:
- error("driver received unexpected token (%d) from taper", cmd);
+ } else if (taper->result != DONE) {
+ g_printf("driver: taper failed %s %s without error\n",
+ dp->host->hostname, qname);
+ } else {
+ delete_diskspace(dp);
+ amfree(sched(dp)->destname);
+ amfree(sched(dp)->dumpdate);
+ amfree(sched(dp)->degr_dumpdate);
+ amfree(sched(dp)->degr_mesg);
+ amfree(sched(dp)->datestamp);
+ amfree(dp->up);
+ }
+
+ amfree(qname);
+
+ taper->state &= ~TAPER_STATE_FILE_TO_TAPE;
+ taper->state |= TAPER_STATE_IDLE;
+ amfree(taper->input_error);
+ amfree(taper->tape_error);
+ taper->disk = NULL;
+ taper_nb_wait_reply--;
+ if (taper_nb_wait_reply == 0) {
+ event_release(taper_ev_read);
+ taper_ev_read = NULL;
}
-}
+ /* continue with those dumps waiting for diskspace */
+ continue_port_dumps();
+ start_some_dumps(&runq);
+ startaflush();
+}
-dumper_t *idle_dumper()
+static void
+dumper_taper_result(
+ disk_t *dp)
{
dumper_t *dumper;
+ taper_t *taper;
+ int is_partial;
+ char *qname;
+
+ dumper = sched(dp)->dumper;
+ taper = sched(dp)->taper;
+
+ free_serial_dp(dp);
+ if(dumper->result == DONE && taper->result == DONE) {
+ update_info_dumper(dp, sched(dp)->origsize,
+ sched(dp)->dumpsize, sched(dp)->dumptime);
+ update_info_taper(dp, taper->first_label, taper->first_fileno,
+ sched(dp)->level);
+ qname = quote_string(dp->name); /*quote to take care of spaces*/
+
+ log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level,
+ sched(dp)->est_time, (long long)sched(dp)->est_nsize,
+ (long long)sched(dp)->est_csize,
+ sched(dp)->est_kps);
+ amfree(qname);
+ } else {
+ update_failed_dump(dp);
+ }
+
+ is_partial = dumper->result != DONE || taper->result != DONE;
- for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
- if(!dumper->busy && !dumper->down) return dumper;
+ sched(dp)->dump_attempted += 1;
+ sched(dp)->taper_attempted += 1;
+ if((dumper->result != DONE || taper->result != DONE) &&
+ sched(dp)->dump_attempted <= 1 &&
+ sched(dp)->taper_attempted <= 1) {
+ enqueue_disk(&directq, dp);
+ }
+
+ if(dumper->ev_read != NULL) {
+ event_release(dumper->ev_read);
+ dumper->ev_read = NULL;
+ }
+ taper_nb_wait_reply--;
+ if (taper_nb_wait_reply == 0 && taper_ev_read != NULL) {
+ event_release(taper_ev_read);
+ taper_ev_read = NULL;
+ }
+ taper->state &= ~TAPER_STATE_DUMP_TO_TAPE;
+ taper->state |= TAPER_STATE_IDLE;
+ amfree(taper->input_error);
+ amfree(taper->tape_error);
+ dumper->busy = 0;
+ dp->host->inprogress -= 1;
+ dp->inprogress = 0;
+ deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
+ taper->dumper = NULL;
+ sched(dp)->dumper = NULL;
+ sched(dp)->taper = NULL;
+ start_some_dumps(&runq);
+}
+
+
+static taper_t *
+idle_taper(void)
+{
+ taper_t *taper;
+
+ /* Use an already started taper first */
+ for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+ taper++) {
+ if ((taper->state & TAPER_STATE_IDLE) &&
+ (taper->state & TAPER_STATE_TAPE_STARTED) &&
+ !(taper->state & TAPER_STATE_DONE) &&
+ !(taper->state & TAPER_STATE_FILE_TO_TAPE) &&
+ !(taper->state & TAPER_STATE_FILE_TO_TAPE))
+ return taper;
+ }
+ for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+ taper++) {
+ if ((taper->state & TAPER_STATE_IDLE) &&
+ (taper->state & TAPER_STATE_RESERVATION) &&
+ !(taper->state & TAPER_STATE_DONE) &&
+ !(taper->state & TAPER_STATE_FILE_TO_TAPE) &&
+ !(taper->state & TAPER_STATE_FILE_TO_TAPE))
+ return taper;
+ }
return NULL;
}
-int some_dumps_in_progress()
+static taper_t *
+taper_from_name(
+ char *name)
{
- dumper_t *dumper;
+ taper_t *taper;
- for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
- if(dumper->busy) return 1;
+ for (taper = tapetable; taper < tapetable+conf_taper_parallel_write;
+ taper++)
+ if (strcmp(taper->name, name) == 0) return taper;
- return taper_busy;
+ return NULL;
}
-int num_busy_dumpers()
+static void
+dumper_chunker_result(
+ disk_t * dp)
{
dumper_t *dumper;
- int n;
+ chunker_t *chunker;
+ assignedhd_t **h=NULL;
+ int activehd, i;
+ off_t dummy;
+ off_t size;
+ int is_partial;
+ char *qname;
+
+ dumper = sched(dp)->dumper;
+ chunker = dumper->chunker;
+
+ free_serial_dp(dp);
+
+ h = sched(dp)->holdp;
+ activehd = sched(dp)->activehd;
+
+ if(dumper->result == DONE && chunker->result == DONE) {
+ update_info_dumper(dp, sched(dp)->origsize,
+ sched(dp)->dumpsize, sched(dp)->dumptime);
+ qname = quote_string(dp->name);/*quote to take care of spaces*/
+
+ log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level,
+ sched(dp)->est_time, (long long)sched(dp)->est_nsize,
+ (long long)sched(dp)->est_csize,
+ sched(dp)->est_kps);
+ amfree(qname);
+ } else {
+ update_failed_dump(dp);
+ }
+
+ deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
+
+ is_partial = dumper->result != DONE || chunker->result != DONE;
+ rename_tmp_holding(sched(dp)->destname, !is_partial);
+ holding_set_origsize(sched(dp)->destname, sched(dp)->origsize);
+
+ dummy = (off_t)0;
+ for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
+ dummy += h[i]->used;
+ }
+
+ size = holding_file_size(sched(dp)->destname, 0);
+ h[activehd]->used = size - dummy;
+ h[activehd]->disk->allocated_dumpers--;
+ adjust_diskspace(dp, DONE);
- n = 0;
- for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
- if(dumper->busy) n += 1;
+ sched(dp)->dump_attempted += 1;
- return n;
+ if((dumper->result != DONE || chunker->result != DONE) &&
+ sched(dp)->dump_attempted <= 1) {
+ delete_diskspace(dp);
+ if (sched(dp)->no_space) {
+ enqueue_disk(&directq, dp);
+ } else {
+ enqueue_disk(&runq, dp);
+ }
+ }
+ else if(size > (off_t)DISK_BLOCK_KB) {
+ enqueue_disk(&tapeq, dp);
+ }
+ else {
+ delete_diskspace(dp);
+ }
+
+ dumper->busy = 0;
+ dp->host->inprogress -= 1;
+ dp->inprogress = 0;
+
+ waitpid(chunker->pid, NULL, 0 );
+ aclose(chunker->fd);
+ chunker->fd = -1;
+ chunker->down = 1;
+
+ dp = NULL;
+ if (chunker->result == ABORT_FINISHED)
+ pending_aborts--;
+ continue_port_dumps();
+ /*
+ * Wakeup any dumpers that are sleeping because of network
+ * or disk constraints.
+ */
+ start_some_dumps(&runq);
+ startaflush();
}
-dumper_t *lookup_dumper(fd)
-int fd;
+
+static void
+handle_dumper_result(
+ void * cookie)
{
- dumper_t *dumper;
+ /* uses global pending_aborts */
+ dumper_t *dumper = cookie;
+ taper_t *taper;
+ disk_t *dp, *sdp, *dp1;
+ cmd_t cmd;
+ int result_argc;
+ char *qname;
+ char **result_argv;
+
+ assert(dumper != NULL);
+ dp = dumper->dp;
+ assert(dp != NULL);
+ assert(sched(dp) != NULL);
+ do {
+
+ short_dump_state();
- for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
- if(dumper->outfd == fd) return dumper;
+ cmd = getresult(dumper->fd, 1, &result_argc, &result_argv);
- return NULL;
+ if(cmd != BOGUS) {
+ /* result_argv[1] always contains the serial number */
+ sdp = serial2disk(result_argv[1]);
+ if (sdp != dp) {
+ error(_("Invalid serial number %s"), result_argv[1]);
+ g_assert_not_reached();
+ }
+ }
+
+ qname = quote_string(dp->name);
+ switch(cmd) {
+
+ case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
+ if(result_argc != 6) {
+ error(_("error [dumper DONE result_argc != 6: %d]"), result_argc);
+ /*NOTREACHED*/
+ }
+
+ sched(dp)->origsize = OFF_T_ATOI(result_argv[2]);
+ sched(dp)->dumptime = TIME_T_ATOI(result_argv[4]);
+
+ g_printf(_("driver: finished-cmd time %s %s dumped %s:%s\n"),
+ walltime_str(curclock()), dumper->name,
+ dp->host->hostname, qname);
+ fflush(stdout);
+
+ dumper->result = cmd;
+
+ break;
+
+ case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
+ /*
+ * Requeue this disk, and fall through to the FAILED
+ * case for cleanup.
+ */
+ if(sched(dp)->dump_attempted) {
+ char *qname = quote_string(dp->name);
+ char *qerr = quote_string(result_argv[2]);
+ log_add(L_FAIL, _("%s %s %s %d [too many dumper retry: %s]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, qerr);
+ g_printf(_("driver: dump failed %s %s %s, too many dumper retry: %s\n"),
+ result_argv[1], dp->host->hostname, qname, qerr);
+ amfree(qname);
+ amfree(qerr);
+ }
+ /* FALLTHROUGH */
+ case FAILED: /* FAILED <handle> <errstr> */
+ /*free_serial(result_argv[1]);*/
+ dumper->result = cmd;
+ break;
+
+ case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
+ /*
+ * We sent an ABORT from the NO-ROOM case because this dump
+ * wasn't going to fit onto the holding disk. We now need to
+ * clean up the remains of this image, and try to finish
+ * other dumps that are waiting on disk space.
+ */
+ assert(pending_aborts);
+ /*free_serial(result_argv[1]);*/
+ dumper->result = cmd;
+ break;
+
+ case BOGUS:
+ /* either EOF or garbage from dumper. Turn it off */
+ log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
+ dumper->name, (long)dumper->pid);
+ if (dumper->ev_read) {
+ event_release(dumper->ev_read);
+ dumper->ev_read = NULL;
+ }
+ aclose(dumper->fd);
+ dumper->busy = 0;
+ dumper->down = 1; /* mark it down so it isn't used again */
+
+ /* if it was dumping something, zap it and try again */
+ if(sched(dp)->dump_attempted) {
+ log_add(L_FAIL, _("%s %s %s %d [%s died]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, dumper->name);
+ } else {
+ log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
+ dumper->name, dp->host->hostname, qname,
+ sched(dp)->level);
+ }
+ dumper->result = cmd;
+ break;
+
+ default:
+ assert(0);
+ }
+ amfree(qname);
+ g_strfreev(result_argv);
+
+ if (cmd != BOGUS) {
+ int last_dump = 1;
+ dumper_t *dumper;
+
+ run_server_scripts(EXECUTE_ON_POST_DLE_BACKUP,
+ get_config_name(), dp, sched(dp)->level);
+ /* check dump not yet started */
+ for (dp1=runq.head; dp1 != NULL; dp1 = dp1->next) {
+ if (dp1->host == dp->host)
+ last_dump = 0;
+ }
+ /* check direct to tape dump */
+ for (dp1=directq.head; dp1 != NULL; dp1 = dp1->next) {
+ if (dp1->host == dp->host)
+ last_dump = 0;
+ }
+ /* check dumping dle */
+ for (dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
+ if (dumper->busy && dumper->dp != dp &&
+ dumper->dp->host == dp->host)
+ last_dump = 0;
+ }
+ if (last_dump && dp->host->post_script == 0) {
+ if (dp->host->post_script == 0) {
+ for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+ run_server_scripts(EXECUTE_ON_POST_HOST_BACKUP,
+ get_config_name(), dp1, -1);
+ }
+ dp->host->post_script = 1;
+ }
+ }
+ }
+
+ taper = sched(dp)->taper;
+ /* send the dumper result to the chunker */
+ if (dumper->chunker) {
+ if (dumper->chunker->down == 0 && dumper->chunker->fd != -1 &&
+ dumper->chunker->result == LAST_TOK) {
+ if (cmd == DONE) {
+ chunker_cmd(dumper->chunker, DONE, dp, NULL);
+ }
+ else {
+ chunker_cmd(dumper->chunker, FAILED, dp, NULL);
+ }
+ }
+ if( dumper->result != LAST_TOK &&
+ dumper->chunker->result != LAST_TOK)
+ dumper_chunker_result(dp);
+ } else { /* send the dumper result to the taper */
+ if (taper->sendresult) {
+ if (cmd == DONE) {
+ taper_cmd(DONE, dp, NULL, 0, NULL);
+ } else {
+ taper_cmd(FAILED, dp, NULL, 0, NULL);
+ }
+ taper->sendresult = 0;
+ }
+ if (taper->dumper && taper->result != LAST_TOK) {
+ dumper_taper_result(dp);
+ }
+ }
+ } while(areads_dataready(dumper->fd));
}
-void handle_dumper_result(fd)
- int fd;
+static void
+handle_chunker_result(
+ void * cookie)
{
+ chunker_t *chunker = cookie;
assignedhd_t **h=NULL;
dumper_t *dumper;
disk_t *dp, *sdp;
- long origsize;
- long dumpsize;
- long dumptime;
cmd_t cmd;
int result_argc;
- char *result_argv[MAX_ARGS+1];
- int i, dummy;
+ char **result_argv;
+ int dummy;
int activehd = -1;
+ char *qname;
- dumper = lookup_dumper(fd);
+ assert(chunker != NULL);
+ dumper = chunker->dumper;
+ assert(dumper != NULL);
dp = dumper->dp;
- assert(dp && sched(dp) && sched(dp)->destname);
+ assert(dp != NULL);
+ assert(sched(dp) != NULL);
+ assert(sched(dp)->destname != NULL);
+ assert(dp != NULL && sched(dp) != NULL && sched(dp)->destname);
- if(dp && sched(dp) && sched(dp)->holdp) {
+ if(sched(dp)->holdp) {
h = sched(dp)->holdp;
activehd = sched(dp)->activehd;
}
- cmd = getresult(fd, 1, &result_argc, result_argv, MAX_ARGS+1);
-
- if(cmd != BOGUS) {
- sdp = serial2disk(result_argv[2]); /* result_argv[2] always contains the serial number */
- assert(sdp == dp);
- }
+ do {
+ short_dump_state();
- switch(cmd) {
+ cmd = getresult(chunker->fd, 1, &result_argc, &result_argv);
- case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
- if(result_argc != 6) {
- error("error [dumper DONE result_argc != 6: %d]", result_argc);
+ if(cmd != BOGUS) {
+ /* result_argv[1] always contains the serial number */
+ sdp = serial2disk(result_argv[1]);
+ if (sdp != dp) {
+ error(_("Invalid serial number %s"), result_argv[1]);
+ g_assert_not_reached();
+ }
}
- free_serial(result_argv[2]);
+ switch(cmd) {
- origsize = (long)atof(result_argv[3]);
- dumpsize = (long)atof(result_argv[4]);
- dumptime = (long)atof(result_argv[5]);
- update_info_dumper(dp, origsize, dumpsize, dumptime);
+ case PARTIAL: /* PARTIAL <handle> <dumpsize> <errstr> */
+ case DONE: /* DONE <handle> <dumpsize> <errstr> */
+ if(result_argc != 4) {
+ error(_("error [chunker %s result_argc != 4: %d]"), cmdstr[cmd],
+ result_argc);
+ /*NOTREACHED*/
+ }
+ /*free_serial(result_argv[1]);*/
- /* adjust holdp[active]->used using the real dumpsize and all other
- * holdp[i]->used as an estimate.
- */
+ sched(dp)->dumpsize = (off_t)atof(result_argv[2]);
- dummy = 0;
- for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
- dummy += h[i]->used;
- }
-
- rename_tmp_holding(sched(dp)->destname, 1);
- assert( h && activehd >= 0 );
- h[activehd]->used = size_holding_files(sched(dp)->destname) - dummy;
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- holdalloc(h[activehd]->disk)->allocated_dumpers--;
- adjust_diskspace(dp, DONE);
- dumper->busy = 0;
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
- sched(dp)->attempted = 0;
- printf("driver: finished-cmd time %s %s dumped %s:%s\n",
- walltime_str(curclock()), dumper->name,
- dp->host->hostname, dp->name);
- fflush(stdout);
+ qname = quote_string(dp->name);
+ g_printf(_("driver: finished-cmd time %s %s chunked %s:%s\n"),
+ walltime_str(curclock()), chunker->name,
+ dp->host->hostname, qname);
+ fflush(stdout);
+ amfree(qname);
- enqueue_disk(&tapeq, dp);
- dp = NULL;
+ event_release(chunker->ev_read);
- startaflush();
- continue_dumps();
+ chunker->result = cmd;
- break;
+ break;
- case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
- case FATAL_TRYAGAIN:
- free_serial(result_argv[2]);
+ case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
+ event_release(chunker->ev_read);
- rename_tmp_holding(sched(dp)->destname, 0);
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- assert( h && activehd >= 0 );
- holdalloc(h[activehd]->disk)->allocated_dumpers--;
- /* Because we don't know how much was written to disk the
- * following functions *must* be called together!
- */
- adjust_diskspace(dp, DONE);
- delete_diskspace(dp);
- dumper->busy = 0;
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
-
- if(sched(dp)->attempted) {
- log_add(L_FAIL, "%s %s %d %s [too many dumper retry]",
- dp->host->hostname, dp->name,
- sched(dp)->level, sched(dp)->datestamp);
- printf("driver: dump failed %s %s %s, too many dumper retry\n", result_argv[2], dp->host->hostname, dp->name);
- } else {
- sched(dp)->attempted++;
- enqueue_disk(&runq, dp);
- }
- continue_dumps();
+ chunker->result = cmd;
- if(cmd == FATAL_TRYAGAIN) {
- /* dumper is confused, start another */
- log_add(L_WARNING, "%s (pid %ld) confused, restarting it.",
- dumper->name, (long)dumper->pid);
- FD_CLR(fd,&readset);
- aclose(fd);
- startup_dump_process(dumper, dumper_program);
- }
- /* sleep in case the dumper failed because of a temporary network
- problem, as NIS or NFS... */
- sleep(15);
- break;
-
- case FAILED: /* FAILED <handle> <errstr> */
- free_serial(result_argv[2]);
-
- rename_tmp_holding(sched(dp)->destname, 0);
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- assert( h && activehd >= 0 );
- holdalloc(h[activehd]->disk)->allocated_dumpers--;
- /* Because we don't know how much was written to disk the
- * following functions *must* be called together!
- */
- adjust_diskspace(dp, DONE);
- delete_diskspace(dp);
- dumper->busy = 0;
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
- continue_dumps();
-
- /* no need to log this, dumper will do it */
- /* sleep in case the dumper failed because of a temporary network
- problem, as NIS or NFS... */
- sleep(15);
- break;
-
- case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
- assert( h && activehd >= 0 );
- h[activehd]->used -= atoi(result_argv[3]);
- h[activehd]->reserved -= atoi(result_argv[3]);
- holdalloc(h[activehd]->disk)->allocated_space -= atoi(result_argv[3]);
- h[activehd]->disk->disksize -= atoi(result_argv[3]);
- break;
-
- case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
- assert( h && activehd >= 0 );
- holdalloc(h[activehd]->disk)->allocated_dumpers--;
- h[activehd]->used = h[activehd]->reserved;
- if( h[++activehd] ) { /* There's still some allocated space left. Tell
- * the dumper about it. */
- sched(dp)->activehd++;
- dumper_cmd( dumper, CONTINUE, dp );
- } else { /* !h[++activehd] - must allocate more space */
- sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
- sched(dp)->est_size = sched(dp)->act_size * 21 / 20; /* +5% */
- sched(dp)->est_size = am_round(sched(dp)->est_size, DISK_BLOCK_KB);
- h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
- &dummy,
- h[activehd-1] );
- if( !h ) {
- /* cur_idle = max(cur_idle, IDLE_NO_DISKSPACE); */
- /* No diskspace available. The reason for this will be
- * determined in continue_dumps(). */
- enqueue_disk( &roomq, dp );
- continue_dumps();
- } else {
- /* OK, allocate space for disk and have dumper continue */
- sched(dp)->activehd = assign_holdingdisk( h, dp );
- dumper_cmd( dumper, CONTINUE, dp );
- amfree(h);
- }
- }
- break;
+ break;
+ case FAILED: /* FAILED <handle> <errstr> */
+ /*free_serial(result_argv[1]);*/
- case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
- assert(pending_aborts);
- free_serial(result_argv[2]);
+ event_release(chunker->ev_read);
- rename_tmp_holding(sched(dp)->destname, 0);
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- /* Because we don't know how much was written to disk the
- * following functions *must* be called together!
- */
- adjust_diskspace(dp, DONE);
- delete_diskspace(dp);
- sched(dp)->attempted++;
- enqueue_disk(&runq, dp); /* we'll try again later */
- dumper->busy = 0;
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
- dp = NULL;
- pending_aborts--;
- continue_dumps();
- break;
-
- case BOGUS:
- /* either EOF or garbage from dumper. Turn it off */
- log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
- dumper->name, (long)dumper->pid);
- FD_CLR(fd,&readset);
- aclose(fd);
- dumper->busy = 0;
- dumper->down = 1; /* mark it down so it isn't used again */
- if(dp) {
- /* if it was dumping something, zap it and try again */
- rename_tmp_holding(sched(dp)->destname, 0);
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- assert( h && activehd >= 0 );
- holdalloc(h[activehd]->disk)->allocated_dumpers--;
- /* Because we don't know how much was written to disk the
- * following functions *must* be called together!
- */
- adjust_diskspace(dp, DONE);
- delete_diskspace(dp);
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
- if(sched(dp)->attempted) {
- log_add(L_FAIL, "%s %s %d %s [%s died]",
- dp->host->hostname, dp->name,
- sched(dp)->level, sched(dp)->datestamp, dumper->name);
+ chunker->result = cmd;
+
+ break;
+
+ case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
+ if (!h || activehd < 0) { /* should never happen */
+ error(_("!h || activehd < 0"));
+ /*NOTREACHED*/
}
- else {
- log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
- dumper->name, dp->host->hostname, dp->name,
- sched(dp)->level);
- sched(dp)->attempted++;
- enqueue_disk(&runq, dp);
+ h[activehd]->used -= OFF_T_ATOI(result_argv[2]);
+ h[activehd]->reserved -= OFF_T_ATOI(result_argv[2]);
+ h[activehd]->disk->allocated_space -= OFF_T_ATOI(result_argv[2]);
+ h[activehd]->disk->disksize -= OFF_T_ATOI(result_argv[2]);
+ break;
+
+ case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
+ if (!h || activehd < 0) { /* should never happen */
+ error(_("!h || activehd < 0"));
+ /*NOTREACHED*/
}
- dp = NULL;
- continue_dumps();
+ h[activehd]->disk->allocated_dumpers--;
+ h[activehd]->used = h[activehd]->reserved;
+ if( h[++activehd] ) { /* There's still some allocated space left.
+ * Tell the dumper about it. */
+ sched(dp)->activehd++;
+ chunker_cmd( chunker, CONTINUE, dp, NULL );
+ } else { /* !h[++activehd] - must allocate more space */
+ sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
+ sched(dp)->est_size = (sched(dp)->act_size/(off_t)20) * (off_t)21; /* +5% */
+ sched(dp)->est_size = am_round(sched(dp)->est_size, (off_t)DISK_BLOCK_KB);
+ if (sched(dp)->est_size < sched(dp)->act_size + 2*DISK_BLOCK_KB)
+ sched(dp)->est_size += 2 * DISK_BLOCK_KB;
+ h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
+ &dummy,
+ h[activehd-1] );
+ if( !h ) {
+ /* No diskspace available. The reason for this will be
+ * determined in continue_port_dumps(). */
+ enqueue_disk( &roomq, dp );
+ continue_port_dumps();
+ /* continue flush waiting for new tape */
+ startaflush();
+ } else {
+ /* OK, allocate space for disk and have chunker continue */
+ sched(dp)->activehd = assign_holdingdisk( h, dp );
+ chunker_cmd( chunker, CONTINUE, dp, NULL );
+ amfree(h);
+ }
+ }
+ break;
+
+ case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
+ /*
+ * We sent an ABORT from the NO-ROOM case because this dump
+ * wasn't going to fit onto the holding disk. We now need to
+ * clean up the remains of this image, and try to finish
+ * other dumps that are waiting on disk space.
+ */
+ /*assert(pending_aborts);*/
+
+ /*free_serial(result_argv[1]);*/
+
+ event_release(chunker->ev_read);
+
+ chunker->result = cmd;
+
+ break;
+
+ case BOGUS:
+ /* either EOF or garbage from chunker. Turn it off */
+ log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
+ chunker->name, (long)chunker->pid);
+
+ /* if it was dumping something, zap it and try again */
+ g_assert(h && activehd >= 0);
+ qname = quote_string(dp->name);
+ if(sched(dp)->dump_attempted) {
+ log_add(L_FAIL, _("%s %s %s %d [%s died]"),
+ dp->host->hostname, qname, sched(dp)->datestamp,
+ sched(dp)->level, chunker->name);
+ } else {
+ log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
+ chunker->name, dp->host->hostname, qname,
+ sched(dp)->level);
+ }
+ amfree(qname);
+ dp = NULL;
+
+ event_release(chunker->ev_read);
+
+ chunker->result = cmd;
+
+ break;
+
+ default:
+ assert(0);
}
- break;
+ g_strfreev(result_argv);
- default:
- assert(0);
- }
+ if(chunker->result != LAST_TOK && chunker->dumper->result != LAST_TOK)
+ dumper_chunker_result(dp);
- return;
+ } while(areads_dataready(chunker->fd));
}
-void read_flush(tapeqp)
-disklist_t *tapeqp;
+static void
+read_flush(
+ void * cookie)
{
sched_t *sp;
disk_t *dp;
int line;
- dumpfile_t file;
char *hostname, *diskname, *datestamp;
int level;
char *destname;
char *command;
char *s;
int ch;
- long flush_size = 0;
+ char *qname = NULL;
+ char *qdestname = NULL;
+ char *conf_infofile;
+
+ (void)cookie; /* Quiet unused parameter warning */
+
+ event_release(flush_ev_read);
+ flush_ev_read = NULL;
/* read schedule from stdin */
+ conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
+ if (open_infofile(conf_infofile)) {
+ error(_("could not open info db \"%s\""), conf_infofile);
+ /*NOTREACHED*/
+ }
+ amfree(conf_infofile);
for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
+ dumpfile_t file;
+
line++;
+ if (inpline[0] == '\0')
+ continue;
s = inpline;
ch = *s++;
- skip_whitespace(s, ch); /* find the command */
+ skip_whitespace(s, ch); /* find the command */
if(ch == '\0') {
- error("Aflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (no command)"), line);
+ /*NOTREACHED*/
}
command = s - 1;
skip_non_whitespace(s, ch);
}
if(strcmp(command,"FLUSH") != 0) {
- error("Bflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (%s != FLUSH)"), line, command);
+ /*NOTREACHED*/
}
skip_whitespace(s, ch); /* find the hostname */
if(ch == '\0') {
- error("Cflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (no hostname)"), line);
+ /*NOTREACHED*/
}
hostname = s - 1;
skip_non_whitespace(s, ch);
skip_whitespace(s, ch); /* find the diskname */
if(ch == '\0') {
- error("Cflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (no diskname)"), line);
+ /*NOTREACHED*/
}
- diskname = s - 1;
- skip_non_whitespace(s, ch);
- s[-1] = '\0';
+ qname = s - 1;
+ skip_quoted_string(s, ch);
+ s[-1] = '\0'; /* terminate the disk name */
+ diskname = unquote_string(qname);
skip_whitespace(s, ch); /* find the datestamp */
if(ch == '\0') {
- error("Cflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (no datestamp)"), line);
+ /*NOTREACHED*/
}
datestamp = s - 1;
skip_non_whitespace(s, ch);
skip_whitespace(s, ch); /* find the level number */
if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
- error("Cflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (bad level)"), line);
+ /*NOTREACHED*/
}
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the filename */
if(ch == '\0') {
- error("Cflush line %d: syntax error", line);
- continue;
+ error(_("flush line %d: syntax error (no filename)"), line);
+ /*NOTREACHED*/
}
- destname = s - 1;
- skip_non_whitespace(s, ch);
+ qdestname = s - 1;
+ skip_quoted_string(s, ch);
s[-1] = '\0';
+ destname = unquote_string(qdestname);
- get_dumpfile(destname, &file);
+ holding_file_get_dumpfile(destname, &file);
if( file.type != F_DUMPFILE) {
if( file.type != F_CONT_DUMPFILE )
- log_add(L_INFO, "%s: ignoring cruft file.", destname);
+ log_add(L_INFO, _("%s: ignoring cruft file."), destname);
+ amfree(diskname);
+ amfree(destname);
+ dumpfile_free_data(&file);
continue;
}
if(strcmp(hostname, file.name) != 0 ||
strcmp(diskname, file.disk) != 0 ||
strcmp(datestamp, file.datestamp) != 0) {
- log_add(L_INFO, "disk %s:%s not consistent with file %s",
+ log_add(L_INFO, _("disk %s:%s not consistent with file %s"),
hostname, diskname, destname);
+ amfree(diskname);
+ amfree(destname);
+ dumpfile_free_data(&file);
continue;
}
+ amfree(diskname);
dp = lookup_disk(file.name, file.disk);
if (dp == NULL) {
- log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
+ log_add(L_INFO, _("%s: disk %s:%s not in database, skipping it."),
destname, file.name, file.disk);
+ amfree(destname);
+ dumpfile_free_data(&file);
continue;
}
if(file.dumplevel < 0 || file.dumplevel > 9) {
- log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
+ log_add(L_INFO, _("%s: ignoring file with bogus dump level %d."),
destname, file.dumplevel);
+ amfree(destname);
+ dumpfile_free_data(&file);
+ continue;
+ }
+
+ if (holding_file_size(destname,1) <= 0) {
+ log_add(L_INFO, "%s: removing file with no data.", destname);
+ holding_file_unlink(destname);
+ amfree(destname);
+ dumpfile_free_data(&file);
continue;
}
- dp1 = (disk_t *)alloc(sizeof(disk_t));
+ dp1 = (disk_t *)alloc(SIZEOF(disk_t));
*dp1 = *dp;
dp1->next = dp1->prev = NULL;
/* add it to the flushhost list */
if(!flushhost) {
- flushhost = alloc(sizeof(am_host_t));
+ flushhost = alloc(SIZEOF(am_host_t));
flushhost->next = NULL;
flushhost->hostname = stralloc("FLUSHHOST");
flushhost->up = NULL;
dp1->hostnext = flushhost->disks;
flushhost->disks = dp1;
- sp = (sched_t *) alloc(sizeof(sched_t));
- sp->destname = stralloc(destname);
+ sp = (sched_t *) alloc(SIZEOF(sched_t));
+ sp->destname = destname;
sp->level = file.dumplevel;
sp->dumpdate = NULL;
sp->degr_dumpdate = NULL;
+ sp->degr_mesg = NULL;
sp->datestamp = stralloc(file.datestamp);
- sp->est_size = 0;
+ sp->est_nsize = (off_t)0;
+ sp->est_csize = (off_t)0;
sp->est_time = 0;
+ sp->est_kps = 10;
+ sp->origsize = file.orig_size;
sp->priority = 0;
sp->degr_level = -1;
- sp->est_kps = 10;
- sp->attempted = 0;
- sp->act_size = size_holding_files(destname);
- /*sp->holdp = NULL; JLM: must be build*/
+ sp->dump_attempted = 0;
+ sp->taper_attempted = 0;
+ sp->act_size = holding_file_size(destname, 0);
sp->holdp = build_diskspace(destname);
- if(sp->holdp == NULL) continue;
+ if(sp->holdp == NULL) continue;
sp->dumper = NULL;
+ sp->taper = NULL;
sp->timestamp = (time_t)0;
dp1->up = (char *)sp;
- enqueue_disk(tapeqp, dp1);
- flush_size += sp->act_size;
+ enqueue_disk(&tapeq, dp1);
+ dumpfile_free_data(&file);
}
- printf("driver: flush size %ld\n", flush_size);
amfree(inpline);
-}
+ close_infofile();
+ startaflush();
+ if (!nodump) {
+ schedule_ev_read = event_register((event_id_t)0, EV_READFD,
+ read_schedule, NULL);
+ }
+}
-void read_schedule(waitqp, runqp)
-disklist_t *waitqp, *runqp;
+static void
+read_schedule(
+ void * cookie)
{
sched_t *sp;
disk_t *dp;
int level, line, priority;
- char *dumpdate, *degr_dumpdate;
+ char *dumpdate, *degr_dumpdate, *degr_mesg;
int degr_level;
- long time, degr_time;
- unsigned long size, degr_size;
+ time_t time, degr_time;
+ time_t *time_p = &time;
+ time_t *degr_time_p = °r_time;
+ off_t nsize, csize, degr_nsize, degr_csize;
+ unsigned long kps, degr_kps;
char *hostname, *features, *diskname, *datestamp, *inpline = NULL;
char *command;
char *s;
int ch;
+ off_t flush_size = (off_t)0;
+ char *qname = NULL;
+ long long time_;
+ long long nsize_;
+ long long csize_;
+ long long degr_nsize_;
+ long long degr_csize_;
+ GPtrArray *errarray;
+
+ (void)cookie; /* Quiet unused parameter warning */
+
+ event_release(schedule_ev_read);
+ schedule_ev_read = NULL;
/* read schedule from stdin */
for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
+ if (inpline[0] == '\0')
+ continue;
line++;
s = inpline;
skip_whitespace(s, ch); /* find the command */
if(ch == '\0') {
- error("schedule line %d: syntax error (no command)", line);
- continue;
+ error(_("schedule line %d: syntax error (no command)"), line);
+ /*NOTREACHED*/
}
command = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
if(strcmp(command,"DUMP") != 0) {
- error("schedule line %d: syntax error (%s != DUMP)", line, command);
- continue;
+ error(_("schedule line %d: syntax error (%s != DUMP)"), line, command);
+ /*NOTREACHED*/
}
skip_whitespace(s, ch); /* find the host name */
if(ch == '\0') {
- error("schedule line %d: syntax error (no host name)", line);
- continue;
+ error(_("schedule line %d: syntax error (no host name)"), line);
+ /*NOTREACHED*/
}
hostname = s - 1;
skip_non_whitespace(s, ch);
skip_whitespace(s, ch); /* find the feature list */
if(ch == '\0') {
- error("schedule line %d: syntax error (no feature list)", line);
- continue;
+ error(_("schedule line %d: syntax error (no feature list)"), line);
+ /*NOTREACHED*/
}
features = s - 1;
skip_non_whitespace(s, ch);
skip_whitespace(s, ch); /* find the disk name */
if(ch == '\0') {
- error("schedule line %d: syntax error (no disk name)", line);
- continue;
+ error(_("schedule line %d: syntax error (no disk name)"), line);
+ /*NOTREACHED*/
}
- diskname = s - 1;
- skip_non_whitespace(s, ch);
- s[-1] = '\0';
+ qname = s - 1;
+ skip_quoted_string(s, ch);
+ s[-1] = '\0'; /* terminate the disk name */
+ diskname = unquote_string(qname);
skip_whitespace(s, ch); /* find the datestamp */
if(ch == '\0') {
- error("schedule line %d: syntax error (no datestamp)", line);
- continue;
+ error(_("schedule line %d: syntax error (no datestamp)"), line);
+ /*NOTREACHED*/
}
datestamp = s - 1;
skip_non_whitespace(s, ch);
skip_whitespace(s, ch); /* find the priority number */
if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
- error("schedule line %d: syntax error (bad priority)", line);
- continue;
+ error(_("schedule line %d: syntax error (bad priority)"), line);
+ /*NOTREACHED*/
}
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the level number */
if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
- error("schedule line %d: syntax error (bad level)", line);
- continue;
+ error(_("schedule line %d: syntax error (bad level)"), line);
+ /*NOTREACHED*/
}
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the dump date */
if(ch == '\0') {
- error("schedule line %d: syntax error (bad dump date)", line);
- continue;
+ error(_("schedule line %d: syntax error (bad dump date)"), line);
+ /*NOTREACHED*/
}
dumpdate = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
- skip_whitespace(s, ch); /* find the size number */
- if(ch == '\0' || sscanf(s - 1, "%lu", &size) != 1) {
- error("schedule line %d: syntax error (bad size)", line);
- continue;
+ skip_whitespace(s, ch); /* find the native size */
+ nsize_ = (off_t)0;
+ if(ch == '\0' || sscanf(s - 1, "%lld", &nsize_) != 1) {
+ error(_("schedule line %d: syntax error (bad nsize)"), line);
+ /*NOTREACHED*/
+ }
+ nsize = (off_t)nsize_;
+ skip_integer(s, ch);
+
+ skip_whitespace(s, ch); /* find the compressed size */
+ csize_ = (off_t)0;
+ if(ch == '\0' || sscanf(s - 1, "%lld", &csize_) != 1) {
+ error(_("schedule line %d: syntax error (bad csize)"), line);
+ /*NOTREACHED*/
}
+ csize = (off_t)csize_;
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the time number */
- if(ch == '\0' || sscanf(s - 1, "%ld", &time) != 1) {
- error("schedule line %d: syntax error (bad estimated time)", line);
+ if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
+ error(_("schedule line %d: syntax error (bad estimated time)"), line);
+ /*NOTREACHED*/
+ }
+ *time_p = (time_t)time_;
+ skip_integer(s, ch);
+
+ skip_whitespace(s, ch); /* find the kps number */
+ if(ch == '\0' || sscanf(s - 1, "%lu", &kps) != 1) {
+ error(_("schedule line %d: syntax error (bad kps)"), line);
continue;
}
skip_integer(s, ch);
degr_dumpdate = NULL; /* flag if degr fields found */
skip_whitespace(s, ch); /* find the degr level number */
- if(ch != '\0') {
+ degr_mesg = NULL;
+ if (ch == '"') {
+ qname = s - 1;
+ skip_quoted_string(s, ch);
+ s[-1] = '\0'; /* terminate degr mesg */
+ degr_mesg = unquote_string(qname);
+ degr_level = -1;
+ degr_nsize = (off_t)0;
+ degr_csize = (off_t)0;
+ degr_time = (time_t)0;
+ degr_kps = 0;
+ } else if (ch != '\0') {
if(sscanf(s - 1, "%d", °r_level) != 1) {
- error("schedule line %d: syntax error (bad degr level)", line);
- continue;
+ error(_("schedule line %d: syntax error (bad degr level)"), line);
+ /*NOTREACHED*/
}
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the degr dump date */
if(ch == '\0') {
- error("schedule line %d: syntax error (bad degr dump date)", line);
- continue;
+ error(_("schedule line %d: syntax error (bad degr dump date)"), line);
+ /*NOTREACHED*/
}
degr_dumpdate = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
- skip_whitespace(s, ch); /* find the degr size number */
- if(ch == '\0' || sscanf(s - 1, "%lu", °r_size) != 1) {
- error("schedule line %d: syntax error (bad degr size)", line);
- continue;
+ skip_whitespace(s, ch); /* find the degr native size */
+ degr_nsize_ = (off_t)0;
+ if(ch == '\0' || sscanf(s - 1, "%lld", °r_nsize_) != 1) {
+ error(_("schedule line %d: syntax error (bad degr nsize)"), line);
+ /*NOTREACHED*/
+ }
+ degr_nsize = (off_t)degr_nsize_;
+ skip_integer(s, ch);
+
+ skip_whitespace(s, ch); /* find the degr compressed size */
+ degr_csize_ = (off_t)0;
+ if(ch == '\0' || sscanf(s - 1, "%lld", °r_csize_) != 1) {
+ error(_("schedule line %d: syntax error (bad degr csize)"), line);
+ /*NOTREACHED*/
}
+ degr_csize = (off_t)degr_csize_;
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the degr time number */
- if(ch == '\0' || sscanf(s - 1, "%lu", °r_time) != 1) {
- error("schedule line %d: syntax error (bad degr estimated time)", line);
- continue;
+ if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
+ error(_("schedule line %d: syntax error (bad degr estimated time)"), line);
+ /*NOTREACHED*/
+ }
+ *degr_time_p = (time_t)time_;
+ skip_integer(s, ch);
+
+ skip_whitespace(s, ch); /* find the degr kps number */
+ if(ch == '\0' || sscanf(s - 1, "%lu", °r_kps) != 1) {
+ error(_("schedule line %d: syntax error (bad degr kps)"), line);
+ /*NOTREACHED*/
}
skip_integer(s, ch);
+ } else {
+ error(_("schedule line %d: no degraded estimate or message"), line);
}
dp = lookup_disk(hostname, diskname);
if(dp == NULL) {
log_add(L_WARNING,
- "schedule line %d: %s:%s not in disklist, ignored",
- line, hostname, diskname);
+ _("schedule line %d: %s:'%s' not in disklist, ignored"),
+ line, hostname, qname);
+ amfree(diskname);
continue;
}
- sp = (sched_t *) alloc(sizeof(sched_t));
- sp->level = level;
+ sp = (sched_t *) alloc(SIZEOF(sched_t));
+ /*@ignore@*/
+ sp->level = level;
sp->dumpdate = stralloc(dumpdate);
- sp->est_size = DISK_BLOCK_KB + size; /* include header */
+ sp->est_nsize = DISK_BLOCK_KB + nsize; /* include header */
+ sp->est_csize = DISK_BLOCK_KB + csize; /* include header */
+ /* round estimate to next multiple of DISK_BLOCK_KB */
+ sp->est_csize = am_round(sp->est_csize, DISK_BLOCK_KB);
+ sp->est_size = sp->est_csize;
sp->est_time = time;
+ sp->est_kps = kps;
sp->priority = priority;
sp->datestamp = stralloc(datestamp);
if(degr_dumpdate) {
sp->degr_level = degr_level;
sp->degr_dumpdate = stralloc(degr_dumpdate);
- sp->degr_size = DISK_BLOCK_KB + degr_size;
+ sp->degr_nsize = DISK_BLOCK_KB + degr_nsize;
+ sp->degr_csize = DISK_BLOCK_KB + degr_csize;
+ /* round estimate to next multiple of DISK_BLOCK_KB */
+ sp->degr_csize = am_round(sp->degr_csize, DISK_BLOCK_KB);
sp->degr_time = degr_time;
+ sp->degr_kps = degr_kps;
+ sp->degr_mesg = NULL;
} else {
sp->degr_level = -1;
sp->degr_dumpdate = NULL;
+ sp->degr_mesg = degr_mesg;
}
+ /*@end@*/
- if(time <= 0)
- sp->est_kps = 10;
- else
- sp->est_kps = size/time;
-
- if(sp->degr_level != -1) {
- if(degr_time <= 0)
- sp->degr_kps = 10;
- else
- sp->degr_kps = degr_size/degr_time;
- }
-
- sp->attempted = 0;
+ sp->dump_attempted = 0;
+ sp->taper_attempted = 0;
sp->act_size = 0;
sp->holdp = NULL;
sp->activehd = -1;
sp->dumper = NULL;
+ sp->taper = NULL;
sp->timestamp = (time_t)0;
sp->destname = NULL;
sp->no_space = 0;
dp->up = (char *) sp;
if(dp->host->features == NULL) {
dp->host->features = am_string_to_feature(features);
+ if (!dp->host->features) {
+ log_add(L_WARNING,
+ _("Invalid feature string from client '%s'"),
+ features);
+ dp->host->features = am_set_default_feature_set();
+ }
+ }
+ remove_disk(&waitq, dp);
+
+ 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,
+ sp->datestamp,
+ (char *)g_ptr_array_index(errarray, i));
+ }
+ amfree(qname);
+ } else {
+
+ if (dp->data_path == DATA_PATH_DIRECTTCP &&
+ dp->to_holdingdisk == HOLD_AUTO) {
+ dp->to_holdingdisk = HOLD_NEVER;
+ }
+
+ if (dp->to_holdingdisk == HOLD_NEVER) {
+ enqueue_disk(&directq, dp);
+ } else {
+ enqueue_disk(&runq, dp);
+ }
+ flush_size += sp->act_size;
}
- remove_disk(waitqp, dp);
- insert_disk(&runq, dp, sort_by_time);
+ amfree(diskname);
}
+ g_printf(_("driver: flush size %lld\n"), (long long)flush_size);
amfree(inpline);
if(line == 0)
- log_add(L_WARNING, "WARNING: got empty schedule from planner");
+ log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
+ if(need_degraded==1) start_degraded_mode(&runq);
+ schedule_done = 1;
+ start_some_dumps(&runq);
+ startaflush();
}
-int free_kps(ip)
-interface_t *ip;
+static unsigned long
+free_kps(
+ netif_t *ip)
{
- int res;
-
- if (ip == (interface_t *)0) {
- interface_t *p;
- int maxusage=0;
- int curusage=0;
- for(p = lookup_interface(NULL); p != NULL; p = p->next) {
- maxusage += p->maxusage;
+ unsigned long res;
+
+ if (ip == NULL) {
+ netif_t *p;
+ unsigned long maxusage=0;
+ unsigned long curusage=0;
+ for(p = disklist_netifs(); p != NULL; p = p->next) {
+ maxusage += interface_get_maxusage(p->config);
curusage += p->curusage;
}
- res = maxusage - curusage;
- }
- else {
- res = ip->maxusage - ip->curusage;
+ if (maxusage >= curusage)
+ res = maxusage - curusage;
+ else
+ res = 0;
+#ifndef __lint
+ } else {
+ if ((unsigned long)interface_get_maxusage(ip->config) >= ip->curusage)
+ res = interface_get_maxusage(ip->config) - ip->curusage;
+ else
+ res = 0;
+#endif
}
return res;
}
-void interface_state(time_str)
-char *time_str;
+static void
+interface_state(
+ char *time_str)
{
- interface_t *ip;
+ netif_t *ip;
- printf("driver: interface-state time %s", time_str);
+ g_printf(_("driver: interface-state time %s"), time_str);
- for(ip = lookup_interface(NULL); ip != NULL; ip = ip->next) {
- printf(" if %s: free %d", ip->name, free_kps(ip));
+ for(ip = disklist_netifs(); ip != NULL; ip = ip->next) {
+ g_printf(_(" if %s: free %lu"), interface_name(ip->config), free_kps(ip));
}
- printf("\n");
+ g_printf("\n");
}
-void allocate_bandwidth(ip, kps)
-interface_t *ip;
-int kps;
+static void
+allocate_bandwidth(
+ netif_t * ip,
+ unsigned long kps)
{
ip->curusage += kps;
}
-void deallocate_bandwidth(ip, kps)
-interface_t *ip;
-int kps;
+static void
+deallocate_bandwidth(
+ netif_t * ip,
+ unsigned long kps)
{
assert(kps <= ip->curusage);
ip->curusage -= kps;
}
/* ------------ */
-unsigned long free_space()
+static off_t
+free_space(void)
{
- holdingdisk_t *hdp;
- unsigned long total_free;
- long diff;
-
- total_free = 0L;
- for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
- diff = hdp->disksize - holdalloc(hdp)->allocated_space;
- if(diff > 0)
+ holdalloc_t *ha;
+ off_t total_free;
+ off_t diff;
+
+ total_free = (off_t)0;
+ for(ha = holdalloc; ha != NULL; ha = ha->next) {
+ diff = ha->disksize - ha->allocated_space;
+ if(diff > (off_t)0)
total_free += diff;
}
return total_free;
}
-assignedhd_t **find_diskspace(size, cur_idle, pref)
-unsigned long size;
-int *cur_idle;
-assignedhd_t *pref;
-/* Rewrite by Peter Conrad <conrad@opus5.de>, June '99:
- * - enable splitting a dump across several holding disks
- * - allocate only as much as size tells us, dumpers may request more later
+/*
* We return an array of pointers to assignedhd_t. The array contains at
* most one entry per holding disk. The list of pointers is terminated by
* a NULL pointer. Each entry contains a pointer to a holdingdisk and
* will allocate the given amount of space.
* If there is not enough room on the holdingdisks, NULL is returned.
*/
+
+static assignedhd_t **
+find_diskspace(
+ off_t size,
+ int * cur_idle,
+ assignedhd_t * pref)
{
-assignedhd_t **result = NULL;
- holdingdisk_t *minp, *hdp;
- int i=0, num_holdingdisks=0; /* are we allowed to use the global thing? */
+ assignedhd_t **result = NULL;
+ holdalloc_t *ha, *minp;
+ int i=0;
int j, minj;
char *used;
- long halloc, dalloc, hfree, dfree;
+ off_t halloc, dalloc, hfree, dfree;
- size = am_round(size, DISK_BLOCK_KB);
+ (void)cur_idle; /* Quiet unused parameter warning */
-#ifdef HOLD_DEBUG
- printf("find diskspace: want %lu K\n", size );
- fflush(stdout);
-#endif
+ if (size < 2*DISK_BLOCK_KB)
+ size = 2*DISK_BLOCK_KB;
+ size = am_round(size, (off_t)DISK_BLOCK_KB);
- for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
- num_holdingdisks++;
- }
+ hold_debug(1, _("find_diskspace: want %lld K\n"),
+ (long long)size);
- used = alloc(sizeof(char) * num_holdingdisks);/*disks used during this run*/
- memset( used, 0, num_holdingdisks );
- result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
+ used = alloc(SIZEOF(*used) * num_holdalloc);/*disks used during this run*/
+ memset( used, 0, (size_t)num_holdalloc );
+ result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
result[0] = NULL;
- while( i < num_holdingdisks && size > 0 ) {
+ while( i < num_holdalloc && size > (off_t)0 ) {
/* find the holdingdisk with the fewest active dumpers and among
* those the one with the biggest free space
*/
minp = NULL; minj = -1;
- for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next, j++ ) {
- if( pref && pref->disk == hdp && !used[j] &&
- holdalloc(hdp)->allocated_space <= hdp->disksize - DISK_BLOCK_KB) {
- minp = hdp;
+ for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
+ if( pref && pref->disk == ha && !used[j] &&
+ ha->allocated_space <= ha->disksize - (off_t)DISK_BLOCK_KB) {
+ minp = ha;
minj = j;
break;
}
- else if( holdalloc(hdp)->allocated_space <= hdp->disksize - 2*DISK_BLOCK_KB &&
- !used[j] &&
+ else if( ha->allocated_space <= ha->disksize - (off_t)(2*DISK_BLOCK_KB) &&
+ !used[j] &&
(!minp ||
- holdalloc(hdp)->allocated_dumpers < holdalloc(minp)->allocated_dumpers ||
- (holdalloc(hdp)->allocated_dumpers == holdalloc(minp)->allocated_dumpers &&
- hdp->disksize-holdalloc(hdp)->allocated_space > minp->disksize-holdalloc(minp)->allocated_space)) ) {
- minp = hdp;
+ ha->allocated_dumpers < minp->allocated_dumpers ||
+ (ha->allocated_dumpers == minp->allocated_dumpers &&
+ ha->disksize-ha->allocated_space > minp->disksize-minp->allocated_space)) ) {
+ minp = ha;
minj = j;
}
}
+
pref = NULL;
if( !minp ) { break; } /* all holding disks are full */
used[minj] = 1;
/* hfree = free space on the disk */
- hfree = minp->disksize - holdalloc(minp)->allocated_space;
+ hfree = minp->disksize - minp->allocated_space;
/* dfree = free space for data, remove 1 header for each chunksize */
- dfree = hfree - (((hfree-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
+ dfree = hfree - (((hfree-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
/* dalloc = space I can allocate for data */
dalloc = ( dfree < size ) ? dfree : size;
/* halloc = space to allocate, including 1 header for each chunksize */
- halloc = dalloc + (((dalloc-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
-
-#ifdef HOLD_DEBUG
- fprintf(stdout,"find diskspace: size %ld hf %ld df %ld da %ld ha %ld\n", size, hfree, dfree, dalloc, halloc);
- fflush(stdout);
-#endif
+ halloc = dalloc + (((dalloc-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
+
+ hold_debug(1, _("find_diskspace: find diskspace: size %lld hf %lld df %lld da %lld ha %lld\n"),
+ (long long)size,
+ (long long)hfree,
+ (long long)dfree,
+ (long long)dalloc,
+ (long long)halloc);
size -= dalloc;
- result[i] = alloc(sizeof(assignedhd_t));
+ result[i] = alloc(SIZEOF(assignedhd_t));
result[i]->disk = minp;
result[i]->reserved = halloc;
- result[i]->used = 0;
+ result[i]->used = (off_t)0;
result[i]->destname = NULL;
result[i+1] = NULL;
i++;
- } /* while i < num_holdingdisks && size > 0 */
+ }
amfree(used);
- if( size ) { /* not enough space available */
-#ifdef HOLD_DEBUG
- printf("find diskspace: not enough diskspace. Left with %lu K\n", size);
+ if(size != (off_t)0) { /* not enough space available */
+ g_printf(_("find diskspace: not enough diskspace. Left with %lld K\n"), (long long)size);
fflush(stdout);
-#endif
free_assignedhd(result);
result = NULL;
}
-#ifdef HOLD_DEBUG
- for( i = 0; result && result[i]; i++ ) {
- printf("find diskspace: selected %s free %ld reserved %ld dumpers %d\n",
- result[i]->disk->diskdir,
- result[i]->disk->disksize - holdalloc(result[i]->disk)->allocated_space,
- result[i]->reserved,
- holdalloc(result[i]->disk)->allocated_dumpers);
+ if (debug_holding > 1) {
+ for( i = 0; result && result[i]; i++ ) {
+ hold_debug(1, _("find_diskspace: find diskspace: selected %s free %lld reserved %lld dumpers %d\n"),
+ holdingdisk_get_diskdir(result[i]->disk->hdisk),
+ (long long)(result[i]->disk->disksize -
+ result[i]->disk->allocated_space),
+ (long long)result[i]->reserved,
+ result[i]->disk->allocated_dumpers);
+ }
}
- fflush(stdout);
-#endif
return result;
}
-int assign_holdingdisk(holdp, diskp)
-assignedhd_t **holdp;
-disk_t *diskp;
+static int
+assign_holdingdisk(
+ assignedhd_t ** holdp,
+ disk_t * diskp)
{
-/* Modified by Peter Conrad <conrad@opus5.de>, June '99
- * Modifications for splitting dumps across holding disks:
- * sched(diskp)->holdp now contains an array of pointers to assignedhd_t.
- */
int i, j, c, l=0;
- unsigned long size;
+ off_t size;
char *sfn = sanitise_filename(diskp->name);
char lvl[64];
assignedhd_t **new_holdp;
+ char *qname;
- ap_snprintf( lvl, sizeof(lvl), "%d", sched(diskp)->level );
+ g_snprintf( lvl, SIZEOF(lvl), "%d", sched(diskp)->level );
size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
- DISK_BLOCK_KB);
+ (off_t)DISK_BLOCK_KB);
- for( c = 0; holdp[c]; c++ ); /* count number of disks */
+ for( c = 0; holdp[c]; c++ )
+ (void)c; /* count number of disks */
/* allocate memory for sched(diskp)->holdp */
- for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++) {}
- new_holdp = (assignedhd_t **)alloc(sizeof(assignedhd_t*)*(j+c+1));
+ for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++)
+ (void)j; /* Quiet lint */
+ new_holdp = (assignedhd_t **)alloc(SIZEOF(assignedhd_t*)*(j+c+1));
if (sched(diskp)->holdp) {
- memcpy(new_holdp, sched(diskp)->holdp, j * sizeof(*new_holdp));
+ memcpy(new_holdp, sched(diskp)->holdp, j * SIZEOF(*new_holdp));
amfree(sched(diskp)->holdp);
}
sched(diskp)->holdp = new_holdp;
l=j;
if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
- holdalloc(holdp[0]->disk)->allocated_space += holdp[0]->reserved;
- size = (holdp[0]->reserved>size) ? 0 : size-holdp[0]->reserved;
-#ifdef HOLD_DEBUG
- printf("merging holding disk %s to disk %s:%s, add %lu for reserved %lu, left %lu\n",
- sched(diskp)->holdp[j-1]->disk->diskdir,
- diskp->host->hostname, diskp->name,
- holdp[0]->reserved, sched(diskp)->holdp[j-1]->reserved,
- size );
- fflush(stdout);
-#endif
+ holdp[0]->disk->allocated_space += holdp[0]->reserved;
+ size = (holdp[0]->reserved>size) ? (off_t)0 : size-holdp[0]->reserved;
+ qname = quote_string(diskp->name);
+ hold_debug(1, _("assign_holdingdisk: merging holding disk %s to disk %s:%s, add %lld for reserved %lld, left %lld\n"),
+ holdingdisk_get_diskdir(
+ sched(diskp)->holdp[j-1]->disk->hdisk),
+ diskp->host->hostname, qname,
+ (long long)holdp[0]->reserved,
+ (long long)sched(diskp)->holdp[j-1]->reserved,
+ (long long)size);
i++;
+ amfree(qname);
amfree(holdp[0]);
l=j-1;
}
/* copy assignedhd_s to sched(diskp), adjust allocated_space */
for( ; holdp[i]; i++ ) {
holdp[i]->destname = newvstralloc( holdp[i]->destname,
- holdp[i]->disk->diskdir, "/",
- timestamp, "/",
+ holdingdisk_get_diskdir(holdp[i]->disk->hdisk), "/",
+ hd_driver_timestamp, "/",
diskp->host->hostname, ".",
sfn, ".",
lvl, NULL );
sched(diskp)->holdp[j++] = holdp[i];
- holdalloc(holdp[i]->disk)->allocated_space += holdp[i]->reserved;
- size = (holdp[i]->reserved>size) ? 0 : size-holdp[i]->reserved;
-#ifdef HOLD_DEBUG
- printf("assigning holding disk %s to disk %s:%s, reserved %lu, left %lu\n",
- holdp[i]->disk->diskdir, diskp->host->hostname, diskp->name,
- holdp[i]->reserved, size );
- fflush(stdout);
-#endif
+ holdp[i]->disk->allocated_space += holdp[i]->reserved;
+ size = (holdp[i]->reserved > size) ? (off_t)0 :
+ (size - holdp[i]->reserved);
+ qname = quote_string(diskp->name);
+ hold_debug(1,
+ _("assign_holdingdisk: %d assigning holding disk %s to disk %s:%s, reserved %lld, left %lld\n"),
+ i, holdingdisk_get_diskdir(holdp[i]->disk->hdisk),
+ diskp->host->hostname, qname,
+ (long long)holdp[i]->reserved,
+ (long long)size);
+ amfree(qname);
holdp[i] = NULL; /* so it doesn't get free()d... */
}
sched(diskp)->holdp[j] = NULL;
- sched(diskp)->destname = newstralloc(sched(diskp)->destname,sched(diskp)->holdp[0]->destname);
amfree(sfn);
return l;
}
-static void adjust_diskspace(diskp, cmd)
-disk_t *diskp;
-cmd_t cmd;
+static void
+adjust_diskspace(
+ disk_t * diskp,
+ cmd_t cmd)
{
-/* Re-write by Peter Conrad <conrad@opus5.de>, March '99
- * Modifications for splitting dumps across holding disks:
- * Dumpers no longer write more than they've allocated, therefore an
- * adjustment may only free some allocated space.
- * 08/99: Jean-Louis suggested that dumpers tell us how much they've written.
- * We just believe them and don't stat all the files but rely on the used
- * field.
- */
-
assignedhd_t **holdp;
- unsigned long total=0;
- long diff;
+ off_t total = (off_t)0;
+ off_t diff;
int i;
+ char *qname, *hqname, *qdest;
-#ifdef HOLD_DEBUG
- printf("adjust: %s:%s %s\n", diskp->host->hostname, diskp->name,
- sched(diskp)->destname );
- fflush(stdout);
-#endif
+ (void)cmd; /* Quiet unused parameter warning */
+
+ qname = quote_string(diskp->name);
+ qdest = quote_string(sched(diskp)->destname);
+ hold_debug(1, _("adjust_diskspace: %s:%s %s\n"),
+ diskp->host->hostname, qname, qdest);
holdp = sched(diskp)->holdp;
- assert(holdp);
+ assert(holdp != NULL);
for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
diff = holdp[i]->used - holdp[i]->reserved;
total += holdp[i]->used;
- holdalloc(holdp[i]->disk)->allocated_space += diff;
-#ifdef HOLD_DEBUG
- printf("adjust: hdisk %s done, reserved %ld used %ld diff %ld alloc %ld dumpers %d\n",
- holdp[i]->disk->name, holdp[i]->reserved, holdp[i]->used, diff,
- holdalloc(holdp[i]->disk)->allocated_space,
- holdalloc(holdp[i]->disk)->allocated_dumpers );
- fflush(stdout);
-#endif
+ holdp[i]->disk->allocated_space += diff;
+ hqname = quote_string(holdingdisk_name(holdp[i]->disk->hdisk));
+ hold_debug(1, _("adjust_diskspace: hdisk %s done, reserved %lld used %lld diff %lld alloc %lld dumpers %d\n"),
+ holdingdisk_name(holdp[i]->disk->hdisk),
+ (long long)holdp[i]->reserved,
+ (long long)holdp[i]->used,
+ (long long)diff,
+ (long long)holdp[i]->disk->allocated_space,
+ holdp[i]->disk->allocated_dumpers );
holdp[i]->reserved += diff;
+ amfree(hqname);
}
sched(diskp)->act_size = total;
-#ifdef HOLD_DEBUG
- printf("adjust: after: disk %s:%s used %ld\n", diskp->host->hostname,
- diskp->name, sched(diskp)->act_size );
- fflush(stdout);
-#endif
+
+ hold_debug(1, _("adjust_diskspace: after: disk %s:%s used %lld\n"),
+ diskp->host->hostname, qname,
+ (long long)sched(diskp)->act_size);
+ amfree(qdest);
+ amfree(qname);
}
-static void delete_diskspace(diskp)
-disk_t *diskp;
+static void
+delete_diskspace(
+ disk_t *diskp)
{
-/* Re-write by Peter Conrad <conrad@opus5.de>, March '99
- * Modifications for splitting dumps across holding disks:
- * After implementing Jean-Louis' suggestion (see above) this looks much
- * simpler... again, we rely on assignedhd_s containing correct info
- */
assignedhd_t **holdp;
int i;
holdp = sched(diskp)->holdp;
- assert(holdp);
+ assert(holdp != NULL);
for( i = 0; holdp[i]; i++ ) { /* for each disk */
- /* find all files of this dump on that disk, and subtract their
- * reserved sizes from the disk's allocated space
- */
- holdalloc(holdp[i]->disk)->allocated_space -= holdp[i]->used;
+ /* find all files of this dump on that disk, and subtract their
+ * reserved sizes from the disk's allocated space
+ */
+ holdp[i]->disk->allocated_space -= holdp[i]->used;
}
- unlink_holding_files(holdp[0]->destname); /* no need for the entire list,
- because unlink_holding_files
- will walk through all files
- using cont_filename */
-
+ holding_file_unlink(holdp[0]->destname); /* no need for the entire list,
+ * because holding_file_unlink
+ * will walk through all files
+ * using cont_filename */
free_assignedhd(sched(diskp)->holdp);
sched(diskp)->holdp = NULL;
- sched(diskp)->act_size = 0;
- amfree(sched(diskp)->destname);
+ sched(diskp)->act_size = (off_t)0;
}
-assignedhd_t **build_diskspace(destname)
-char *destname;
+static assignedhd_t **
+build_diskspace(
+ char * destname)
{
int i, j;
int fd;
- int buflen;
+ size_t buflen;
char buffer[DISK_BLOCK_BYTES];
dumpfile_t file;
assignedhd_t **result;
- holdingdisk_t *hdp;
- int *used;
- int num_holdingdisks=0;
+ holdalloc_t *ha;
+ off_t *used;
char dirname[1000], *ch;
struct stat finfo;
char *filename = destname;
- for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
- num_holdingdisks++;
- }
- used = alloc(sizeof(int) * num_holdingdisks);
- for(i=0;i<num_holdingdisks;i++)
- used[i] = 0;
- result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
+ memset(buffer, 0, sizeof(buffer));
+ used = alloc(SIZEOF(off_t) * num_holdalloc);
+ for(i=0;i<num_holdalloc;i++)
+ used[i] = (off_t)0;
+ result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
result[0] = NULL;
while(filename != NULL && filename[0] != '\0') {
strncpy(dirname, filename, 999);
dirname[999]='\0';
ch = strrchr(dirname,'/');
- *ch = '\0';
- ch = strrchr(dirname,'/');
- *ch = '\0';
+ if (ch) {
+ *ch = '\0';
+ ch = strrchr(dirname,'/');
+ if (ch) {
+ *ch = '\0';
+ }
+ }
+
+ if (!ch) {
+ g_fprintf(stderr,_("build_diskspace: bogus filename '%s'\n"), filename);
+ amfree(used);
+ amfree(result);
+ return NULL;
+ }
- for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL;
- hdp = hdp->next, j++ ) {
- if(strcmp(dirname,hdp->diskdir)==0) {
+ for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
+ if(strcmp(dirname, holdingdisk_get_diskdir(ha->hdisk))==0) {
break;
}
}
if(stat(filename, &finfo) == -1) {
- fprintf(stderr, "stat %s: %s\n", filename, strerror(errno));
- finfo.st_size = 0;
+ g_fprintf(stderr, _("stat %s: %s\n"), filename, strerror(errno));
+ finfo.st_size = (off_t)0;
}
- used[j] += (finfo.st_size+1023)/1024;
+ used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
if((fd = open(filename,O_RDONLY)) == -1) {
- fprintf(stderr,"build_diskspace: open of %s failed: %s\n",
+ g_fprintf(stderr,_("build_diskspace: open of %s failed: %s\n"),
filename, strerror(errno));
+ amfree(used);
+ amfree(result);
return NULL;
}
- buflen = fullread(fd, buffer, sizeof(buffer));
- parse_file_header(buffer, &file, buflen);
+ if ((buflen = full_read(fd, buffer, SIZEOF(buffer))) > 0) {;
+ parse_file_header(buffer, &file, buflen);
+ }
close(fd);
filename = file.cont_filename;
}
- for(j = 0, i=0, hdp = getconf_holdingdisks(); hdp != NULL;
- hdp = hdp->next, j++ ) {
- if(used[j]) {
- result[i] = alloc(sizeof(assignedhd_t));
- result[i]->disk = hdp;
+ for(j = 0, i=0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
+ if(used[j] != (off_t)0) {
+ result[i] = alloc(SIZEOF(assignedhd_t));
+ result[i]->disk = ha;
result[i]->reserved = used[j];
result[i]->used = used[j];
result[i]->destname = stralloc(destname);
return result;
}
-
-void holdingdisk_state(time_str)
-char *time_str;
+static void
+holdingdisk_state(
+ char * time_str)
{
- holdingdisk_t *hdp;
+ holdalloc_t *ha;
int dsk;
- long diff;
+ off_t diff;
- printf("driver: hdisk-state time %s", time_str);
+ g_printf(_("driver: hdisk-state time %s"), time_str);
- for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
- diff = hdp->disksize - holdalloc(hdp)->allocated_space;
- printf(" hdisk %d: free %ld dumpers %d", dsk, diff,
- holdalloc(hdp)->allocated_dumpers);
+ for(ha = holdalloc, dsk = 0; ha != NULL; ha = ha->next, dsk++) {
+ diff = ha->disksize - ha->allocated_space;
+ g_printf(_(" hdisk %d: free %lld dumpers %d"), dsk,
+ (long long)diff, ha->allocated_dumpers);
}
- printf("\n");
+ g_printf("\n");
}
-static void update_failed_dump_to_tape(dp)
-disk_t *dp;
+static void
+update_failed_dump(
+ disk_t * dp)
{
time_t save_timestamp = sched(dp)->timestamp;
/* setting timestamp to 0 removes the current level from the
* gnutar-lists might have been updated already, and a bumped
* incremental might be created. */
sched(dp)->timestamp = 0;
- update_info_dumper(dp, -1, -1, -1);
+ update_info_dumper(dp, (off_t)-1, (off_t)-1, (time_t)-1);
sched(dp)->timestamp = save_timestamp;
}
/* ------------------- */
-int dump_to_tape(dp)
- disk_t *dp;
+
+static int
+queue_length(
+ disklist_t q)
{
- dumper_t *dumper;
- int failed = 0;
- int filenum;
- long origsize = 0;
- long dumpsize = 0;
- long dumptime = 0;
- cmd_t cmd;
- int result_argc;
- char *result_argv[MAX_ARGS+1];
- int dumper_tryagain = 0;
+ disk_t *p;
+ int len;
- inside_dump_to_tape = 1; /* for simulator */
+ for(len = 0, p = q.head; p != NULL; len++, p = p->next)
+ (void)len; /* Quiet lint */
+ return len;
+}
- printf("driver: dumping %s:%s directly to tape\n",
- dp->host->hostname, dp->name);
- fflush(stdout);
+static void
+short_dump_state(void)
+{
+ int i, nidle;
+ char *wall_time;
- /* pick a dumper and fail if there are no idle dumpers */
+ wall_time = walltime_str(curclock());
- dumper = idle_dumper();
- if (!dumper) {
- printf("driver: no idle dumpers for %s:%s.\n",
- dp->host->hostname, dp->name);
- fflush(stdout);
- log_add(L_WARNING, "no idle dumpers for %s:%s.\n",
- dp->host->hostname, dp->name);
- inside_dump_to_tape = 0;
- return 2; /* fatal problem */
+ g_printf(_("driver: state time %s "), wall_time);
+ g_printf(_("free kps: %lu space: %lld taper: "),
+ free_kps(NULL),
+ (long long)free_space());
+ if(degraded_mode) g_printf(_("DOWN"));
+ else {
+ taper_t *taper;
+ int writing = 0;
+ for(taper = tapetable; taper < tapetable+conf_taper_parallel_write;
+ taper++) {
+ if (taper->state & TAPER_STATE_DUMP_TO_TAPE ||
+ taper->state & TAPER_STATE_FILE_TO_TAPE)
+ writing = 1;
+ }
+ if(writing)
+ g_printf(_("writing"));
+ else
+ g_printf(_("idle"));
}
+ nidle = 0;
+ for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
+ g_printf(_(" idle-dumpers: %d"), nidle);
+ g_printf(_(" qlen tapeq: %d"), queue_length(tapeq));
+ g_printf(_(" runq: %d"), queue_length(runq));
+ g_printf(_(" roomq: %d"), queue_length(roomq));
+ g_printf(_(" wakeup: %d"), (int)sleep_time);
+ g_printf(_(" driver-idle: %s\n"), _(idle_strings[idle_reason]));
+ interface_state(wall_time);
+ holdingdisk_state(wall_time);
+ fflush(stdout);
+}
- /* tell the taper to read from a port number of its choice */
-
- taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
- cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
- if(cmd != PORT) {
- printf("driver: did not get PORT from taper for %s:%s\n",
- dp->host->hostname, dp->name);
- fflush(stdout);
- inside_dump_to_tape = 0;
- return 2; /* fatal problem */
+static TapeAction
+tape_action(
+ taper_t *taper,
+ char **why_no_new_tape)
+{
+ TapeAction result = TAPE_ACTION_NO_ACTION;
+ dumper_t *dumper;
+ taper_t *taper1;
+ disk_t *dp;
+ off_t dumpers_size;
+ off_t runq_size;
+ off_t directq_size;
+ off_t tapeq_size;
+ off_t sched_size;
+ off_t dump_to_disk_size;
+ int dump_to_disk_terminated;
+ off_t my_flush_threshold_dumped;
+ off_t my_flush_threshold_scheduled;
+ off_t my_taperflush;
+ int nb_taper_active = nb_sent_new_tape;
+ int nb_taper_flushing = 0;
+
+ dumpers_size = 0;
+ for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
+ if (dumper->busy && !sched(dumper->dp)->taper)
+ dumpers_size += sched(dumper->dp)->est_size;
}
- /* copy port number */
- sched(dp)->destname = newvstralloc(sched(dp)->destname, result_argv[2], NULL );
-
- /* tell the dumper to dump to a port */
-
- dumper_cmd(dumper, PORT_DUMP, dp);
- dp->host->start_t = time(NULL) + 15;
-
- /* update statistics & print state */
+ driver_debug(1, _("dumpers_size: %lld\n"), (long long)dumpers_size);
- taper_busy = dumper->busy = 1;
- dp->host->inprogress += 1;
- dp->inprogress = 1;
- sched(dp)->timestamp = time((time_t *)0);
- allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
- idle_reason = 0;
-
- short_dump_state();
-
- /* wait for result from dumper */
-
- cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
-
- if(cmd != BOGUS)
- free_serial(result_argv[2]);
-
- switch(cmd) {
- case BOGUS:
- /* either eof or garbage from dumper */
- log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
- dumper->name, (long)dumper->pid);
- dumper->down = 1; /* mark it down so it isn't used again */
- failed = 1; /* dump failed, must still finish up with taper */
- break;
-
- case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
- /* everything went fine */
- origsize = (long)atof(result_argv[3]);
- dumpsize = (long)atof(result_argv[4]);
- dumptime = (long)atof(result_argv[5]);
- break;
-
- case NO_ROOM: /* NO-ROOM <handle> */
- dumper_cmd(dumper, ABORT, dp);
- cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
- if(cmd != BOGUS)
- free_serial(result_argv[2]);
- assert(cmd == ABORT_FINISHED);
-
- case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
- default:
- /* dump failed, but we must still finish up with taper */
- /* problem with dump, possibly nonfatal, retry one time */
- sched(dp)->attempted++;
- failed = sched(dp)->attempted;
- dumper_tryagain = 1;
- break;
-
- case FAILED: /* FAILED <handle> <errstr> */
- /* dump failed, but we must still finish up with taper */
- failed = 2; /* fatal problem with dump */
- break;
+ runq_size = 0;
+ for(dp = runq.head; dp != NULL; dp = dp->next) {
+ runq_size += sched(dp)->est_size;
}
+ driver_debug(1, _("runq_size: %lld\n"), (long long)runq_size);
- /*
- * Note that at this point, even if the dump above failed, it may
- * not be a fatal failure if taper below says we can try again.
- * E.g. a dumper failure above may actually be the result of a
- * tape overflow, which in turn causes dump to see "broken pipe",
- * "no space on device", etc., since taper closed the port first.
- */
+ directq_size = 0;
+ for(dp = directq.head; dp != NULL; dp = dp->next) {
+ directq_size += sched(dp)->est_size;
+ }
+ driver_debug(1, _("directq_size: %lld\n"), (long long)directq_size);
- cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
+ tapeq_size = directq_size;
+ for(dp = tapeq.head; dp != NULL; dp = dp->next) {
+ tapeq_size += sched(dp)->act_size;
+ }
- switch(cmd) {
- case DONE: /* DONE <handle> <label> <tape file> <err mess> */
- if(result_argc != 5) {
- error("error [dump to tape DONE result_argc != 5: %d]", result_argc);
+ for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
+ taper1++) {
+ if (taper1->state & TAPER_STATE_FILE_TO_TAPE ||
+ taper1->state & TAPER_STATE_DUMP_TO_TAPE) {
+ nb_taper_flushing++;
}
+ }
- if(failed == 1) goto tryagain; /* dump didn't work */
- else if(failed == 2) goto failed_dumper;
+ /* Add what is currently written to tape and in the go. */
+ for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
+ taper1++) {
+ if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+ tapeq_size -= taper1->left;
+ }
+ if (taper1->disk) {
+ if (taper1->dumper) {
+ tapeq_size += sched(taper1->disk)->est_size - taper1->written;
+ } else {
+ tapeq_size += sched(taper1->disk)->act_size - taper1->written;
+ }
+ }
+ }
+ driver_debug(1, _("tapeq_size: %lld\n"), (long long)tapeq_size);
- free_serial(result_argv[2]);
+ sched_size = runq_size + directq_size + tapeq_size + dumpers_size;
+ driver_debug(1, _("sched_size: %lld\n"), (long long)sched_size);
- /* every thing went fine */
- update_info_dumper(dp, origsize, dumpsize, dumptime);
- filenum = atoi(result_argv[4]);
- update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
- /* note that update_info_dumper() must be run before
- update_info_taper(), since update_info_dumper overwrites
- tape information. */
+ dump_to_disk_size = dumpers_size + runq_size + directq_size;
+ driver_debug(1, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
- break;
+ dump_to_disk_terminated = schedule_done && dump_to_disk_size == 0;
- case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
- if(dumper_tryagain == 0) {
- sched(dp)->attempted++;
- if(sched(dp)->attempted > failed)
- failed = sched(dp)->attempted;
+ for (taper1 = tapetable; taper1 < tapetable + conf_taper_parallel_write;
+ taper1++) {
+ if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+ nb_taper_active++;
}
- tryagain:
- if(failed <= 1)
- headqueue_disk(&runq, dp);
- failed_dumper:
- update_failed_dump_to_tape(dp);
- free_serial(result_argv[2]);
- tape_left = tape_length;
- break;
-
-
- case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
- case BOGUS:
- default:
- update_failed_dump_to_tape(dp);
- free_serial(result_argv[2]);
- failed = 2; /* fatal problem */
- start_degraded_mode(&runq);
+ }
+ if (nb_taper_active >= 1) {
+ my_flush_threshold_dumped = flush_threshold_dumped +
+ (nb_taper_active-nb_taper_active) * tape_length;
+ my_flush_threshold_scheduled = flush_threshold_scheduled +
+ (nb_taper_active-nb_taper_active) * tape_length;
+ my_taperflush = taperflush + (nb_taper_active-nb_taper_active) * tape_length;
+ } else {
+ my_flush_threshold_dumped = flush_threshold_dumped +
+ nb_taper_active * tape_length;
+ my_flush_threshold_scheduled = flush_threshold_scheduled +
+ nb_taper_active * tape_length;
+ my_taperflush = taperflush + nb_taper_active * tape_length;
}
- /* reset statistics & return */
-
- taper_busy = dumper->busy = 0;
- dp->host->inprogress -= 1;
- dp->inprogress = 0;
- deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
-
- inside_dump_to_tape = 0;
- return failed;
-}
-
-int queue_length(q)
-disklist_t q;
-{
- disk_t *p;
- int len;
+ // Changing conditionals can produce a driver hang, take care.
+ //
+ // when to start writting to a new tape
+ if (taper->state & TAPER_STATE_TAPE_REQUESTED) {
+ if (current_tape >= conf_runtapes && taper_nb_scan_volume == 0 &&
+ nb_taper_active == 0) {
+ *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
+ "does not allow additional tapes"), current_tape, conf_runtapes);
+ result |= TAPE_ACTION_NO_NEW_TAPE;
+ } else if (current_tape < conf_runtapes &&
+ taper_nb_scan_volume == 0 &&
+ ((my_flush_threshold_dumped < tapeq_size &&
+ my_flush_threshold_scheduled < sched_size) ||
+ nb_taper_active == 0) &&
+ (last_started_taper == NULL ||
+ last_started_taper == taper)) {
+ result |= TAPE_ACTION_SCAN;
+ } else {
+ result |= TAPE_ACTION_MOVE;
+ }
+ } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
+ ((taper->state & TAPER_STATE_DUMP_TO_TAPE) || // for dump to tape
+ !empty(directq) || // if a dle is waiting for a dump to tape
+ !empty(roomq) || // holding disk constraint
+ idle_reason == IDLE_NO_DISKSPACE || // holding disk constraint
+ (my_flush_threshold_dumped < tapeq_size && // flush-threshold-dumped &&
+ my_flush_threshold_scheduled < sched_size) || // flush-threshold-scheduled
+ (my_taperflush < tapeq_size && // taperflush
+ (force_flush == 1 || // if force_flush
+ dump_to_disk_terminated)) // or all dump to disk terminated
+ )) {
+ result |= TAPE_ACTION_NEW_TAPE;
+ // when to stop using new tape
+ } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
+ (my_taperflush >= tapeq_size && // taperflush criteria
+ (force_flush == 1 || // if force_flush
+ dump_to_disk_terminated)) // or all dump to disk
+ ) {
+ if (nb_taper_active <= 0) {
+ if (current_tape >= conf_runtapes) {
+ *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
+ "does not allow additional tapes"), current_tape, conf_runtapes);
+ } else {
+ *why_no_new_tape = _("taperflush criteria not met");
+ }
+ result |= TAPE_ACTION_NO_NEW_TAPE;
+ }
+ }
- for(len = 0, p = q.head; p != NULL; len++, p = p->next);
- return len;
+ // when to start a flush
+ // We don't start a flush if taper_tape_started == 1 && dump_to_disk_terminated && force_flush == 0,
+ // it is a criteria need to exit the first event_loop without flushing everything to tape,
+ // they will be flush in another event_loop.
+ if (taper->state & TAPER_STATE_IDLE) {
+ if (!degraded_mode && (!empty(tapeq) || !empty(directq)) &&
+ (((taper->state & TAPER_STATE_TAPE_STARTED) &&
+ force_flush == 1) || // if tape already started and force_flush
+ !empty(roomq) || // holding disk constraint
+ idle_reason == IDLE_NO_DISKSPACE || // holding disk constraint
+ (my_flush_threshold_dumped < tapeq_size && // flush-threshold-dumped &&
+ my_flush_threshold_scheduled < sched_size) || // flush-threshold-scheduled
+ (force_flush == 1 && my_taperflush < tapeq_size))) { // taperflush if force_flush
+
+ if (nb_taper_flushing == 0) {
+ result |= TAPE_ACTION_START_A_FLUSH;
+ } else {
+ result |= TAPE_ACTION_START_A_FLUSH_FIT;
+ }
+ }
+ }
+ return result;
}
-
-void short_dump_state()
+static int
+all_taper_idle(void)
{
- int i, nidle;
- char *wall_time;
-
- wall_time = walltime_str(curclock());
+ taper_t *taper;
- printf("driver: state time %s ", wall_time);
- printf("free kps: %d space: %lu taper: ",
- free_kps((interface_t *)0), free_space());
- if(degraded_mode) printf("DOWN");
- else if(!taper_busy) printf("idle");
- else printf("writing");
- nidle = 0;
- for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
- printf(" idle-dumpers: %d", nidle);
- printf(" qlen tapeq: %d", queue_length(tapeq));
- printf(" runq: %d", queue_length(runq));
- printf(" roomq: %d", queue_length(roomq));
- printf(" wakeup: %d", (int)sleep_time.tv_sec);
- printf(" driver-idle: %s\n", idle_strings[idle_reason]);
- interface_state(wall_time);
- holdingdisk_state(wall_time);
- fflush(stdout);
+ for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+ taper++) {
+ if (taper->state & TAPER_STATE_DUMP_TO_TAPE ||
+ taper->state & TAPER_STATE_FILE_TO_TAPE)
+ return 0;
+ }
+ return 1;
}
-void dump_state(str)
-char *str;
+#if 0
+static void
+dump_state(
+ const char *str)
{
int i;
disk_t *dp;
-
- printf("================\n");
- printf("driver state at time %s: %s\n", walltime_str(curclock()), str);
- printf("free kps: %d, space: %lu\n", free_kps((interface_t *)0), free_space());
- if(degraded_mode) printf("taper: DOWN\n");
- else if(!taper_busy) printf("taper: idle\n");
- else printf("taper: writing %s:%s.%d est size %lu\n",
- taper_disk->host->hostname, taper_disk->name,
- sched(taper_disk)->level,
- sched(taper_disk)->est_size);
+ char *qname;
+
+ g_printf("================\n");
+ g_printf(_("driver state at time %s: %s\n"), walltime_str(curclock()), str);
+ g_printf(_("free kps: %lu, space: %lld\n"),
+ free_kps(NULL),
+ (long long)free_space());
+ if(degraded_mode) g_printf(_("taper: DOWN\n"));
+ else if(taper->status == TAPER_IDLE) g_printf(_("taper: idle\n"));
+ else g_printf(_("taper: writing %s:%s.%d est size %lld\n"),
+ taper->disk->host->hostname, taper->disk->name,
+ sched(taper->disk)->level,
+ (long long)sched(taper->disk)->est_size);
for(i = 0; i < inparallel; i++) {
dp = dmptable[i].dp;
if(!dmptable[i].busy)
- printf("%s: idle\n", dmptable[i].name);
+ g_printf(_("%s: idle\n"), dmptable[i].name);
else
- printf("%s: dumping %s:%s.%d est kps %d size %lu time %ld\n",
- dmptable[i].name, dp->host->hostname, dp->name, sched(dp)->level,
- sched(dp)->est_kps, sched(dp)->est_size, sched(dp)->est_time);
+ qname = quote_string(dp->name);
+ g_printf(_("%s: dumping %s:%s.%d est kps %d size %lld time %lu\n"),
+ dmptable[i].name, dp->host->hostname, qname, sched(dp)->level,
+ sched(dp)->est_kps, (long long)sched(dp)->est_size, sched(dp)->est_time);
+ amfree(qname);
}
dump_queue("TAPE", tapeq, 5, stdout);
dump_queue("ROOM", roomq, 5, stdout);
dump_queue("RUN ", runq, 5, stdout);
- printf("================\n");
+ g_printf("================\n");
fflush(stdout);
}
+#endif