Ejemplo n.º 1
0
static char *
hexstr(guint8 *p, int len)
{
    char *result = NULL;
    int i;

    for (i = 0; i < len; i++) {
	if (result)
	    result = newvstrallocf(result, "%s %02x", result, (guint)(*(p++)));
	else
	    result = g_strdup_printf("[%02x", (guint)(*(p++)));
    }
    result = newvstrallocf(result, "%s]", result);

    return result;
}
Ejemplo n.º 2
0
static int
scan_init(
    void *data,
    int rc,
    G_GNUC_UNUSED int nslots,
    int backwards,
    G_GNUC_UNUSED int searchable)
{
    changertrack_t *ct = ((changertrack_t*)data);

    if (rc) {
        *(ct->error_message) = newvstrallocf(*(ct->error_message),
                                             _("%scould not get changer info: %s\n"),
                                             *(ct->error_message), changer_resultstr);
        ct->output_callback(ct->output_data, *(ct->error_message));
        amfree(*(ct->error_message));
    }

    ct->backwards = backwards;
    return 0;
}
Ejemplo n.º 3
0
static char *quote_heredoc(
    char  *text,
    char  *delimiter_prefix)
{
    char *delimiter = stralloc(delimiter_prefix);
    int delimiter_n = 0;
    int delimiter_len = strlen(delimiter);
    char *quoted;

    /* keep picking delimiters until we find one that's not a line in TEXT */
    while (1) {
	char *line = text;
	char *c = text;
	gboolean found_delimiter = FALSE;

	while (1) {
	    if (*c == '\n' || *c == '\0') {
		int linelen = c - line;
		if (linelen == delimiter_len && 0 == strncmp(line, delimiter, linelen)) {
		    found_delimiter = TRUE;
		    break;
		}
		line = c+1;
	    }
	    if (!*c) break;
	    c++;
	}

	if (!found_delimiter)
	    break;

	delimiter = newvstrallocf(delimiter, "%s%d", delimiter_prefix, ++delimiter_n);
	delimiter_len = strlen(delimiter);
    }

    /* we have a delimiter .. now use it */
    quoted = vstrallocf("<<%s\n%s\n%s", delimiter, text, delimiter);
    amfree(delimiter);
    return quoted;
}
Ejemplo n.º 4
0
/*
 * Forks a ssh to the host listed in rc->hostname
 * Returns negative on error, with an errmsg in rc->errmsg.
 */
static int
runssh(
    struct tcp_conn *	rc,
    const char *	amandad_path,
    const char *	client_username,
    const char *	ssh_keys)
{
    int rpipe[2], wpipe[2];
    char *xamandad_path = (char *)amandad_path;
    char *xclient_username = (char *)client_username;
    char *xssh_keys = (char *)ssh_keys;

    memset(rpipe, -1, SIZEOF(rpipe));
    memset(wpipe, -1, SIZEOF(wpipe));
    if (pipe(rpipe) < 0 || pipe(wpipe) < 0) {
	rc->errmsg = newvstrallocf(rc->errmsg, _("pipe: %s"), strerror(errno));
	return (-1);
    }

    switch (rc->pid = fork()) {
    case -1:
	rc->errmsg = newvstrallocf(rc->errmsg, _("fork: %s"), strerror(errno));
	aclose(rpipe[0]);
	aclose(rpipe[1]);
	aclose(wpipe[0]);
	aclose(wpipe[1]);
	return (-1);
    case 0:
	dup2(wpipe[0], 0);
	dup2(rpipe[1], 1);
	break;
    default:
	rc->read = rpipe[0];
	aclose(rpipe[1]);
	rc->write = wpipe[1];
	aclose(wpipe[0]);
	return (0);
    }

    safe_fd(-1, 0);

    if(!xamandad_path || strlen(xamandad_path) <= 1) 
	xamandad_path = vstralloc(amlibexecdir, "/", "amandad",
				 versionsuffix(), NULL);
    if(!xclient_username || strlen(xclient_username) <= 1)
	xclient_username = CLIENT_LOGIN;
    if(!ssh_keys || strlen(ssh_keys) <= 1) {
	execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
	       rc->hostname, xamandad_path, "-auth=ssh", "amdump", "amindexd",
	       "amidxtaped", (char *)NULL);
    }
    else {
	execlp(SSH, SSH, SSH_OPTIONS, "-l", xclient_username,
	       "-i", xssh_keys, rc->hostname, xamandad_path, "-auth=ssh",
	       "amdump", "amindexd", "amidxtaped", (char *)NULL);
    }
    error("error: couldn't exec %s: %s", SSH, strerror(errno));

    /* should never go here, shut up compiler warning */
    return(-1);
}
Ejemplo n.º 5
0
/* This function checks the label of a single tape, which may or may not
 * have been loaded by the changer. With the addition of char *dev, and *slot,
 * it has the same interface as taper_scan. slot should be the slot where
 * this tape is found, or NULL if no changer is in use.
 * Return value is the same as taper_scan.
 */
