/* We call this when we can't find a tape to write data to. This could happen with the first (or only) part of a file, but it could also happen with an intermediate part of a split dump. dump_bytes is 0 if this is the first part of a dump. */ static void bail_no_volume( dump_info_t *dump_info, char *errmsg) { char *errstr; if (errmsg) errstr = quote_string(errmsg); else errstr = quote_string("no new tape"); if (dump_info->total_bytes > 0) { /* Second or later part of a split dump, so PARTIAL message. */ double dump_time = g_timeval_to_double(dump_info->total_time); guint64 dump_kbytes = dump_info->total_bytes / 1024; double dump_kbps = get_kbps(dump_kbytes, dump_time); putresult(PARTIAL, "%s INPUT-GOOD TAPE-ERROR " "\"[sec %f kb %ju kps %f]\" \"\" %s\n", dump_info->handle, dump_time, (uintmax_t)dump_kbytes, dump_kbps, errstr); put_partial_log(dump_info, dump_time, dump_kbytes, errstr); } else { char * qdiskname = quote_string(dump_info->diskname); putresult(FAILED, "%s INPUT-GOOD TAPE-ERROR \"\" %s\n", dump_info->handle, errstr); log_add(L_FAIL, "%s %s %s %d %s", dump_info->hostname, qdiskname, dump_info->timestamp, dump_info->level, errstr); amfree(qdiskname); } amfree(errstr); }
/* In running mode (not startup mode), get a command from driver and deal with it. */ static gboolean process_driver_command(taper_state_t * state) { struct cmdargs *cmdargs; char * q; /* This will return QUIT if driver has died. */ cmdargs = getcmd(); switch (cmdargs->cmd) { case PORT_WRITE: /* * PORT-WRITE * handle * hostname * features * diskname * level * datestamp * splitsize * split_diskbuffer */ process_port_write(state, cmdargs); break; case FILE_WRITE: /* * FILE-WRITE * handle * filename * hostname * features * diskname * level * datestamp * splitsize */ process_file_write(state, cmdargs); break; case QUIT: free_cmdargs(cmdargs); if (state->device && state->device->volume_label) { log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n", state->device->volume_label, (long long)((state->total_bytes+(off_t)1023) / (off_t)1024), state->device->file); } return send_quitting(state); default: if (cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc("(no input?)"); } putresult(BAD_COMMAND, "%s\n", q); amfree(q); break; } free_cmdargs(cmdargs); return TRUE; }
/* * Do a task group: fork the processes, then wait for them. */ static void runtaskgroup(unsigned count, void (*prep)(unsigned, unsigned), void (*task)(unsigned, unsigned), void (*cleanup)(unsigned, unsigned), unsigned groupid) { pid_t mypids[count]; unsigned i; unsigned failures = 0; time_t secs; unsigned long nsecs; prep(groupid, count); for (i=0; i<count; i++) { mypids[i] = fork(); if (mypids[i] < 0) { err(1, "fork"); } if (mypids[i] == 0) { /* child (of second fork) */ task(groupid, i); exit(0); } /* parent (of second fork) - continue */ } /* * now wait for the task to finish */ for (i=0; i<count; i++) { failures += dowait(mypids[i]); } /* * Store the end time. */ __time(&secs, &nsecs); openresultsfile(O_WRONLY); putresult(groupid, secs, nsecs); closeresultsfile(); cleanup(groupid, count); exit(failures ? 1 : 0); }
/* Link up the TaperSource with the Device, including retries etc. */ static void run_device_output(taper_state_t * taper_state, dump_info_t * dump_info) { GValue val; guint file_number; dump_info->current_part = 1; dump_info->total_time.tv_sec = 0; dump_info->total_time.tv_usec = 0; dump_info->total_bytes = 0; for (;;) { GTimeVal start_time, end_time, run_time; StreamingRequirement streaming_mode; queue_result_flags queue_result; CountingConsumerData consumer_data; dumpfile_t *this_header; size_t max_memory; this_header = munge_headers(dump_info); if (this_header == NULL) { char * qdiskname = quote_string(dump_info->diskname); char * errstr = taper_source_get_errmsg(dump_info->source); if (!errstr) errstr = "Failed reading dump header."; errstr = quote_string(errstr); putresult(FAILED, "%s INPUT-ERROR TAPE-GOOD %s \"\"\n", dump_info->handle, errstr); log_add(L_FAIL, "%s %s %s %d %s", dump_info->hostname, qdiskname, dump_info->timestamp, dump_info->level, errstr); amfree(qdiskname); amfree(errstr); return; } if (!find_and_label_new_tape(taper_state, dump_info)) { bail_no_volume(dump_info, taper_state->last_errmsg); dumpfile_free(this_header); return; } while (!device_start_file(taper_state->device, this_header)) { /* Close the device. */ device_finish(taper_state->device); g_object_unref(taper_state->device); taper_state->device = NULL; if (!find_and_label_new_tape(taper_state, dump_info)) { bail_no_volume(dump_info, taper_state->last_errmsg); dumpfile_free(this_header); return; } } dumpfile_free(this_header); bzero(&val, sizeof(val)); if (!device_property_get(taper_state->device, PROPERTY_STREAMING, &val) || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) { g_fprintf(stderr, "taper: Couldn't get streaming type!\n"); streaming_mode = STREAMING_REQUIREMENT_REQUIRED; } else { streaming_mode = g_value_get_enum(&val); } file_number = taper_state->device->file; consumer_data.next_consumer = device_write_consumer; consumer_data.next_consumer_data = taper_state->device; consumer_data.bytes_written = 0; g_get_current_time(&start_time); if (getconf_seen(CNF_DEVICE_OUTPUT_BUFFER_SIZE)) { max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); if (getconf_seen(CNF_TAPEBUFS)) { g_fprintf(stderr, "Configuration directives 'device_output_buffer_size' " "and \n" "'tapebufs' are incompatible; using former.\n"); } } else if (getconf_seen(CNF_TAPEBUFS)) { max_memory = getconf_int(CNF_TAPEBUFS) * taper_state->device->block_size; } else { /* Use default. */ max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); } queue_result = do_consumer_producer_queue_full (taper_source_producer, dump_info->source, counting_consumer, &consumer_data, taper_state->device->block_size, max_memory, streaming_mode); g_get_current_time(&end_time); run_time = timesub(end_time, start_time); /* The device_write_consumer leaves the file open, so close it now. */ if (!device_finish_file(taper_state->device)) { queue_result = queue_result | QUEUE_CONSUMER_ERROR; } if (!finish_part_attempt(taper_state, dump_info, queue_result, run_time, consumer_data.bytes_written)) { break; } } }
/* Figure out what to do after a part attempt. Returns TRUE if another attempt should proceed for this dump; FALSE if we are done. */ static gboolean finish_part_attempt(taper_state_t * taper_state, dump_info_t * dump_info, queue_result_flags queue_result, GTimeVal run_time, guint64 run_bytes) { double part_time = g_timeval_to_double(run_time); guint64 part_kbytes = run_bytes / 1024; double part_kbps = get_kbps((double)run_bytes / 1024.0, part_time); char * qdiskname = quote_string(dump_info->diskname); if (queue_result == QUEUE_SUCCESS) { dump_info->total_time = timesadd(run_time, dump_info->total_time); dump_info->total_bytes += run_bytes; log_add(L_PART, "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f]", taper_state->device->volume_label, taper_state->device->file, dump_info->hostname, qdiskname, dump_info->timestamp, dump_info->current_part, taper_source_predict_parts(dump_info->source), dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps); putresult(PARTDONE, "%s %s %d %ju \"[sec %f kb %ju kps %f]\"\n", dump_info->handle, taper_state->device->volume_label, taper_state->device->file, (uintmax_t)part_kbytes, part_time, (uintmax_t)part_kbytes, part_kbps); taper_state->total_bytes += run_bytes; if (taper_source_get_end_of_data(dump_info->source)) { cmd_t result_cmd; logtype_t result_log; double dump_time = g_timeval_to_double(dump_info->total_time); guint64 dump_kbytes = dump_info->total_bytes / 1024; double dump_kbps = get_kbps((double)dump_info->total_bytes / 1024.0, dump_time); find_completion_tags(dump_info, &result_cmd, &result_log); g_object_unref(dump_info->source); dump_info->source = NULL; log_add(result_log, "%s %s %s %d %d [sec %f kb %ju kps %f]", dump_info->hostname, qdiskname, dump_info->timestamp, dump_info->current_part, dump_info->level, dump_time, (uintmax_t)dump_kbytes, dump_kbps); putresult(result_cmd, "%s INPUT-GOOD TAPE-GOOD " "\"[sec %f kb %ju kps %f]\" \"\" \"\"\n", dump_info->handle, dump_time, (uintmax_t)dump_kbytes, dump_kbps); amfree(qdiskname); return FALSE; } else if (taper_source_get_end_of_part(dump_info->source)) { taper_source_start_new_part(dump_info->source); dump_info->current_part ++; amfree(qdiskname); return TRUE; } /* If we didn't read EOF or EOP, then an error occured. But we read QUEUE_SUCCESS, so something is b0rked. */ g_assert_not_reached(); } else { char * volume_label = strdup(taper_state->device->volume_label); int file_number = taper_state->device->file; double dump_time, dump_kbps; guint64 dump_kbytes; char *producer_errstr = quote_string( taper_source_get_errmsg(dump_info->source)); char *consumer_errstr = quote_string( device_error(taper_state->device)); log_add(L_PARTPARTIAL, "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f] %s", volume_label, file_number, dump_info->hostname, qdiskname, dump_info->timestamp, dump_info->current_part, taper_source_predict_parts(dump_info->source), dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps, consumer_errstr); log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n", volume_label, (long long)((taper_state->total_bytes+(off_t)1023) / (off_t)1024), taper_state->device->file); /* A problem occured. */ if (queue_result & QUEUE_CONSUMER_ERROR) { /* Make a note if this was EOM (we treat EOM the same as any other error, * so it's just for debugging purposes */ if (taper_state->device->is_eof) g_debug("device %s ran out of space", taper_state->device->device_name); /* Close the device. */ device_finish(taper_state->device); g_object_unref(taper_state->device); taper_state->device = NULL; } amfree(volume_label); if ((queue_result & QUEUE_CONSUMER_ERROR) && (!(queue_result & QUEUE_PRODUCER_ERROR)) && taper_source_seek_to_part_start(dump_info->source)) { /* It is recoverable. */ log_add(L_INFO, "Will request retry of failed split part."); if (find_and_label_new_tape(taper_state, dump_info)) { /* dump_info->current_part is unchanged. */ amfree(qdiskname); return TRUE; } } dump_time = g_timeval_to_double(dump_info->total_time); dump_kbytes = dump_info->total_bytes / 1024; dump_kbps = get_kbps((double)dump_info->total_bytes / 1024.0, dump_time); putresult(PARTIAL, "%s INPUT-%s TAPE-%s " "\"[sec %f kb %ju kps %f]\" %s %s\n", dump_info->handle, (queue_result & QUEUE_PRODUCER_ERROR) ? "ERROR" : "GOOD", (queue_result & QUEUE_CONSUMER_ERROR) ? "ERROR" : "GOOD", dump_time, (uintmax_t)dump_kbytes, dump_kbps, producer_errstr, consumer_errstr); if (queue_result & QUEUE_CONSUMER_ERROR) { put_partial_log(dump_info, dump_time, dump_kbytes, consumer_errstr); } else { put_partial_log(dump_info, dump_time, dump_kbytes, producer_errstr); } amfree(producer_errstr); amfree(consumer_errstr); } amfree(qdiskname); return FALSE; }
static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) { char *old_volume_name = NULL; char *old_volume_time = NULL; tape_search_request_t request; gboolean search_result; DeviceStatusFlags status; /* If we got here, it means that we have found a tape to label and * have gotten permission from the driver to write it. But we * still can say NO-NEW-TAPE if a problem shows up, and must still * say NEW-TAPE if one doesn't. */ amfree(state->last_errmsg); state->device = device_open(state->next_tape_device); g_assert(state->device != NULL); amfree(state->next_tape_device); if (state->device->status != DEVICE_STATUS_SUCCESS) goto skip_volume; if (!device_configure(state->device, TRUE)) goto skip_volume; /* if we have an error, and are sure it isn't just an unlabeled volume, * then skip this volume */ status = device_read_label(state->device); if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && !(status & DEVICE_STATUS_VOLUME_UNLABELED)) goto skip_volume; old_volume_name = g_strdup(state->device->volume_label); old_volume_time = g_strdup(state->device->volume_time); if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label, state->driver_start_time)) { gboolean tape_used; /* Something broke, see if we can tell if the volume was erased or * not. */ g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n", state->next_tape_label, state->device->device_name, device_error_or_status(state->device)); if (!device_finish(state->device)) goto request_new_volume; /* This time, if we can't read the label, assume we've overwritten * the volume or otherwise corrupted it */ status = device_read_label(state->device); if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && !(status & DEVICE_STATUS_VOLUME_UNLABELED)) goto request_new_volume; tape_used = check_volume_changed(state->device, old_volume_name, old_volume_time); if (tape_used) goto request_new_volume; else goto skip_volume; } amfree(old_volume_name); amfree(old_volume_time); amfree(state->next_tape_label); update_tapelist(state); state->cur_tape++; if (state->have_changer && changer_label("UNKNOWN", state->device->volume_label) != 0) { error(_("couldn't update barcode database")); /*NOTREACHED*/ } log_add(L_START, "datestamp %s label %s tape %d", state->driver_start_time, state->device->volume_label, state->cur_tape); putresult(NEW_TAPE, "%s %s\n", dump_info->handle, state->device->volume_label); return TRUE; request_new_volume: /* Tell the driver we overwrote this volume, even if it was empty, and request * a new volume. */ if (state->device) state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); else state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); putresult(NEW_TAPE, "%s %s\n", dump_info->handle, state->next_tape_label); if (old_volume_name) { log_add(L_WARNING, "Problem writing label '%s' to volume %s " "(volume may be erased): %s\n", state->next_tape_label, old_volume_name, state->last_errmsg); } else { log_add(L_WARNING, "Problem writing label '%s' to new volume " "(volume may be erased): %s\n", state->next_tape_label, state->last_errmsg); } if (state->device) { g_object_unref(state->device); state->device = NULL; } amfree(state->next_tape_label); amfree(old_volume_name); amfree(old_volume_time); return find_and_label_new_tape(state, dump_info); skip_volume: /* grab a new volume without talking to the driver again -- we do this if we're * confident we didn't overwrite the last tape we got. */ if (state->device) state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); else state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); if (old_volume_name) { log_add(L_WARNING, "Problem writing label '%s' to volume '%s' " "(old volume data intact): %s\n", state->next_tape_label, old_volume_name, state->last_errmsg); } else { log_add(L_WARNING, "Problem writing label '%s' to new volume " "(old volume data intact): %s\n", state->next_tape_label, state->last_errmsg); } if (state->device) { g_object_unref(state->device); state->device = NULL; } amfree(state->next_tape_label); amfree(old_volume_name); amfree(old_volume_time); request.state = state; request.prolong = TRUE; request.errmsg = NULL; search_result = GPOINTER_TO_INT(tape_search_thread(&request)); if (search_result) { amfree(request.errmsg); return label_new_tape(state, dump_info); } else { /* Problem finding a new tape! */ log_taper_scan_errmsg(request.errmsg); putresult(NO_NEW_TAPE, "%s\n", dump_info->handle); return FALSE; } }
/* If handle is NULL, then this function assumes that we are in startup mode. * In that case it will wait for a command from driver. If handle is not NULL, * this this function will ask for permission with REQUEST-NEW-TAPE. */ static gboolean find_new_tape(taper_state_t * state, dump_info_t * dump) { GThread * tape_search = NULL; tape_search_request_t search_request; gboolean use_threads; struct cmdargs *cmdargs; cmd_t cmd; if (state->device != NULL) { return TRUE; } /* We save the value here in case it changes while we're running. */ use_threads = g_thread_supported(); search_request.state = state; search_request.prolong = TRUE; search_request.errmsg = NULL; if (use_threads) { tape_search = g_thread_create(tape_search_thread, &search_request, TRUE, NULL); } putresult(REQUEST_NEW_TAPE, "%s\n", dump->handle); cmdargs = getcmd(); cmd = cmdargs->cmd; switch (cmd) { default: g_fprintf(stderr, "taper: Got odd message from driver, expected NEW-TAPE or NO-NEW-TAPE.\n"); /* FALLTHROUGH. */ case NEW_TAPE: { gboolean search_result; if (use_threads) { search_result = GPOINTER_TO_INT(g_thread_join(tape_search)); } else { search_result = GPOINTER_TO_INT(tape_search_thread(&search_request)); } if (search_result) { /* We don't say NEW_TAPE until we actually write the label. */ amfree(search_request.errmsg); free_cmdargs(cmdargs); return TRUE; } else { putresult(NO_NEW_TAPE, "%s\n", dump->handle); log_taper_scan_errmsg(search_request.errmsg); log_add(L_ERROR, "no-tape [%s]", "No more writable valid tape found"); free_cmdargs(cmdargs); return FALSE; } } case NO_NEW_TAPE: search_request.prolong = FALSE; if (use_threads) { g_thread_join(tape_search); } log_add(L_ERROR, "no-tape [%s]", cmdargs->argv[1]); state->last_errmsg = stralloc(cmdargs->argv[1]); free_cmdargs(cmdargs); return FALSE; } /* NOTREACHED */ }
/* Open a socket to the dumper. Returns TRUE if everything is happy, FALSE otherwise. */ static gboolean open_read_socket(dump_info_t * info, char * split_diskbuffer, guint64 splitsize, guint64 fallback_splitsize) { in_port_t port = 0; int socket; int fd; int result; struct addrinfo *res; if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) { char *m; char *q; int save_errno = errno; char *qdiskname = quote_string(info->diskname); m = vstralloc("[localhost resolve failure: ", strerror(save_errno), "]", NULL); q = quote_string(m); putresult(TAPE_ERROR, "%s %s\n", info->handle, q); log_add(L_FAIL, "%s %s %s %d %s", info->hostname, qdiskname, info->timestamp, info->level, q); amfree(qdiskname); amfree(m); amfree(q); return FALSE; } socket = stream_server(res->ai_family, &port, 0, STREAM_BUFSIZE, 0); freeaddrinfo(res); if (socket < 0) { char *m; char *q; int save_errno = errno; char *qdiskname = quote_string(info->diskname); m = vstralloc("[port create failure: ", strerror(save_errno), "]", NULL); q = quote_string(m); putresult(TAPE_ERROR, "%s %s\n", info->handle, q); log_add(L_FAIL, "%s %s %s %d %s", info->hostname, qdiskname, info->timestamp, info->level, q); amfree(qdiskname); amfree(m); amfree(q); return FALSE; } putresult(PORT, "%d\n", port); fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); if (fd < 0) { char *m, *q; int save_errno = errno; char *qdiskname = quote_string(info->diskname); m = vstralloc("[port connect failure: ", strerror(save_errno), "]", NULL); q = quote_string(m); putresult(TAPE_ERROR, "%s %s\n", info->handle, q); log_add(L_FAIL, "%s %s %s %d %s", info->hostname, qdiskname, info->timestamp, info->level, q); amfree(qdiskname); aclose(socket); amfree(m); amfree(q); return FALSE; } else { aclose(socket); } info->source = taper_source_new(info->handle, PORT_WRITE, NULL, fd, split_diskbuffer, splitsize, fallback_splitsize); /* FIXME: This should be handled properly. */ g_assert(info->source != NULL); return TRUE; }
/* This function recieves the START_TAPER command from driver, and returns the attached timestamp. */ static gboolean find_first_tape(taper_state_t * state) { struct cmdargs *cmdargs; tape_search_request_t search_request; GThread * tape_search = NULL; gboolean use_threads; /* We save the value here in case it changes while we're running. */ use_threads = g_thread_supported(); search_request.state = state; search_request.prolong = TRUE; search_request.errmsg = NULL; if (use_threads) { tape_search = g_thread_create(tape_search_thread, &search_request, TRUE, NULL); } cmdargs = getcmd(); switch (cmdargs->cmd) { case START_TAPER: { gboolean search_result; state->driver_start_time = strdup(cmdargs->argv[1]); if (use_threads) { search_result = GPOINTER_TO_INT(g_thread_join(tape_search)); } else { search_result = GPOINTER_TO_INT(tape_search_thread(&search_request)); } if (search_result) { putresult(TAPER_OK, "\n"); } else { char *msg = quote_string(_("Could not find a tape to use")); putresult(TAPE_ERROR, "99-99999 %s\n", msg); log_add(L_ERROR, "no-tape [%s]", "Could not find a tape to use"); if (search_request.errmsg != NULL) { char *c, *c1; c = c1 = search_request.errmsg; while (*c != '\0') { if (*c == '\n') { *c = '\0'; log_add(L_WARNING,"%s", c1); c1 = c+1; } c++; } if (strlen(c1) > 1 ) log_add(L_WARNING,"%s", c1); } } amfree(search_request.errmsg); free_cmdargs(cmdargs); return TRUE; } case QUIT: search_request.prolong = FALSE; if (use_threads) { g_thread_join(tape_search); } free_cmdargs(cmdargs); return send_quitting(state); default: error("error [file_reader_side cmd %d argc %d]", cmdargs->cmd, cmdargs->argc); } g_assert_not_reached(); }
/* Send QUITTING message to driver and associated logging. Always returns false. */ static gboolean send_quitting(taper_state_t * state) { putresult(QUITTING, "\n"); g_fprintf(stderr,"taper: DONE\n"); cleanup(state); return FALSE; }
/* * Write out the buffer to the backing file */ static int databuf_flush( struct databuf * db) { struct cmdargs *cmdargs = NULL; int rc = 1; size_t size_to_write; size_t written; off_t left_in_chunk; char *arg_filename = NULL; char *new_filename = NULL; char *tmp_filename = NULL; char sequence[NUM_STR_SIZE]; int newfd; filetype_t save_type; char *q; int a; char *pc; /* * If there's no data, do nothing. */ if (db->dataout >= db->datain) { goto common_exit; } /* * See if we need to split this file. */ while (db->split_size > (off_t)0 && dumpsize >= db->split_size) { if( db->use == (off_t)0 ) { /* * Probably no more space on this disk. Request some more. */ putresult(RQ_MORE_DISK, "%s\n", handle); cmdargs = getcmd(); if(command_in_transit == NULL && (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) { command_in_transit = cmdargs; cmdargs = getcmd(); } if(cmdargs->cmd == CONTINUE) { /* * CONTINUE * serial * filename * chunksize * use */ a = 2; /* skip CONTINUE and serial */ if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: filename]")); /*NOTREACHED*/ } arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: chunksize]")); /*NOTREACHED*/ } db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]); db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: use]")); /*NOTREACHED*/ } db->use = OFF_T_ATOI(cmdargs->argv[a++]); if(a != cmdargs->argc) { error(_("error [chunker CONTINUE: too many args: %d != %d]"), cmdargs->argc, a); /*NOTREACHED*/ } if(strcmp(db->filename, arg_filename) == 0) { /* * Same disk, so use what room is left up to the * next chunk boundary or the amount we were given, * whichever is less. */ left_in_chunk = db->chunk_size - filesize; if(left_in_chunk > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += left_in_chunk; db->use -= left_in_chunk; } if(left_in_chunk > (off_t)0) { /* * We still have space in this chunk. */ break; } } else { /* * Different disk, so use new file. */ db->filename = newstralloc(db->filename, arg_filename); } } else if(cmdargs->cmd == ABORT) { abort_pending = 1; errstr = newstralloc(errstr, cmdargs->argv[1]); putresult(ABORT_FINISHED, "%s\n", handle); rc = 0; goto common_exit; } else { if(cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } error(_("error [bad command after RQ-MORE-DISK: \"%s\"]"), q); /*NOTREACHED*/ } } /* * Time to use another file. */ /* * First, open the new chunk file, and give it a new header * that has no cont_filename pointer. */ g_snprintf(sequence, SIZEOF(sequence), "%d", db->filename_seq); new_filename = newvstralloc(new_filename, db->filename, ".", sequence, NULL); tmp_filename = newvstralloc(tmp_filename, new_filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); /* Only a problem if db->filename has no /. */ *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (newfd == -1) { int save_errno = errno; char *m; if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("creating chunk holding file \"%s\": %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(db->fd); rc = 0; goto common_exit; } save_type = file.type; file.type = F_CONT_DUMPFILE; file.cont_filename[0] = '\0'; if(write_tapeheader(newfd, &file)) { int save_errno = errno; char *m; aclose(newfd); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("write_tapeheader file %s: %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * Now, update the header of the current file to point * to the next chunk, and then close it. */ if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) { char *m = vstrallocf(_("lseek holding file %s: %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); rc = 0; goto common_exit; } file.type = save_type; strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename)); file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0'; if(write_tapeheader(db->fd, &file)) { char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); unlink(tmp_filename); rc = 0; goto common_exit; } file.type = F_CONT_DUMPFILE; /* * Now shift the file descriptor. */ aclose(db->fd); db->fd = newfd; newfd = -1; /* * Update when we need to chunk again */ if(db->use <= (off_t)DISK_BLOCK_KB) { /* * Cheat and use one more block than allowed so we can make * some progress. */ db->split_size += (off_t)(2 * DISK_BLOCK_KB); db->use = (off_t)0; } else if(db->chunk_size > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += db->chunk_size; db->use -= db->chunk_size; } amfree(tmp_filename); amfree(new_filename); dumpsize += (off_t)DISK_BLOCK_KB; filesize = (off_t)DISK_BLOCK_KB; headersize += DISK_BLOCK_KB; db->filename_seq++; } /* * Write out the buffer */ size_to_write = (size_t)(db->datain - db->dataout); written = full_write(db->fd, db->dataout, size_to_write); if (written > 0) { db->dataout += written; dumpbytes += (off_t)written; } dumpsize += (dumpbytes / (off_t)1024); filesize += (dumpbytes / (off_t)1024); dumpbytes %= 1024; if (written < size_to_write) { if (errno != ENOSPC) { char *m = vstrallocf(_("data write: %s"), strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * NO-ROOM is informational only. Later, RQ_MORE_DISK will be * issued to use another holding disk. */ putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; goto common_exit; } if (db->datain == db->dataout) { /* * We flushed the whole buffer so reset to use it all. */ db->datain = db->dataout = db->buf; } common_exit: if (cmdargs) free_cmdargs(cmdargs); amfree(new_filename); /*@i@*/ amfree(tmp_filename); amfree(arg_filename); return rc; }
static int do_chunk( int infd, struct databuf * db) { size_t nread; char header_buf[DISK_BLOCK_BYTES]; startclock(); dumpsize = dumpbytes = filesize = (off_t)0; headersize = 0; memset(header_buf, 0, sizeof(header_buf)); /* * The first thing we should receive is the file header, which we * need to save into "file", as well as write out. Later, the * chunk code will rewrite it. */ nread = full_read(infd, header_buf, SIZEOF(header_buf)); if (nread != sizeof(header_buf)) { if(errno != 0) { errstr = vstrallocf(_("cannot read header: %s"), strerror(errno)); } else { errstr = vstrallocf(_("cannot read header: got %zd bytes instead of %zd"), nread, sizeof(header_buf)); } return 0; } parse_file_header(header_buf, &file, (size_t)nread); if(write_tapeheader(db->fd, &file)) { int save_errno = errno; char *m = vstrallocf(_("write_tapeheader file %s: %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); } return 0; } dumpsize += (off_t)DISK_BLOCK_KB; filesize = (off_t)DISK_BLOCK_KB; headersize += DISK_BLOCK_KB; /* * We've written the file header. Now, just write data until the * end. */ while ((nread = full_read(infd, db->buf, (size_t)(db->datalimit - db->datain))) > 0) { db->datain += nread; while(db->dataout < db->datain) { if(!databuf_flush(db)) { return 0; } } } while(db->dataout < db->datain) { if(!databuf_flush(db)) { return 0; } } if(dumpbytes > (off_t)0) { dumpsize += (off_t)1; /* count partial final KByte */ filesize += (off_t)1; } return 1; }
/* * Returns a file descriptor to the incoming port * on success, or -1 on error. */ static int startup_chunker( char * filename, off_t use, off_t chunksize, struct databuf * db) { int infd, outfd; char *tmp_filename, *pc; in_port_t data_port; int data_socket; int result; struct addrinfo *res; data_port = 0; if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) { errstr = newvstrallocf(errstr, _("could not resolve localhost: %s"), gai_strerror(result)); return -1; } data_socket = stream_server(res->ai_family, &data_port, 0, STREAM_BUFSIZE, 0); if (res) freeaddrinfo(res); if(data_socket < 0) { errstr = vstrallocf(_("error creating stream server: %s"), strerror(errno)); return -1; } putresult(PORT, "%d\n", data_port); infd = stream_accept(data_socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); aclose(data_socket); if(infd == -1) { errstr = vstrallocf(_("error accepting stream: %s"), strerror(errno)); return -1; } tmp_filename = vstralloc(filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) { int save_errno = errno; char *m = vstrallocf(_("holding file \"%s\": %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); amfree(tmp_filename); aclose(infd); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)use); return -2; } else { return -1; } } amfree(tmp_filename); databuf_init(db, outfd, filename, use, chunksize); db->filename_seq++; return infd; }
int main( int argc, char ** argv) { static struct databuf db; struct cmdargs *cmdargs; int infd; char *q = NULL; char *filename = NULL; off_t chunksize, use; times_t runtime; am_feature_t *their_features = NULL; int a; config_overrides_t *cfg_ovr = NULL; char *cfg_opt = NULL; char *m; /* * Configure program for internationalization: * 1) Only set the message locale for now. * 2) Set textdomain for all amanda related programs to "amanda" * We don't want to be forced to support dozens of message catalogs. */ setlocale(LC_MESSAGES, "C"); textdomain("amanda"); safe_fd(-1, 0); set_pname("chunker"); dbopen(DBG_SUBDIR_SERVER); /* Don't die when child closes pipe */ signal(SIGPIPE, SIG_IGN); add_amanda_log_handler(amanda_log_stderr); add_amanda_log_handler(amanda_log_trace_log); cfg_ovr = extract_commandline_config_overrides(&argc, &argv); if (argc > 1) cfg_opt = argv[1]; config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt); apply_config_overrides(cfg_ovr); if (config_errors(NULL) >= CFGERR_WARNINGS) { config_print_errors(); if (config_errors(NULL) >= CFGERR_ERRORS) { g_critical(_("errors processing config file")); } } safe_cd(); /* do this *after* config_init() */ check_running_as(RUNNING_AS_DUMPUSER); dbrename(get_config_name(), DBG_SUBDIR_SERVER); log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"), get_pname(), (long) getpid(), argv[0], VERSION); fflush(stderr); /* now, make sure we are a valid user */ signal(SIGPIPE, SIG_IGN); signal(SIGCHLD, SIG_IGN); cmdargs = getcmd(); if(cmdargs->cmd == START) { if(cmdargs->argc <= 1) error(_("error [dumper START: not enough args: timestamp]")); chunker_timestamp = newstralloc(chunker_timestamp, cmdargs->argv[1]); } else { log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); error(_("Didn't get START command")); } /* do {*/ cmdargs = getcmd(); switch(cmdargs->cmd) { case QUIT: break; case PORT_WRITE: /* * PORT-WRITE * handle * filename * host * features * disk * level * dumpdate * chunksize * progname * use * options */ a = 1; if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: handle]")); /*NOTREACHED*/ } handle = newstralloc(handle, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: filename]")); /*NOTREACHED*/ } filename = newstralloc(filename, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: hostname]")); /*NOTREACHED*/ } hostname = newstralloc(hostname, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: features]")); /*NOTREACHED*/ } am_release_feature_set(their_features); their_features = am_string_to_feature(cmdargs->argv[a++]); if (!their_features) { error(_("error [chunker PORT-WRITE: invalid feature string]")); /*NOTREACHED*/ } if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: diskname]")); /*NOTREACHED*/ } diskname = newstralloc(diskname, cmdargs->argv[a++]); if (qdiskname) amfree(qdiskname); qdiskname = quote_string(diskname); /* qdiskname is a global */ if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: level]")); /*NOTREACHED*/ } level = atoi(cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: dumpdate]")); /*NOTREACHED*/ } dumpdate = newstralloc(dumpdate, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: chunksize]")); /*NOTREACHED*/ } chunksize = OFF_T_ATOI(cmdargs->argv[a++]); chunksize = am_floor(chunksize, (off_t)DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: progname]")); /*NOTREACHED*/ } progname = newstralloc(progname, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: use]")); /*NOTREACHED*/ } use = am_floor(OFF_T_ATOI(cmdargs->argv[a++]), DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker PORT-WRITE: not enough args: options]")); /*NOTREACHED*/ } options = newstralloc(options, cmdargs->argv[a++]); if(a != cmdargs->argc) { error(_("error [chunker PORT-WRITE: too many args: %d != %d]"), cmdargs->argc, a); /*NOTREACHED*/ } if((infd = startup_chunker(filename, use, chunksize, &db)) < 0) { q = quote_string(vstrallocf(_("[chunker startup failed: %s]"), errstr)); putresult(TRYAGAIN, "%s %s\n", handle, q); error("startup_chunker failed: %s", errstr); } command_in_transit = NULL; if(infd >= 0 && do_chunk(infd, &db)) { char kb_str[NUM_STR_SIZE]; char kps_str[NUM_STR_SIZE]; double rt; runtime = stopclock(); rt = g_timeval_to_double(runtime); g_snprintf(kb_str, SIZEOF(kb_str), "%lld", (long long)(dumpsize - (off_t)headersize)); g_snprintf(kps_str, SIZEOF(kps_str), "%3.1lf", isnormal(rt) ? (double)dumpsize / rt : 0.0); errstr = newvstrallocf(errstr, "sec %s kb %s kps %s", walltime_str(runtime), kb_str, kps_str); m = vstrallocf("[%s]", errstr); q = quote_string(m); amfree(m); if(command_in_transit != NULL) { cmdargs = command_in_transit; command_in_transit = NULL; } else { cmdargs = getcmd(); } switch(cmdargs->cmd) { case DONE: putresult(DONE, "%s %lld %s\n", handle, (long long)(dumpsize - (off_t)headersize), q); log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); break; case BOGUS: case TRYAGAIN: case FAILED: case ABORT_FINISHED: if(dumpsize > (off_t)DISK_BLOCK_KB) { putresult(PARTIAL, "%s %lld %s\n", handle, (long long)(dumpsize - (off_t)headersize), q); log_add(L_PARTIAL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); } else { errstr = newvstrallocf(errstr, _("dumper returned %s"), cmdstr[cmdargs->cmd]); amfree(q); m = vstrallocf("[%s]",errstr); q = quote_string(m); amfree(m); putresult(FAILED, "%s %s\n", handle, q); log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); } default: break; } amfree(q); } else if(infd != -2) { if(q == NULL) { m = vstrallocf("[%s]", errstr); q = quote_string(m); amfree(m); } if(!abort_pending) { putresult(FAILED, "%s %s\n", handle, q); } log_add(L_FAIL, "%s %s %s %d [%s]", hostname, qdiskname, chunker_timestamp, level, errstr); amfree(q); } amfree(filename); amfree(db.filename); break; default: if(cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } putresult(BAD_COMMAND, "%s\n", q); amfree(q); break; } /* } while(cmdargs->cmd != QUIT); */ log_add(L_INFO, "pid-done %ld", (long)getpid()); amfree(errstr); amfree(chunker_timestamp); amfree(handle); amfree(hostname); amfree(diskname); amfree(qdiskname); amfree(dumpdate); amfree(progname); amfree(options); free_cmdargs(cmdargs); if (command_in_transit) free_cmdargs(command_in_transit); am_release_feature_set(their_features); their_features = NULL; dbclose(); return (0); /* exit */ }