Beispiel #1
0
/* 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;
}
Beispiel #2
0
/* 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 */
}
Beispiel #3
0
/*
 * 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;
}
Beispiel #4
0
/* 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();
}
Beispiel #5
0
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 */
}