static void render_manifest_headers(struct http_request *hr, strbuf sb)
{
  httpd_request *r = (httpd_request *) hr;
  rhizome_manifest *m = r->manifest;
  strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Id: %s\r\n", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic));
  strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Version: %"PRIu64"\r\n", m->version);
  strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Filesize: %"PRIu64"\r\n", m->filesize);
  if (m->filesize != 0)
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Filehash: %s\r\n", alloca_tohex_rhizome_filehash_t(m->filehash));
  if (m->has_bundle_key)
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-BK: %s\r\n", alloca_tohex_rhizome_bk_t(m->bundle_key));
  if (m->has_date)
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Date: %"PRIu64"\r\n", m->date);
  if (m->name) {
    strbuf_puts(sb, "Serval-Rhizome-Bundle-Name: ");
    strbuf_append_quoted_string(sb, m->name);
    strbuf_puts(sb, "\r\n");
  }
  if (m->service)
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Service: %s\r\n", m->service);
  assert(m->authorship != AUTHOR_LOCAL);
  if (m->authorship == AUTHOR_AUTHENTIC)
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Author: %s\r\n", alloca_tohex_sid_t(m->author));
  assert(m->haveSecret);
  {
    char secret[RHIZOME_BUNDLE_KEY_STRLEN + 1];
    rhizome_bytes_to_hex_upper(m->cryptoSignSecret, secret, RHIZOME_BUNDLE_KEY_BYTES);
    strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Secret: %s\r\n", secret);
  }
  strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Rowid: %"PRIu64"\r\n", m->rowid);
  strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Inserttime: %"PRIu64"\r\n", m->inserttime);
}
void msp_debug()
{
  time_ms_t now = gettime_ms();
  struct msp_sock *p=root;
  DEBUGF("Msp sockets;");
  while(p){
    DEBUGF("State %d, from %s:%d to %s:%d, next %"PRId64"ms, ack %"PRId64"ms timeout %"PRId64"ms", 
      p->state, 
      alloca_tohex_sid_t(p->header.local.sid), p->header.local.port, 
      alloca_tohex_sid_t(p->header.remote.sid), p->header.remote.port,
      (p->next_action - now),
      (p->next_ack - now),
      (p->timeout - now));
    p=p->_next;
  }
}
int
dna_helper_enqueue(struct subscriber *source, mdp_port_t source_port, const char *did)
{
  if (config.debug.dnahelper)
    DEBUGF("DNAHELPER request did=%s sid=%s", did, alloca_tohex_sid_t(source->sid));
  if (dna_helper_pid == 0)
    return 0;
  // Only try to restart a DNA helper process if the previous one is well and truly gone.
  if (dna_helper_pid == -1 && dna_helper_stdin == -1 && dna_helper_stdout == -1 && dna_helper_stderr == -1) {
    if (dna_helper_start() == -1) {
      /* Something broke, bail out */
      return WHY("DNAHELPER start failed");
    }
  }
  /* Write request to dna helper.
     Request takes form:  SID-of-Requestor|DID|\n
     By passing the requestor's SID to the helper, we don't need to maintain
     any state, as all we have to do is wait for responses from the helper,
     which will include the requestor's SID.
  */
  if (dna_helper_stdin == -1)
    return 0;
  if (request_bufptr && request_bufptr != request_buffer) {
    WARNF("DNAHELPER currently sending request %s -- dropping new request", request_buffer);
    return 0;
  }
  if (awaiting_reply) {
    WARN("DNAHELPER currently awaiting reply -- dropping new request");
    return 0;
  }
  char buffer[sizeof request_buffer];
  strbuf b = strbuf_local(request_bufptr == request_buffer ? buffer : request_buffer, sizeof buffer);
  strbuf_tohex(b, SID_STRLEN, source->sid.binary);
  strbuf_putc(b, '|');
  strbuf_puts(b, did);
  strbuf_putc(b, '|');
  strbuf_putc(b, '\n');
  if (strbuf_overrun(b)) {
    WHYF("DNAHELPER request buffer overrun: %s -- request not sent", strbuf_str(b));
    request_bufptr = request_bufend = NULL;
  } else {
    if (strbuf_str(b) != request_buffer) {
      if (strcmp(strbuf_str(b), request_buffer) != 0)
	WARNF("DNAHELPER overwriting unsent request %s", request_buffer);
      strcpy(request_buffer, strbuf_str(b));
    }
    request_bufptr = request_buffer;
    request_bufend = request_buffer + strbuf_len(b);
    request_source = source;
    request_port = source_port;
    strncpy(request_did, did, sizeof request_did);
    request_did[sizeof request_did - 1] = '\0';
  }
  if (dna_helper_started) {
    sched_requests.poll.fd = dna_helper_stdin;
    watch(&sched_requests);
  }
  return 1;
}
static int insert_mime_part_end(struct http_request *hr)
{
  httpd_request *r = (httpd_request *) hr;
  if (r->u.insert.current_part == PART_AUTHOR) {
    if (   r->u.insert.author_hex_len != sizeof r->u.insert.author_hex
	|| strn_to_sid_t(&r->u.insert.author, r->u.insert.author_hex, NULL) == -1
    )
      return http_response_form_part(r, "Invalid", PART_AUTHOR, r->u.insert.author_hex, r->u.insert.author_hex_len);
    r->u.insert.received_author = 1;
    if (config.debug.rhizome)
      DEBUGF("received %s = %s", PART_AUTHOR, alloca_tohex_sid_t(r->u.insert.author));
  }
  else if (r->u.insert.current_part == PART_SECRET) {
    if (   r->u.insert.secret_hex_len != sizeof r->u.insert.secret_hex
	|| strn_to_rhizome_bk_t(&r->u.insert.bundle_secret, r->u.insert.secret_hex, NULL) == -1
    )
      return http_response_form_part(r, "Invalid", PART_SECRET, r->u.insert.secret_hex, r->u.insert.secret_hex_len);
    r->u.insert.received_secret = 1;
    if (config.debug.rhizome)
      DEBUGF("received %s = %s", PART_SECRET, alloca_tohex_rhizome_bk_t(r->u.insert.bundle_secret));
  }
  else if (r->u.insert.current_part == PART_MANIFEST) {
    r->u.insert.received_manifest = 1;
    int result = insert_make_manifest(r);
    if (result)
      return result;
    if (r->manifest->has_id && r->u.insert.received_secret)
      rhizome_apply_bundle_secret(r->manifest, &r->u.insert.bundle_secret);
    if (r->manifest->service == NULL)
      rhizome_manifest_set_service(r->manifest, RHIZOME_SERVICE_FILE);
    if (rhizome_fill_manifest(r->manifest, NULL, r->u.insert.received_author ? &r->u.insert.author: NULL) == -1) {
      WHY("rhizome_fill_manifest() failed");
      return 500;
    }
    if (r->manifest->is_journal) {
      http_request_simple_response(&r->http, 403, "Insert not supported for journals");
      return 403;
    }
    assert(r->manifest != NULL);
  }
  else if (r->u.insert.current_part == PART_PAYLOAD) {
    r->u.insert.received_payload = 1;
    if (r->u.insert.payload_status == RHIZOME_PAYLOAD_STATUS_NEW)
      r->u.insert.payload_status = rhizome_finish_write(&r->u.insert.write);
    if (r->u.insert.payload_status == RHIZOME_PAYLOAD_STATUS_ERROR) {
      WHYF("rhizome_finish_write() returned status = %d", r->u.insert.payload_status);
      return 500;
    }
  } else
    FATALF("current_part = %s", alloca_str_toprint(r->u.insert.current_part));
  r->u.insert.current_part = NULL;
  return 0;
}
int main(int argc, char **argv)
{
  int i;
  for (i = 1; i < argc; ++i) {
    int fd = open(argv[i], O_RDONLY);
    if (fd == -1) {
      perror("open");
      exit(1);
    }
    struct stat st;
    fstat(fd, &st);
    char *buf = malloc(st.st_size);
    if (!buf) {
      perror("malloc");
      exit(1);
    }
    if (read(fd, buf, st.st_size) != st.st_size) {
      perror("read");
      exit(1);
    }
    struct cf_om_node *root = NULL;
    int ret = cf_om_parse(argv[i], buf, st.st_size, &root);
    close(fd);
    DEBUGF("ret = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), ret)));
    //cf_dump_node(root, 0);
    struct config_main config;
    memset(&config, 0, sizeof config);
    cf_dfl_config_main(&config);
    int result = root ? cf_opt_config_main(&config, root) : CFEMPTY;
    cf_om_free_node(&root);
    free(buf);
    DEBUGF("result = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), result)));
    DEBUGF("config.log.file.path = %s", alloca_str_toprint(config.log.file.path));
    DEBUGF("config.log.file.show_pid = %d", config.log.file.show_pid);
    DEBUGF("config.log.file.show_time = %d", config.log.file.show_time);
    DEBUGF("config.server.chdir = %s", alloca_str_toprint(config.server.chdir));
    DEBUGF("config.debug.verbose = %d", config.debug.verbose);
    DEBUGF("config.directory.service = %s", alloca_tohex_sid_t(config.directory.service));
    DEBUGF("config.rhizome.api.addfile.allow_host = %s", inet_ntoa(config.rhizome.api.addfile.allow_host));
    unsigned j;
    for (j = 0; j < config.mdp.iftype.ac; ++j) {
      DEBUGF("config.mdp.iftype.%u", config.mdp.iftype.av[j].key);
      DEBUGF("   .tick_ms = %u", config.mdp.iftype.av[j].value.tick_ms);
    }
    for (j = 0; j < config.dna.helper.argv.ac; ++j) {
      DEBUGF("config.dna.helper.argv.%u=%s", config.dna.helper.argv.av[j].key, config.dna.helper.argv.av[j].value);
    }
    for (j = 0; j < config.rhizome.direct.peer.ac; ++j) {
      DEBUGF("config.rhizome.direct.peer.%s", config.rhizome.direct.peer.av[j].key);
      DEBUGF("   .protocol = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.protocol));
      DEBUGF("   .host = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.host));
      DEBUGF("   .port = %u", config.rhizome.direct.peer.av[j].value.port);
    }
    for (j = 0; j < config.interfaces.ac; ++j) {
      DEBUGF("config.interfaces.%u", config.interfaces.av[j].key);
      DEBUGF("   .exclude = %d", config.interfaces.av[j].value.exclude);
      DEBUGF("   .match = [");
      unsigned k;
      for (k = 0; k < config.interfaces.av[j].value.match.patc; ++k)
	DEBUGF("             %s", alloca_str_toprint(config.interfaces.av[j].value.match.patv[k]));
      DEBUGF("            ]");
      DEBUGF("   .type = %d", config.interfaces.av[j].value.type);
      DEBUGF("   .port = %u", config.interfaces.av[j].value.port);
      DEBUGF("   .drop_broadcasts = %llu", (unsigned long long) config.interfaces.av[j].value.drop_broadcasts);
      DEBUGF("   .drop_unicasts = %llu", (unsigned long long) config.interfaces.av[j].value.drop_unicasts);
      DEBUGF("   .drop_packets = %llu", (unsigned long long) config.interfaces.av[j].value.drop_packets);
    }
    for (j = 0; j < config.hosts.ac; ++j) {
      char sidhex[SID_STRLEN + 1];
      tohex(sidhex, SID_STRLEN, config.hosts.av[j].key.binary);
      DEBUGF("config.hosts.%s", sidhex);
      DEBUGF("   .interface = %s", alloca_str_toprint(config.hosts.av[j].value.interface));
      DEBUGF("   .address = %s", inet_ntoa(config.hosts.av[j].value.address));
      DEBUGF("   .port = %u", config.hosts.av[j].value.port);
    }
  }
  exit(0);
}
Beispiel #6
0
// Function for adding a file to the Rhizome store for complex RPC part.
int _rpc_add_file_to_store (char *filehash, sid_t sid, char *rpc_name, char *filepath) {
	int result = 0;
    // Construct the manifest and write it to the manifest file. We have to treat it differently if the call is braodcasted.
	char tmp_manifest_file_name[] = "/tmp/mf_XXXXXX";
	if (is_sid_t_broadcast(sid)) {
		int manifest_size = strlen("service=RPC\nname=f_\nsender=\n") + strlen(rpc_name) + strlen(alloca_tohex_sid_t(sid));
		char manifest_str[manifest_size];
		sprintf(manifest_str, "service=RPC\nname=f_%s\nsender=%s\n", rpc_name, alloca_tohex_sid_t(my_subscriber->sid));
		_rpc_write_tmp_file(tmp_manifest_file_name, manifest_str, strlen(manifest_str));
	} else {
		int manifest_size = strlen("service=RPC\nnamef_=\nsender=\nrecipient=\n") + strlen(rpc_name) + (strlen(alloca_tohex_sid_t(sid)) * 2);
	    char manifest_str[manifest_size];
	    sprintf(manifest_str, "service=RPC\nname=f_%s\nsender=%s\nrecipient=%s\n", rpc_name, alloca_tohex_sid_t(my_subscriber->sid), alloca_tohex_sid_t(sid));
	    _rpc_write_tmp_file(tmp_manifest_file_name, manifest_str, strlen(manifest_str));
	}

    // Init the cURL stuff.
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL *curl_handler = NULL;
    CURLcode curl_res;
    struct CurlResultMemory curl_result_memory;
    _rpc_curl_init_memory(&curl_result_memory);
    if ((curl_handler = curl_easy_init()) == NULL) {
        pfatal("Failed to create curl handle in post. Aborting.");
        result = -1;
        goto clean_rhizome_insert;
    }

    // Declare all needed headers forms and URLs.
    struct curl_slist *header = NULL;
    struct curl_httppost *formpost = NULL;
    struct curl_httppost *lastptr = NULL;
    char *url_insert = "http://localhost:4110/restful/rhizome/insert";

    // Set basic cURL options (see function).
    _rpc_curl_set_basic_opt(url_insert, curl_handler, header);
    curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, _rpc_curl_write_response);
    curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, (void *) &curl_result_memory);

    // Add the manifest and payload form and add the form to the cURL request.
    _rpc_curl_add_file_form(tmp_manifest_file_name, filepath, curl_handler, formpost, lastptr);

    // Perfom request, which means insert the RPC file to the store.
    curl_res = curl_easy_perform(curl_handler);
    if (curl_res != CURLE_OK) {
        pfatal("CURL failed (post add file): %s. Aborting.", curl_easy_strerror(curl_res));
        result = -1;
        goto clean_rhizome_insert_all;
    }

	// Search for the last '=' and skip it to get the filehash.
	char *response_filehash = &strrchr((char *) curl_result_memory.memory, '=')[1];
	// Store the filehash in the variable and make sure it is \0 terminated.
	memcpy(filehash, response_filehash, 128);
	memcpy(&filehash[128], "\0", 1);

    // Clean up.
	clean_rhizome_insert_all:
		curl_slist_free_all(header);
    clean_rhizome_insert:
		curl_easy_cleanup(curl_handler);
		_rpc_curl_free_memory(&curl_result_memory);
		remove(tmp_manifest_file_name);
        curl_global_cleanup();

    return result;
}
Beispiel #7
0
// Download file if it is an complex RPC.
int _rpc_download_file (char *fpath, char *rpc_name, char *sid) {
    int return_code = -1;

	// Init the cURL stuff.
    curl_global_init(CURL_GLOBAL_DEFAULT);
    CURL *curl_handler = NULL;
    CURLcode curl_res;
    struct CurlResultMemory curl_result_memory;
    _rpc_curl_init_memory(&curl_result_memory);
    if ((curl_handler = curl_easy_init()) == NULL) {
        pfatal("Failed to create curl handle in post. Aborting.");
        return_code = -1;
        goto clean_rhizome_server_listener;
    }

	// Declare all needed headers forms and URLs.
    struct curl_slist *header = NULL;
    char *url_get = "http://localhost:4110/restful/rhizome/bundlelist.json";

    // Set basic cURL options and a callback function, where results from the cURL call are handled.
    _rpc_curl_set_basic_opt(url_get, curl_handler, header);
    curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, _rpc_curl_write_response);
    curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, (void *) &curl_result_memory);

    // Get the bundlelist.
    curl_res = curl_easy_perform(curl_handler);
    if (curl_res != CURLE_OK) {
        pfatal("CURL failed (get server download file): %s. Aborting.", curl_easy_strerror(curl_res));
        return_code = -1;
        goto clean_rhizome_server_listener_all;
    }

    {
        // Write the bundlelist to a string for manipulations.
        char json_string[(size_t)curl_result_memory.size - 1];
        memcpy(json_string, curl_result_memory.memory, (size_t)curl_result_memory.size - 1);

        // Parse JSON:
        // Init, ...
        cJSON *incoming_json = cJSON_Parse(json_string);
        // ... get the 'rows' entry, ...
        cJSON *rows = cJSON_GetObjectItem(incoming_json, "rows");
        // (if there are no files in the store, abort)
		int num_rows = cJSON_GetArraySize(rows);
        if (num_rows <= 0) {
            return_code = 0;
            goto clean_rhizome_server_listener_all;
        }

		int i;
		for (i = 0; i < num_rows; i++) {
	        // ... consider only the recent file, ...
	        cJSON *recent_file = cJSON_GetArrayItem(rows, i);
	        // ... get the 'service', ...
	        char *service = cJSON_GetArrayItem(recent_file, 2)->valuestring;
			// ... the insert time.
	        int64_t in_time = (int64_t) cJSON_GetArrayItem(recent_file, 4)->valuedouble;
	        // ... the sender from the recent file.
	        char *sender = cJSON_GetArrayItem(recent_file, 11)->valuestring;
	        // ... the recipient from the recent file.
	        char *recipient = cJSON_GetArrayItem(recent_file, 12)->valuestring;
	        // ... the recipient from the recent file.
	        char *name = cJSON_GetArrayItem(recent_file, 13)->valuestring;

			char rpc_cmp_name[4 + strlen(rpc_name)];
			sprintf(rpc_cmp_name, "f_%s", rpc_name);

	        // Check, if this file is an RPC packet and if it is not from but for the client.
	        int service_is_rpc = !strncmp(service, "RPC", strlen("RPC"));
	        int filename_is_right = !strncmp(name, rpc_cmp_name, strlen(rpc_cmp_name));
            int right_client = sender && (!strncmp(alloca_tohex_sid_t(SID_BROADCAST), sid, strlen(sid)) || !strncmp(sender, sid, strlen(sid)));
			int not_my_file = 0;
			if (recipient) {
	        	not_my_file = recipient != NULL && !strcmp(recipient, alloca_tohex_sid_t(my_subscriber->sid));
			} else {
				not_my_file = 1;
			}

	        // If this is an interesting file: handle it.
	        if (service_is_rpc  && not_my_file && filename_is_right && right_client) {
	            // Free everyhing, again.

				if ((curl_handler = curl_easy_init()) == NULL) {
			        pfatal("Failed to create curl handle in post. Aborting.");
			        return_code = -1;
			        goto clean_rhizome_server_listener;
			    }

	            _rpc_curl_reinit_memory(&curl_result_memory);
	            curl_slist_free_all(header);
	            header = NULL;

				char rpc_down_name[4 + strlen(rpc_name) + sizeof(in_time) + sizeof(sender)];
				sprintf(rpc_down_name, "f_%s_%" PRId64 "_%s", rpc_name, in_time, sender);

				char filepath[strlen(RPC_TMP_FOLDER) + strlen(rpc_down_name)];
				sprintf(filepath, "%s%s", RPC_TMP_FOLDER, rpc_down_name);
				mkdir(RPC_TMP_FOLDER, 0700);
				FILE* rpc_file = fopen(filepath, "w");

	            // Get the bundle ID of the file which should be decrypted.
	            char *bid = cJSON_GetArrayItem(recent_file, 3)->valuestring;
	            char url_decrypt[117];
	            sprintf(url_decrypt, "http://localhost:4110/restful/rhizome/%s/decrypted.bin", bid);

	            _rpc_curl_set_basic_opt(url_decrypt, curl_handler, header);
        		curl_easy_setopt(curl_handler, CURLOPT_WRITEFUNCTION, _rpc_curl_write_to_file);
				curl_easy_setopt(curl_handler, CURLOPT_WRITEDATA, rpc_file);

	            // Decrypt the file.
	            curl_res = curl_easy_perform(curl_handler);
	            if (curl_res != CURLE_OK) {
	                pfatal("CURL failed (decrypt server download file): %s.", curl_easy_strerror(curl_res));
	                return_code = -1;
	                goto clean_rhizome_server_listener_all;
	            }
				fclose(rpc_file);
				strcpy(fpath, filepath);
				return_code = 1;
				break;
	        }
	    }
	}
    clean_rhizome_server_listener_all:
        curl_slist_free_all(header);
        curl_easy_cleanup(curl_handler);
    clean_rhizome_server_listener:
        _rpc_curl_free_memory(&curl_result_memory);
        curl_global_cleanup();

    return return_code;
}
int
dna_helper_start()
{
  if (!config.dna.helper.executable[0]) {
    /* Check if we have a helper configured. If not, then set
     dna_helper_pid to magic value of 0 so that we don't waste time
     in future looking up the dna helper configuration value. */
    INFO("DNAHELPER none configured");
    dna_helper_pid = 0;
    return 0;
  }
  
  if (!my_subscriber)
    return WHY("Unable to lookup my SID");
  
  const char *mysid = alloca_tohex_sid_t(my_subscriber->sid);
  
  dna_helper_close_pipes();
  int stdin_fds[2], stdout_fds[2], stderr_fds[2];
  if (pipe(stdin_fds) == -1)
    return WHY_perror("pipe");
  if (pipe(stdout_fds) == -1) {
    WHY_perror("pipe");
    close(stdin_fds[0]);
    close(stdin_fds[1]);
    return -1;
  }
  if (pipe(stderr_fds) == -1) {
    WHY_perror("pipe");
    close(stdin_fds[0]);
    close(stdin_fds[1]);
    close(stdout_fds[0]);
    close(stdout_fds[1]);
    return -1;
  }
  // Construct argv[] for execv() and log messages.
  const char *argv[config.dna.helper.argv.ac + 2];
  argv[0] = config.dna.helper.executable;
  unsigned i;
  for (i = 0; i < config.dna.helper.argv.ac; ++i)
    argv[i + 1] = config.dna.helper.argv.av[i].value;
  argv[i + 1] = NULL;
  strbuf argv_sb = strbuf_append_argv(strbuf_alloca(1024), config.dna.helper.argv.ac + 1, argv);
  switch (dna_helper_pid = fork()) {
  case 0:
    /* Child, should exec() to become helper after installing file descriptors. */
    close_log_file();
    setenv("MYSID", mysid, 1);
    signal(SIGTERM, SIG_DFL);
    close(stdin_fds[1]);
    close(stdout_fds[0]);
    close(stderr_fds[0]);
    if (dup2(stderr_fds[1], 2) == -1 || dup2(stdout_fds[1], 1) == -1 || dup2(stdin_fds[0], 0) == -1) {
      LOG_perror(LOG_LEVEL_FATAL, "dup2");
      _exit(-1);
    }
    {
      execv(config.dna.helper.executable, (char **)argv);
      LOGF_perror(LOG_LEVEL_FATAL, "execv(%s, [%s])",
	  alloca_str_toprint(config.dna.helper.executable),
	  strbuf_str(argv_sb)
	);
    }
    do { _exit(-1); } while (1);
    break;
  case -1:
    /* fork failed */
    WHY_perror("fork");
    close(stdin_fds[0]);
    close(stdin_fds[1]);
    close(stdout_fds[0]);
    close(stdout_fds[1]);
    close(stderr_fds[0]);
    close(stderr_fds[1]);
    return -1;
  default:
    /* Parent, should put file descriptors into place for use */
    close(stdin_fds[0]);
    close(stdout_fds[1]);
    close(stderr_fds[1]);
    dna_helper_started = 0;
    dna_helper_stdin = stdin_fds[1];
    dna_helper_stdout = stdout_fds[0];
    dna_helper_stderr = stderr_fds[0];
    INFOF("STARTED DNA HELPER pid=%u stdin=%d stdout=%d stderr=%d executable=%s argv=[%s]",
	dna_helper_pid,
	dna_helper_stdin,
	dna_helper_stdout,
	dna_helper_stderr,
	alloca_str_toprint(config.dna.helper.executable),
	strbuf_str(argv_sb)
      );

    sched_replies.poll.fd = dna_helper_stdout;
    sched_replies.poll.events = POLLIN;
    sched_errors.poll.fd = dna_helper_stderr;
    sched_errors.poll.events = POLLIN;
    sched_requests.poll.fd = -1;
    sched_requests.poll.events = POLLOUT;
    sched_harvester.alarm = gettime_ms() + 1000;
    sched_harvester.deadline = sched_harvester.alarm + 1000;
    reply_bufend = reply_buffer;
    discarding_until_nl = 0;
    awaiting_reply = 0;
    watch(&sched_replies);
    watch(&sched_errors);
    schedule(&sched_harvester);
    return 0;
  }
  return -1;
}
int cf_fmt_sid(const char **textp, const sid_t *sidp)
{
  *textp = str_edup(alloca_tohex_sid_t(*sidp));
  return CFOK;
}