int scan_read_label(
    char *dev,
    char *slot,
    char *desired_label,
    char** label,
    char** timestamp,
    char** error_message)
{
    Device * device;
    char *labelstr;
    DeviceStatusFlags device_status;
    char *new_label_errmsg;

    g_return_val_if_fail(dev != NULL, -1);

    if (*error_message == NULL)
        *error_message = stralloc("");

    *label = *timestamp = NULL;
    device = device_open(dev);
    g_assert(device != NULL);

    if (device->status != DEVICE_STATUS_SUCCESS ) {
        *error_message = newvstrallocf(*error_message,
                                       _("%sError opening device %s: %s.\n"),
                                       *error_message, dev,
                                       device_error_or_status(device));
        g_object_unref(device);
        amfree(*timestamp);
        amfree(*label);
        return -1;
    }

    if (!device_configure(device, TRUE)) {
        *error_message = newvstrallocf(*error_message,
                                       _("%sError configuring device %s: %s.\n"),
                                       *error_message, dev,
                                       device_error_or_status(device));
        g_object_unref(device);
        amfree(*timestamp);
        amfree(*label);
        return -1;
    }

    device_status = device_read_label(device);

    if (device_status == DEVICE_STATUS_SUCCESS && device->volume_label != NULL) {
        *label = g_strdup(device->volume_label);
        *timestamp = strdup(device->volume_time);
    } else if (device_status & DEVICE_STATUS_VOLUME_UNLABELED) {
        if (!getconf_seen(CNF_LABEL_NEW_TAPES)) {
            *error_message = newvstrallocf(*error_message,
                                           _("%sFound an empty or non-amanda tape.\n"),
                                           *error_message);
            g_object_unref(device);
            return -1;
        }

        /* If we got a header, but the Device doesn't think it's labeled, then this
         * tape probably has some data on it, so refuse to automatically label it */
        if (device->volume_header && device->volume_header->type != F_EMPTY) {
            *error_message = newvstrallocf(*error_message,
                                           _("%sFound a non-amanda tape; check and relabel it with 'amlabel -f'\n"),
                                           *error_message);
            g_object_unref(device);
            return -1;
        }
        g_object_unref(device);

        *label = find_brand_new_tape_label(&new_label_errmsg);
        if (*label != NULL) {
            *timestamp = stralloc("X");
            *error_message = newvstrallocf(*error_message,
                                           _("%sFound an empty tape, will label it `%s'.\n"),
                                           *error_message, *label);

            return 3;
        }
        *error_message = newvstrallocf(*error_message,
                                       _("%s%s.\n"),
                                       *error_message, new_label_errmsg);

        return -1;
    } else {
        char * label_errstr;
        label_errstr = g_strdup_printf(_("Error reading label: %s.\n"),
                                       device_error_or_status(device));
        *error_message = newvstralloc(*error_message, *error_message,
                                      label_errstr, NULL);
        g_free(label_errstr);
        return -1;
    }

    g_assert(*label != NULL && *timestamp != NULL);
    g_object_unref(device);

    *error_message = newvstrallocf(*error_message,
                                   _("%sread label `%s', date `%s'.\n"),
                                   *error_message, *label, *timestamp);

    /* Register this with the barcode database, even if its not ours. */
    if (slot != NULL) {
        changer_label(slot, *label);
    }

    if (desired_label != NULL && strcmp(*label, desired_label) == 0) {
        /* Got desired label. */
        return 1;
    }

    /* Is this actually an acceptable tape? */
    labelstr = getconf_str(CNF_LABELSTR);
    if(!match(labelstr, *label)) {
        *error_message = newvstrallocf(*error_message,
                                       _("%slabel \"%s\" doesn't match \"%s\".\n"),
                                       *error_message, *label, labelstr);

        return -1;
    } else {
        tape_t *tp;
        if (strcmp(*timestamp, "X") == 0) {
            /* new, labeled tape. */
            return 1;
        }

        tp = lookup_tapelabel(*label);

        if(tp == NULL) {
            *error_message =
                newvstrallocf(*error_message,
                              _("%slabel \"%s\" matches labelstr but it is"
                                " not listed in the tapelist file.\n"),
                              *error_message, *label);
            return -1;
        } else if(tp != NULL && !reusable_tape(tp)) {
            *error_message =
                newvstrallocf(*error_message,
                              _("%sTape with label %s is still active"
                                " and cannot be overwritten.\n"),
                              *error_message, *label);
            return -1;
        }
    }

    /* Yay! We got a good tape! */
    return 2;
}
Ejemplo n.º 6
0
int
scan_slot(
    void *data,
    int rc,
    char *slotstr,
    char *device)
{
    int label_result;
    changertrack_t *ct = ((changertrack_t*)data);
    int result;

    if (ct->prolong_callback &&
            !ct->prolong_callback(ct->prolong_data)) {
        return 1;
    }

    if (ct->persistent != NULL) {
        gpointer key;
        gpointer value;
        if (g_hash_table_lookup_extended(ct->persistent->scanned_slots,
                                         slotstr, &key, &value)) {
            /* We already returned this slot in a previous invocation,
               skip it now. */
            return 0;
        }
    }

    if (*(ct->error_message) == NULL)
        *(ct->error_message) = stralloc("");

    switch (rc) {
    default:
        *(ct->error_message) = newvstrallocf(*(ct->error_message),
                                             _("%sfatal changer error: slot %s: %s\n"),
                                             *(ct->error_message), slotstr, changer_resultstr);
        result = 1;
        break;

    case 1:
        *(ct->error_message) = newvstrallocf(*(ct->error_message),
                                             _("%schanger error: slot %s: %s\n"),
                                             *(ct->error_message), slotstr, changer_resultstr);
        result = 0;
        break;

    case 0:
        *(ct->error_message) = newvstrallocf(*(ct->error_message),
                                             _("slot %s:"), slotstr);
        amfree(*ct->gotlabel);
        amfree(*ct->timestamp);
        label_result = scan_read_label(device, slotstr,
                                       ct->wantlabel, ct->gotlabel,
                                       ct->timestamp, ct->error_message);
        if (label_result == 1 || label_result == 3 ||
                (label_result == 2 && !ct->backwards)) {
            *(ct->tapedev) = stralloc(device);
            ct->tape_status = label_result;
            amfree(ct->slotstr);
            ct->slotstr = stralloc(slotstr);
            result = 1;
        } else {
            if ((label_result == 2) && (ct->first_labelstr_slot == NULL))
                ct->first_labelstr_slot = stralloc(slotstr);
            result = 0;
        }
        break;
    }
    ct->output_callback(ct->output_data, *(ct->error_message));
    amfree(*(ct->error_message));
    return result;
}
Ejemplo n.º 7
0
static void
amindexd_response(
    void *datap,
    pkt_t *pkt,
    security_handle_t *sech)
{
    int ports[NSTREAMS], *response_error = datap;
    guint i;
    char *p;
    char *tok;
    char *extra = NULL;

    assert(response_error != NULL);
    assert(sech != NULL);

    if (pkt == NULL) {
	errstr = newvstrallocf(errstr, _("[request failed: %s]"),
			     security_geterror(sech));
	*response_error = 1;
	return;
    }

    if (pkt->type == P_NAK) {
#if defined(PACKET_DEBUG)
	dbprintf(_("got nak response:\n----\n%s\n----\n\n"), pkt->body);
#endif

	tok = strtok(pkt->body, " ");
	if (tok == NULL || strcmp(tok, "ERROR") != 0)
	    goto bad_nak;

	tok = strtok(NULL, "\n");
	if (tok != NULL) {
	    errstr = newvstrallocf(errstr, "NAK: %s", tok);
	    *response_error = 1;
	} else {
bad_nak:
	    errstr = newvstrallocf(errstr, _("request NAK"));
	    *response_error = 2;
	}
	return;
    }

    if (pkt->type != P_REP) {
	errstr = newvstrallocf(errstr, _("received strange packet type %s: %s"),
			      pkt_type2str(pkt->type), pkt->body);
	*response_error = 1;
	return;
    }

#if defined(PACKET_DEBUG)
    g_fprintf(stderr, _("got response:\n----\n%s\n----\n\n"), pkt->body);
#endif

    for(i = 0; i < NSTREAMS; i++) {
        ports[i] = -1;
        streams[i].fd = NULL;
    }

    p = pkt->body;
    while((tok = strtok(p, " \n")) != NULL) {
	p = NULL;

	/*
	 * Error response packets have "ERROR" followed by the error message
	 * followed by a newline.
	 */
	if (strcmp(tok, "ERROR") == 0) {
	    tok = strtok(NULL, "\n");
	    if (tok == NULL) {
	        errstr = newvstrallocf(errstr, _("[bogus error packet]"));
	    } else {
		errstr = newvstrallocf(errstr, "%s", tok);
	    }
	    *response_error = 2;
	    return;
	}


        /*
         * Regular packets have CONNECT followed by three streams
         */
        if (strcmp(tok, "CONNECT") == 0) {

	    /*
	     * Parse the three stream specifiers out of the packet.
	     */
	    for (i = 0; i < NSTREAMS; i++) {
		tok = strtok(NULL, " ");
		if (tok == NULL || strcmp(tok, streams[i].name) != 0) {
		    extra = g_strdup_printf(
			   _("CONNECT token is \"%s\": expected \"%s\""),
			   tok ? tok : _("(null)"), streams[i].name);
		    goto parse_error;
		}
		tok = strtok(NULL, " \n");
		if (tok == NULL || sscanf(tok, "%d", &ports[i]) != 1) {
		    extra = g_strdup_printf(
			   _("CONNECT %s token is \"%s\" expected a port number"),
			   streams[i].name, tok ? tok : _("(null)"));
		    goto parse_error;
		}
	    }
	    continue;
	}

	/*
	 * OPTIONS [options string] '\n'
	 */
	if (strcmp(tok, "OPTIONS") == 0) {
	    tok = strtok(NULL, "\n");
	    if (tok == NULL) {
		extra = g_strdup(_("OPTIONS token is missing"));
		goto parse_error;
	    }
#if 0
	    tok_end = tok + strlen(tok);
	    while((p = strchr(tok, ';')) != NULL) {
		*p++ = '\0';
		if(strncmp_const(tok, "features=") == 0) {
		    tok += sizeof("features=") - 1;
		    am_release_feature_set(their_features);
		    if((their_features = am_string_to_feature(tok)) == NULL) {
			errstr = newvstrallocf(errstr,
				      _("OPTIONS: bad features value: %s"),
				      tok);
			goto parse_error;
		    }
		}
		tok = p;
	    }
#endif
	    continue;
	}
#if 0
	extra = g_strdup_printf(_("next token is \"%s\": expected \"CONNECT\", \"ERROR\" or \"OPTIONS\""), tok ? tok : _("(null)"));
	goto parse_error;
#endif
    }

    /*
     * Connect the streams to their remote ports
     */
    for (i = 0; i < NSTREAMS; i++) {
/*@i@*/	if (ports[i] == -1)
	    continue;
	streams[i].fd = security_stream_client(sech, ports[i]);
	if (streams[i].fd == NULL) {
	    errstr = newvstrallocf(errstr,
			_("[could not connect %s stream: %s]"),
			streams[i].name, security_geterror(sech));
	    goto connect_error;
	}
    }
    /*
     * Authenticate the streams
     */
    for (i = 0; i < NSTREAMS; i++) {
	if (streams[i].fd == NULL)
	    continue;
	if (security_stream_auth(streams[i].fd) < 0) {
	    errstr = newvstrallocf(errstr,
		_("[could not authenticate %s stream: %s]"),
		streams[i].name, security_stream_geterror(streams[i].fd));
	    goto connect_error;
	}
    }

    /*
     * The MESGFD and DATAFD streams are mandatory.  If we didn't get
     * them, complain.
     */
    if (streams[MESGFD].fd == NULL) {
        errstr = newvstrallocf(errstr, _("[couldn't open MESG streams]"));
        goto connect_error;
    }

    /* everything worked */
    *response_error = 0;
    amindexd_alive = 1;
    return;

parse_error:
    errstr = newvstrallocf(errstr,
			  _("[parse of reply message failed: %s]"),
			  extra ? extra : _("(no additional information)"));
    amfree(extra);
    *response_error = 2;
    return;

connect_error:
    stop_amindexd();
    *response_error = 1;
}
Ejemplo n.º 8
0
int
check_status(
    pid_t	pid,
    amwait_t	w,
    int		mesgfd)
{
    char *thiserr = NULL;
    char *str, *strX;
    int ret, sig, rc;

    str = childstr(pid);

    if(WIFSIGNALED(w)) {
	ret = 0;
	rc = sig = WTERMSIG(w);
    } else {
	sig = 0;
	rc = ret = WEXITSTATUS(w);
    }

    if(pid == indexpid) {
	/*
	 * Treat an index failure (other than signal) as a "STRANGE"
	 * rather than an error so the dump goes ahead and gets processed
	 * but the failure is noted.
	 */
	if(ret != 0) {
	    fdprintf(mesgfd, _("? index %s returned %d\n"), str, ret);
	    rc = 0;
	}
	indexpid = -1;
	strX = "index";
    } else if(pid == comppid) {
	/*
	 * compress returns 2 sometimes, but it is ok.
	 */
#ifndef HAVE_GZIP
	if(ret == 2) {
	    rc = 0;
	}
#endif
	comppid = -1;
	strX = "compress";
    } else if(pid == dumppid && tarpid == -1) {
        /*
	 * Ultrix dump returns 1 sometimes, but it is ok.
	 */
#ifdef DUMP_RETURNS_1
        if(ret == 1) {
	    rc = 0;
	}
#endif
	dumppid = -1;
	strX = "dump";
    } else if(pid == tarpid) {
	if (ret == 1) {
	    rc = 0;
	}
	/*
	 * tar bitches about active filesystems, but we do not care.
	 */
#ifdef IGNORE_TAR_ERRORS
        if(ret == 2) {
	    rc = 0;
	}
#endif
	dumppid = tarpid = -1;
	strX = "dump";
    } else if(pid == application_api_pid) {
	strX = "Application";
    } else {
	strX = "unknown";
    }

    if(rc == 0) {
	return 0;				/* normal exit */
    }

    if(ret == 0) {
	thiserr = vstrallocf(_("%s (%d) %s got signal %d"), strX, (int)pid, str,
			     sig);
    } else {
	thiserr = vstrallocf(_("%s (%d) %s returned %d"), strX, (int)pid, str, ret);
    }

    fdprintf(mesgfd, "? %s\n", thiserr);

    if(errorstr) {
	errorstr =  newvstrallocf(errorstr, "%s, %s", errorstr, thiserr);
	amfree(thiserr);
    } else {
	errorstr = thiserr;
	thiserr = NULL;
    }
    return 1;
}
Ejemplo n.º 9
0
/*
 * 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;
}
Ejemplo n.º 10
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 */
}