static int
dna_helper_harvest(int blocking)
{
  if (dna_helper_pid > 0) {
    if (blocking && (config.debug.dnahelper))
      DEBUGF("DNAHELPER waiting for pid=%d to die", dna_helper_pid);
    int status;
    pid_t pid = waitpid(dna_helper_pid, &status, blocking ? 0 : WNOHANG);
    if (pid == dna_helper_pid) {
      strbuf b = strbuf_alloca(80);
      INFOF("DNAHELPER process pid=%u %s", pid, strbuf_str(strbuf_append_exit_status(b, status)));
      unschedule(&sched_harvester);
      dna_helper_pid = -1;
      if (awaiting_reply) {
	unschedule(&sched_timeout);
	awaiting_reply = 0;
      }
      return 1;
    } else if (pid == -1) {
      return WHYF_perror("waitpid(%d, %s)", dna_helper_pid, blocking ? "0" : "WNOHANG");
    } else if (pid) {
      return WHYF("waitpid(%d, %s) returned %d", dna_helper_pid, blocking ? "0" : "WNOHANG", pid);
    }
  }
  return 0;
}
示例#2
0
int app_rhizome_direct_sync(const struct cli_parsed *parsed, void *context)
{
  if (config.debug.verbose)
    DEBUG_cli_parsed(parsed);
  /* Attempt to connect with a remote Rhizome Direct instance,
     and negotiate which BARs to synchronise. */
  const char *modeName = (parsed->argc >= 3 ? parsed->args[2] : "sync");
  int mode=3; /* two-way sync */
  if (!strcasecmp(modeName,"push")) mode=1; /* push only */
  if (!strcasecmp(modeName,"pull")) mode=2; /* pull only */
  DEBUGF("sync direction = %d",mode);
  if (parsed->args[3]) {
    struct config_rhizome_peer peer;
    const struct config_rhizome_peer *peers[1] = { &peer };
    int result = cf_opt_rhizome_peer_from_uri(&peer, parsed->args[3]);
    if (result == CFOK)
      return rhizome_sync_with_peers(mode, 1, peers);
    else {
      strbuf b = strbuf_alloca(128);
      strbuf_cf_flag_reason(b, result);
      return WHYF("Invalid peer URI %s -- %s", alloca_str_toprint(parsed->args[3]), strbuf_str(b));
    }
  } else if (config.rhizome.direct.peer.ac == 0) {
    DEBUG("No rhizome direct peers were configured or supplied");
    return -1;
  } else {
    const struct config_rhizome_peer *peers[config.rhizome.direct.peer.ac];
    int i;
    for (i = 0; i < config.rhizome.direct.peer.ac; ++i)
      peers[i] = &config.rhizome.direct.peer.av[i].value;
    return rhizome_sync_with_peers(mode, config.rhizome.direct.peer.ac, peers);
  }
}
示例#3
0
int cli_usage_args(const int argc, const char *const *args, const struct cli_schema *commands, XPRINTF xpf)
{
  unsigned cmd;
  int matched_any = 0;
  for (cmd = 0; commands[cmd].function; ++cmd) {
    unsigned opt;
    const char *word;
    int matched = 1;
    for (opt = 0; matched && opt < argc && (word = commands[cmd].words[opt]); ++opt)
      if (strncmp(word, args[opt], strlen(args[opt])) != 0)
	matched = 0;
    if (matched) {
      matched_any = 1;
      for (opt = 0; (word = commands[cmd].words[opt]); ++opt) {
	if (word[0] == '|')
	  ++word;
	xprintf(xpf, " %s", word);
      }
      xputc('\n', xpf);
      if (commands[cmd].description && commands[cmd].description[0])
	xprintf(xpf, "   %s\n", commands[cmd].description);
    }
  }
  if (!matched_any && argc) {
    strbuf b = strbuf_alloca(160);
    strbuf_append_argv(b, argc, args);
    xprintf(xpf, " No commands matching %s\n", strbuf_str(b));
  }
  return 0;
}
示例#4
0
static int
overlay_queue_dump(overlay_txqueue *q)
{
  strbuf b = strbuf_alloca(8192);
  struct overlay_frame *f;
  strbuf_sprintf(b,"overlay_txqueue @ 0x%p\n",q);
  strbuf_sprintf(b,"  length=%d\n",q->length);
  strbuf_sprintf(b,"  maxLenght=%d\n",q->maxLength);
  strbuf_sprintf(b,"  latencyTarget=%d milli-seconds\n",q->latencyTarget);
  strbuf_sprintf(b,"  first=%p\n",q->first);
  f=q->first;
  while(f) {
    strbuf_sprintf(b,"    %p: ->next=%p, ->prev=%p\n",
		   f,f->next,f->prev);
    if (f==f->next) {
      strbuf_sprintf(b,"        LOOP!\n"); break;
    }
    f=f->next;
  }
  strbuf_sprintf(b,"  last=%p\n",q->last);
  f=q->last;
  while(f) {
    strbuf_sprintf(b,"    %p: ->next=%p, ->prev=%p\n",
		   f,f->next,f->prev);
    if (f==f->prev) {
      strbuf_sprintf(b,"        LOOP!\n"); break;
    }
    f=f->prev;
  }
  DEBUG(strbuf_str(b));
  return 0;
}
int cf_fmt_uint32_time_interval(const char **textp, const uint32_t *uintp)
{
  strbuf b = strbuf_alloca(60);
  uint32_t seconds = *uintp;
  if (seconds >= 7 * 24 * 60 * 60) {
    unsigned weeks = seconds / (7 * 24 * 60 * 60);
    seconds = seconds - weeks * (7 * 24 * 60 * 60);
    strbuf_sprintf(b, "%uw", weeks);
  }
  if (seconds >= 24 * 60 * 60) {
    unsigned days = seconds / (24 * 60 * 60);
    seconds = seconds - days * (24 * 60 * 60);
    strbuf_sprintf(b, "%ud", days);
  }
  if (seconds >= 60 * 60) {
    unsigned hours = seconds / (60 * 60);
    seconds = seconds - hours * (60 * 60);
    strbuf_sprintf(b, "%uh", hours);
  }
  if (seconds >= 60) {
    unsigned minutes = seconds / 60;
    seconds = seconds - minutes * 60;
    strbuf_sprintf(b, "%um", minutes);
  }
  if (seconds)
    strbuf_sprintf(b, "%us", seconds);
  if (strbuf_overrun(b))
    return CFINVALID;
  *textp = str_edup(strbuf_str(b));
  return CFOK;
}
static void monitor_requests(struct sched_ent *alarm)
{
  if (config.debug.dnahelper) {
    DEBUGF("sched_requests.poll.fd=%d .revents=%s",
	sched_requests.poll.fd,
	strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_requests.poll.revents))
      );
  }
  assert(alarm == &sched_requests);
  // On Linux, poll(2) returns ERR when the remote reader dies.  On Mac OS X, poll(2) returns NVAL,
  // which is documented to mean the file descriptor is not open, but testing revealed that in this
  // case it is still open.  See issue #5.
  if (sched_requests.poll.revents & (POLLHUP | POLLERR | POLLNVAL)) {
    if (config.debug.dnahelper)
      DEBUGF("DNAHELPER closing stdin fd=%d", dna_helper_stdin);
    close(dna_helper_stdin);
    dna_helper_stdin = -1;
    unwatch(&sched_requests);
    sched_requests.poll.fd = -1;
    dna_helper_kill();
  }
  else if (sched_requests.poll.revents & POLLOUT) {
    if (request_bufptr) {
      if (request_bufptr < request_bufend) {
	size_t remaining = request_bufend - request_bufptr;
	sigPipeFlag = 0;
	ssize_t written = write_nonblock(dna_helper_stdin, request_bufptr, remaining);
	if (sigPipeFlag) {
	  /* Broken pipe is probably due to a dead helper, but make sure the helper is dead, just to be
	    sure.  It will be harvested at the next harvester() timeout, and restarted on the first
	    request that arrives after a suitable pause has elapsed.  Losing the current request is not
	    a big problem, because DNA preemptively retries.
	  */
	  INFO("DNAHELPER got SIGPIPE on write -- stopping process");
	  dna_helper_kill();
	} else if (written > 0) {
	  if (config.debug.dnahelper)
	    DEBUGF("DNAHELPER wrote request %s", alloca_toprint(-1, request_bufptr, written));
	  request_bufptr += written;
	}
      }
      if (request_bufptr >= request_bufend) {
	// Request sent successfully.  Start watching for reply.
	request_bufptr = request_bufend = NULL;
	awaiting_reply = 1;
	sched_timeout.alarm = gettime_ms() + 1500;
	sched_timeout.deadline = sched_timeout.alarm + 3000;
	schedule(&sched_timeout);
      }
    }
    // If no request to send, stop monitoring the helper's stdin pipe.
    if (!request_bufptr) {
      unwatch(&sched_requests);
      sched_requests.poll.fd = -1;
    }
  }
}
/* Config parse function.  Implements the original form of the 'interfaces' config option.  Parses a
 * comma-separated list of interface rules (see cf_opt_network_interface_legacy() for the format of
 * each rule), then parses the regular config array-of-struct style interface option settings so
 * that both forms are supported.
 *
 * @author Andrew Bettison <*****@*****.**>
 */
int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node)
{
  if (!node->text)
    return cf_opt_config_interface_list(listp, node);
  if (node->nodc) {
    cf_warn_incompatible_children(node);
    return CFINCOMPATIBLE;
  }
  const char *p;
  const char *arg = NULL;
  unsigned n = listp->ac;
  int result = CFOK;
  for (p = node->text; n < NELS(listp->av); ++p) {
    if (*p == '\0' || *p == ',' || isspace(*p)) {
      if (arg) {
	int len = p - arg;
	if (len > 80) {
	  result |= CFSTRINGOVERFLOW;
	  goto bye;
	}
	char buf[len + 1];
	strncpy(buf, arg, len)[len] = '\0';
	int ret = cf_opt_network_interface_legacy(&listp->av[n].value, buf);
	switch (ret) {
	case CFERROR: return CFERROR;
	case CFOK:
	  listp->av[n].key = n;
	  ++n;
	  break;
	default: {
	    strbuf b = strbuf_alloca(180);
	    strbuf_cf_flag_reason(b, ret);
	    cf_warn_node(node, NULL, "invalid interface rule %s -- %s", alloca_str_toprint(buf), strbuf_str(b)); \
	    result |= CFSUB(ret);
	    break;
	  }
	}
	arg = NULL;
      }
      if (!*p)
	break;
    } else if (!arg)
      arg = p;
  }
  if (*p) {
    result |= CFARRAYOVERFLOW;
    goto bye;
  }
  assert(n <= NELS(listp->av));
  listp->ac = n;
bye:
  if (listp->ac == 0)
    result |= CFEMPTY;
  return result;
}
static void monitor_replies(struct sched_ent *alarm)
{
  if (config.debug.dnahelper) {
    DEBUGF("sched_replies.poll.fd=%d .revents=%s",
	sched_replies.poll.fd,
	strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_replies.poll.revents))
      );
  }
  assert(alarm == &sched_replies);
  if (sched_replies.poll.revents & POLLIN) {
    size_t remaining = reply_buffer + sizeof reply_buffer - reply_bufend;
    ssize_t nread = read_nonblock(sched_replies.poll.fd, reply_bufend, remaining);
    if (nread > 0) {
      char *bufp = reply_buffer;
      char *readp = reply_bufend;
      reply_bufend += nread;
      char *nl;
      while (nread > 0 && (nl = srv_strnstr(readp, nread, "\n"))) {
	size_t len = nl - bufp + 1;
	if (discarding_until_nl) {
	  if (config.debug.dnahelper)
	    DEBUGF("Discarding %s", alloca_toprint(-1, bufp, len));
	  discarding_until_nl = 0;
	} else {
	  handle_reply_line(bufp, len);
	}
	readp = bufp = nl + 1;
	nread = reply_bufend - readp;
      }
      if (bufp != reply_buffer) {
	size_t len = reply_bufend - bufp;
	memmove(reply_buffer, bufp, len);
	reply_bufend = reply_buffer + len;
      } else if (reply_bufend >= reply_buffer + sizeof reply_buffer) {
	WHY("DNAHELPER reply buffer overrun");
	if (config.debug.dnahelper)
	  DEBUGF("Discarding %s", alloca_toprint(-1, reply_buffer, sizeof reply_buffer));
	reply_bufend = reply_buffer;
	discarding_until_nl = 1;
      }
    }
  }
  if (sched_replies.poll.revents & (POLLHUP | POLLERR | POLLNVAL)) {
    if (config.debug.dnahelper)
      DEBUGF("DNAHELPER closing stdout fd=%d", dna_helper_stdout);
    close(dna_helper_stdout);
    dna_helper_stdout = -1;
    unwatch(&sched_replies);
    sched_replies.poll.fd = -1;
    dna_helper_kill();
  }
}
示例#9
0
void _debug_cli_parsed(struct __sourceloc __whence, const struct cli_parsed *parsed)
{
  DEBUG_argv("command", parsed->argc, parsed->args);
  strbuf b = strbuf_alloca(1024);
  int i;
  for (i = 0; i < parsed->labelc; ++i) {
    const struct labelv *lab = &parsed->labelv[i];
    strbuf_sprintf(b, " %s=%s", alloca_toprint(-1, lab->label, lab->len), alloca_str_toprint(lab->text));
  }
  if (parsed->varargi >= 0)
    strbuf_sprintf(b, " varargi=%d", parsed->varargi); 
  DEBUGF("parsed%s", strbuf_str(b));
}
示例#10
0
static void monitor_errors(struct sched_ent *alarm)
{
  if (debug & DEBUG_DNAHELPER) {
    DEBUGF("sched_errors.poll.fd=%d .revents=%s",
	sched_errors.poll.fd,
	strbuf_str(strbuf_append_poll_events(strbuf_alloca(40), sched_errors.poll.revents))
      );
  }
  if (sched_errors.poll.revents & POLLIN) {
    char buffer[1024];
    ssize_t nread = read_nonblock(sched_errors.poll.fd, buffer, sizeof buffer);
    if (nread > 0)
      WHYF("DNAHELPER stderr %s", alloca_toprint(-1, buffer, nread));
  }
  if (sched_errors.poll.revents & (POLLHUP | POLLERR | POLLNVAL)) {
    if (debug & DEBUG_DNAHELPER)
      DEBUGF("DNAHELPER closing stderr fd=%d", dna_helper_stderr);
    close(dna_helper_stderr);
    dna_helper_stderr = -1;
    unwatch(&sched_errors);
    sched_errors.poll.fd = -1;
  }
}
示例#11
0
int rhizome_direct_form_received(rhizome_http_request *r)
{
  const char *submitBareFileURI=confValueGet("rhizome.api.addfile.uri", NULL);

  /* Process completed form based on the set of fields seen */
  if (!strcmp(r->path,"/rhizome/import")) {
    switch(r->fields_seen) {
    case RD_MIME_STATE_MANIFESTHEADERS | RD_MIME_STATE_DATAHEADERS: {
	/* Got a bundle to import */
	DEBUGF("Call bundle import for rhizomedata.%d.{data,file}",
	       r->alarm.poll.fd);
	strbuf manifest_path = strbuf_alloca(50);
	strbuf payload_path = strbuf_alloca(50);
	strbuf_sprintf(manifest_path, "rhizomedirect.%d.manifest", r->alarm.poll.fd);
	strbuf_sprintf(payload_path, "rhizomedirect.%d.data", r->alarm.poll.fd);
	int ret = rhizome_bundle_import_files(strbuf_str(manifest_path), strbuf_str(payload_path), 1); // ttl = 1
	
	DEBUGF("Import returned %d",ret);
	
	rhizome_direct_clear_temporary_files(r);
	/* report back to caller.
	  200 = ok, which is probably appropriate for when we already had the bundle.
	  201 = content created, which is probably appropriate for when we successfully
	  import a bundle (or if we already have it).
	  403 = forbidden, which might be appropriate if we refuse to accept it, e.g.,
	  the import fails due to malformed data etc.
	  (should probably also indicate if we have a newer version if possible)
	*/
	switch (ret) {
	case 0:
	  return rhizome_server_simple_http_response(r, 201, "Bundle succesfully imported.");
	case 2:
	  return rhizome_server_simple_http_response(r, 200, "Bundle already imported.");
	}
	return rhizome_server_simple_http_response(r, 500, "Server error: Rhizome import command failed.");
      }
      break;     
    default:
      /* Clean up after ourselves */
      rhizome_direct_clear_temporary_files(r);	     
    }
  } else if (!strcmp(r->path,"/rhizome/enquiry")) {
    int fd=-1;
    char file[1024];
    switch(r->fields_seen) {
    case RD_MIME_STATE_DATAHEADERS:
      /* Read data buffer in, pass to rhizome direct for comparison with local
	 rhizome database, and send back responses. */
      snprintf(file,1024,"rhizomedirect.%d.%s",r->alarm.poll.fd,"data");
      fd=open(file,O_RDONLY);
      if (fd == -1) {
	WHYF_perror("open(%s, O_RDONLY)", alloca_str_toprint(file));
	/* Clean up after ourselves */
	rhizome_direct_clear_temporary_files(r);	     
	return rhizome_server_simple_http_response(r,500,"Couldn't read a file");
      }
      struct stat stat;
      if (fstat(fd, &stat) == -1) {
	WHYF_perror("stat(%d)", fd);
	/* Clean up after ourselves */
	close(fd);
	rhizome_direct_clear_temporary_files(r);	     
	return rhizome_server_simple_http_response(r,500,"Couldn't stat a file");
      }
      unsigned char *addr = mmap(NULL, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
      if (addr==MAP_FAILED) {
	WHYF_perror("mmap(NULL, %lld, PROT_READ, MAP_SHARED, %d, 0)", (long long) stat.st_size, fd);
	/* Clean up after ourselves */
	close(fd);
	rhizome_direct_clear_temporary_files(r);	     
	return rhizome_server_simple_http_response(r,500,"Couldn't mmap() a file");
      }
      /* Ask for a fill response.  Regardless of the size of the set of BARs passed
	 to us, we will allow up to 64KB of response. */
      rhizome_direct_bundle_cursor 
	*c=rhizome_direct_get_fill_response(addr,stat.st_size,65536);
      munmap(addr,stat.st_size);
      close(fd);

      if (c)
	{
	  /* TODO: Write out_buffer as the body of the response.
	     We should be able to do this using the async framework fairly easily.
	  */
	  
	  int bytes=c->buffer_offset_bytes+c->buffer_used;
	  r->buffer=malloc(bytes+1024);
	  r->buffer_size=bytes+1024;
	  r->buffer_offset=0;
	  assert(r->buffer);

	  /* Write HTTP response header */
	  struct http_response hr;
	  hr.result_code=200;
	  hr.content_type="binary/octet-stream";
	  hr.content_length=bytes;
	  hr.body=NULL;
	  r->request_type=0;
	  rhizome_server_set_response(r,&hr);
	  assert(r->buffer_offset<1024);

	  /* Now append body and send it back. */
	  bcopy(c->buffer,&r->buffer[r->buffer_length],bytes);
	  r->buffer_length+=bytes;
	  r->buffer_offset=0;

	  /* Clean up cursor after sending response */
	  rhizome_direct_bundle_iterator_free(&c);
	  /* Clean up after ourselves */
	  rhizome_direct_clear_temporary_files(r);	     

	  return 0;
	}
      else
	{
	  return rhizome_server_simple_http_response(r,500,"Could not get response to enquiry");
	}

      /* Clean up after ourselves */
      rhizome_direct_clear_temporary_files(r);	     
      break;
    default:
      /* Clean up after ourselves */
      rhizome_direct_clear_temporary_files(r);	     

      return rhizome_server_simple_http_response(r, 404, "/rhizome/enquiry requires 'data' field");
    }
  }  
  /* Allow servald to be configured to accept files without manifests via HTTP
     from localhost, so that rhizome bundles can be created programatically.
     There are probably still some security loop-holes here, which is part of
     why we leave it disabled by default, but it will be sufficient for testing
     possible uses, including integration with OpenDataKit.
  */
  else if (submitBareFileURI&&(!strcmp(r->path,submitBareFileURI))) {
    if (strcmp(inet_ntoa(r->requestor.sin_addr),
	       confValueGet("rhizome.api.addfile.allowedaddress","127.0.0.1")))
      {	
	DEBUGF("rhizome.api.addfile request received from %s, but is only allowed from %s",
	       inet_ntoa(r->requestor.sin_addr),
	       confValueGet("rhizome.api.addfile.allowedaddress", "127.0.0.1"));

	rhizome_direct_clear_temporary_files(r);	     
	return rhizome_server_simple_http_response(r,404,"Not available from here.");
      }

    switch(r->fields_seen) {
    case RD_MIME_STATE_DATAHEADERS:
      /* We have been given a file without a manifest, we should only
	 accept if it we are configured to do so, and the connection is from
	 localhost.  Otherwise people could cause your servald to create
	 arbitrary bundles, which would be bad.
      */
      /* A bundle to import */
      DEBUGF("Call bundle import sans-manifest for rhizomedata.%d.{data,file}",
	     r->alarm.poll.fd);
      
      char filepath[1024];
      snprintf(filepath,1024,"rhizomedirect.%d.data",r->alarm.poll.fd);

      const char *manifestTemplate
	=confValueGet("rhizome.api.addfile.manifesttemplate", NULL);
      
      if (manifestTemplate&&access(manifestTemplate, R_OK) != 0)
	{
	  rhizome_direct_clear_temporary_files(r);	     
	  return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.manifesttemplate points to a file I could not read.");
	}

      rhizome_manifest *m = rhizome_new_manifest();
      if (!m)
	{
	  rhizome_server_simple_http_response(r,500,"No free manifest slots. Try again later.");
	  rhizome_direct_clear_temporary_files(r);	     
	  return WHY("Manifest struct could not be allocated -- not added to rhizome");
	}

      if (manifestTemplate)
	if (rhizome_read_manifest_file(m, manifestTemplate, 0) == -1) {
	  rhizome_manifest_free(m);
	  rhizome_direct_clear_temporary_files(r);	     
	  return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.manifesttemplate can't be read as a manifest.");
	}

      /* Fill in a few missing manifest fields, to make it easier to use when adding new files:
	 - the default service is FILE
	 - use the current time for "date"
	 - if service is file, then use the payload file's basename for "name"
      */
      const char *service = rhizome_manifest_get(m, "service", NULL, 0);
      if (service == NULL) {
	rhizome_manifest_set(m, "service", (service = RHIZOME_SERVICE_FILE));
	if (debug & DEBUG_RHIZOME) DEBUGF("missing 'service', set default service=%s", service);
      } else {
	if (debug & DEBUG_RHIZOME) DEBUGF("manifest contains service=%s", service);
      }
      if (rhizome_manifest_get(m, "date", NULL, 0) == NULL) {
	rhizome_manifest_set_ll(m, "date", (long long) gettime_ms());
	if (debug & DEBUG_RHIZOME) DEBUGF("missing 'date', set default date=%s", rhizome_manifest_get(m, "date", NULL, 0));
      }

      const char *name = rhizome_manifest_get(m, "name", NULL, 0);
      if (name == NULL) {
	name=r->data_file_name;
	rhizome_manifest_set(m, "name", r->data_file_name);
	if (debug & DEBUG_RHIZOME) DEBUGF("missing 'name', set name=\"%s\" from HTTP post field filename specification", name);
      } else {
	if (debug & DEBUG_RHIZOME) DEBUGF("manifest contains name=\"%s\"", name);
      }

      const char *senderhex
	= rhizome_manifest_get(m, "sender", NULL, 0);
      if (!senderhex) senderhex=confValueGet("rhizome.api.addfile.author",NULL);
      unsigned char authorSid[SID_SIZE];
      if (senderhex) fromhexstr(authorSid,senderhex,SID_SIZE);
      const char *bskhex
	=confValueGet("rhizome.api.addfile.bundlesecretkey", NULL);   

      /* Bind an ID to the manifest, and also bind the file.  Then finalise the 
	 manifest. But if the manifest already contains an ID, don't override it. */
      if (rhizome_manifest_get(m, "id", NULL, 0) == NULL) {
	if (rhizome_manifest_bind_id(m, senderhex ? authorSid : NULL)) {
	  rhizome_manifest_free(m);
	  m = NULL;
	  rhizome_direct_clear_temporary_files(r);
	  return rhizome_server_simple_http_response(r,500,"Could not bind manifest to an ID");
	}	
      } else if (bskhex) {
	/* Allow user to specify a bundle secret key so that the same bundle can
	   be updated, rather than creating a new bundle each time. */
	unsigned char bsk[RHIZOME_BUNDLE_KEY_BYTES];
	fromhexstr(bsk,bskhex,RHIZOME_BUNDLE_KEY_BYTES);
	memcpy(m->cryptoSignSecret, bsk, RHIZOME_BUNDLE_KEY_BYTES);
	if (rhizome_verify_bundle_privatekey(m) == -1) {
	  rhizome_manifest_free(m);
	  m = NULL;
	  rhizome_direct_clear_temporary_files(r);
	  return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.bundlesecretkey did not verify.  Using the right key for the right bundle?");
	}
      } else {
	/* Bundle ID specified, but without a BSK or sender SID specified.
	   Therefore we cannot work out the bundle key, and cannot update the
	   bundle. */
	rhizome_manifest_free(m);
	m = NULL;
	rhizome_direct_clear_temporary_files(r);
	return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.bundlesecretkey not set, and manifest template contains no sender, but template contains a hard-wired bundle ID.  You must specify at least one, or not supply id= in the manifest template.");
	
      }
      
      int encryptP = 0; // TODO Determine here whether payload is to be encrypted.
      if (rhizome_manifest_bind_file(m, filepath, encryptP)) {
	rhizome_manifest_free(m);
	rhizome_direct_clear_temporary_files(r);
	return rhizome_server_simple_http_response(r,500,"Could not bind manifest to file");
      }      
      if (rhizome_manifest_finalise(m)) {
	rhizome_manifest_free(m);
	rhizome_direct_clear_temporary_files(r);
	return rhizome_server_simple_http_response(r,500,
						   "Could not finalise manifest");
      }
      if (rhizome_add_manifest(m,255 /* TTL */)) {
	rhizome_manifest_free(m);
	rhizome_direct_clear_temporary_files(r);
	return rhizome_server_simple_http_response(r,500,
						   "Add manifest operation failed");
      }
            
      DEBUGF("Import sans-manifest appeared to succeed");
      
      /* Respond with the manifest that was added. */
      rhizome_server_simple_http_response(r, 200, (char *)m->manifestdata);

      /* clean up after ourselves */
      rhizome_manifest_free(m);
      rhizome_direct_clear_temporary_files(r);

      return 0;
      break;
    default:
      /* Clean up after ourselves */
      rhizome_direct_clear_temporary_files(r);	     
      
      return rhizome_server_simple_http_response(r, 400, "Rhizome create bundle from file API requires 'data' field");     
    }
  }  

  /* Clean up after ourselves */
  rhizome_direct_clear_temporary_files(r);	     
  /* Report error */
  return rhizome_server_simple_http_response(r, 500, "Something went wrong.  Probably a missing data or manifest part, or invalid combination of URI and data/manifest provision.");

}
示例#12
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);
}
示例#13
0
static void _open_log_file(_log_iterator *it)
{
  assert(it->state == &state_file);
  if (_log_file != NO_FILE) {
    if (_log_file_path == NULL)
      _log_file_path = getenv("SERVALD_LOG_FILE");
    if (_log_file_path == NULL && !cf_limbo) {
      strbuf sbfile = strbuf_local(_log_file_path_buf, sizeof _log_file_path_buf);
      strbuf_path_join(sbfile, serval_instancepath(), log_file_directory_path(), NULL);
      _compute_file_start_time(it);
      if (config.log.file.path[0]) {
	strbuf_path_join(sbfile, config.log.file.path, NULL);
      } else {
	struct tm tm;
	(void)localtime_r(&it->file_start_time, &tm);
	strbuf_append_strftime(sbfile, "/serval-%Y%m%d%H%M%S.log", &tm);
      }
      if (strbuf_overrun(sbfile)) {
	_log_file = NO_FILE;
	_logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot form log file name - buffer overrun");
      } else {
	_log_file_start_time = it->file_start_time;
	_log_file_path = strbuf_str(sbfile);
      }
    }
    if (!_log_file) {
      if (_log_file_path == NULL) {
	if (cf_limbo)
	  return;
	_log_file = NO_FILE;
	_logs_printf_nl(serverMode ? LOG_LEVEL_WARN : LOG_LEVEL_INFO, __NOWHERE__, "No log file configured");
      } else {
	// Create the new log file.
	size_t dirsiz = strlen(_log_file_path) + 1;
	char _dir[dirsiz];
	strcpy(_dir, _log_file_path);
	const char *dir = dirname(_dir); // modifies _dir[]
	if (mkdirs(dir, 0700) != -1 && (_log_file = fopen(_log_file_path, "a"))) {
	  setlinebuf(_log_file);
	  memset(it->state, 0, sizeof *it->state);
	  // The first line in every log file must be the starting time stamp.  (After that, it is up
	  // to _log_update() to insert other mandatory messages in any suitable order.)
	  _log_current_datetime(it, LOG_LEVEL_INFO);
	  _logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Logging to %s (fd %d)", _log_file_path, fileno(_log_file));
	  // Update the log symlink to point to the latest log file.
	  strbuf sbsymlink = strbuf_alloca(400);
	  strbuf_path_join(sbsymlink, serval_instancepath(), "serval.log", NULL);
	  if (strbuf_overrun(sbsymlink))
	    _logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot form log symlink name - buffer overrun");
	  else {
	    const char *f = _log_file_path;
	    const char *s = strbuf_str(sbsymlink);
	    const char *relpath = f;
	    for (; *f && *f == *s; ++f, ++s)
	      if (*f == '/')
		relpath = f;
	    while (*relpath == '/')
	      ++relpath;
	    while (*s == '/')
	      ++s;
	    if (strchr(s, '/'))
	      relpath = _log_file_path;
	    unlink(strbuf_str(sbsymlink));
	    if (symlink(relpath, strbuf_str(sbsymlink)) == -1)
	      _logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot symlink %s to %s - %s [errno=%d]", strbuf_str(sbsymlink), relpath, strerror(errno), errno);
	  }
	  // Expire old log files.
	  size_t pathsiz = strlen(_log_file_path) + 1;
	  char path[pathsiz];
	  while (1) {
	    strcpy(path, _log_file_path);
	    const char *base = basename(path); // modifies path[]
	    DIR *d = opendir(dir);
	    if (!d) {
	      _logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot expire log files: opendir(%s) - %s [errno=%d]", dir, strerror(errno), errno);
	      break;
	    }
	    struct dirent oldest;
	    memset(&oldest, 0, sizeof oldest);
	    unsigned count = 0;
	    while (1) {
	      struct dirent ent;
	      struct dirent *ep;
	      int err = readdir_r(d, &ent, &ep);
	      if (err) {
		_logs_printf_nl(LOG_LEVEL_ERROR, __HERE__, "Cannot expire log files: r_readdir(%s) - %s [errno=%d]", dir, strerror(err), err);
		break;
	      }
	      if (!ep)
		break;
	      const char *e;
	      if (   str_startswith(ent.d_name, "serval-", &e)
		  && isdigit(e[0]) && isdigit(e[1]) && isdigit(e[2]) && isdigit(e[3]) // YYYY
		  && isdigit(e[4]) && isdigit(e[5]) // MM
		  && isdigit(e[6]) && isdigit(e[7]) // DD
		  && isdigit(e[8]) && isdigit(e[9]) // HH
		  && isdigit(e[10]) && isdigit(e[11]) // MM
		  && isdigit(e[12]) && isdigit(e[13]) // SS
		  && strcmp(&e[14], ".log") == 0
	      ) {
		++count;
		if ( strcmp(ent.d_name, base) != 0
		  && (!oldest.d_name[0] || strcmp(ent.d_name, oldest.d_name) < 0)
		)
		  oldest = ent;
	      }
	    }
	    closedir(d);
	    if (count <= config.log.file.rotate || !oldest.d_name[0])
	      break;
	    strbuf b = strbuf_local(path, pathsiz);
	    strbuf_path_join(b, dir, oldest.d_name, NULL);
	    assert(!strbuf_overrun(b));
	    _logs_printf_nl(LOG_LEVEL_INFO, __NOWHERE__, "Unlink %s", path);
	    unlink(path);
	  }
	} else {
	  _log_file = NO_FILE;
	  _logs_printf_nl(LOG_LEVEL_WARN, __HERE__, "Cannot create/append %s - %s [errno=%d]", _log_file_path, strerror(errno), errno);
	}
      }
    }
  }
}
示例#14
0
int overlay_saw_mdp_frame(overlay_mdp_frame *mdp, time_ms_t now)
{
  IN();
  int i;
  int match=-1;

  switch(mdp->packetTypeAndFlags&MDP_TYPE_MASK) {
  case MDP_TX: 
    /* Regular MDP frame addressed to us.  Look for matching port binding,
       and if available, push to client.  Else do nothing, or if we feel nice
       send back a connection refused type message? Silence is probably the
       more prudent path.
    */

    if (debug & DEBUG_MDPREQUESTS) 
      DEBUGF("Received packet with listener (MDP ports: src=%s*:%d, dst=%d)",
	   alloca_tohex(mdp->out.src.sid, 7),
	   mdp->out.src.port,mdp->out.dst.port);

    // TODO pass in dest subscriber as an argument, we should know it by now
    struct subscriber *destination = NULL;
    if (!is_broadcast(mdp->out.dst.sid)){
      destination = find_subscriber(mdp->out.dst.sid, SID_SIZE, 1);
    }
    
    for(i=0;i<MDP_MAX_BINDINGS;i++)
      {
	if (mdp_bindings[i].port!=mdp->out.dst.port)
	  continue;
	
	if ((!destination) || mdp_bindings[i].subscriber == destination){
	  /* exact match, so stop searching */
	  match=i;
	  break;
	}else if (!mdp_bindings[i].subscriber){
	  /* If we find an "ANY" binding, remember it. But we will prefer an exact match if we find one */
	  match=i;
	}
      }
    
    if (match>-1) {
      struct sockaddr_un addr;

      bcopy(mdp_bindings[match].socket_name,addr.sun_path,mdp_bindings[match].name_len);
      addr.sun_family=AF_UNIX;
      errno=0;
      int len=overlay_mdp_relevant_bytes(mdp);
      int r=sendto(mdp_named.poll.fd,mdp,len,0,(struct sockaddr*)&addr,sizeof(addr));
      if (r==overlay_mdp_relevant_bytes(mdp)) {	
	RETURN(0);
      }
      WHY("didn't send mdp packet");
      if (errno==ENOENT) {
	/* far-end of socket has died, so drop binding */
	INFOF("Closing dead MDP client '%s'",mdp_bindings[match].socket_name);
	overlay_mdp_releasebindings(&addr,mdp_bindings[match].name_len);
      }
      WHY_perror("sendto(e)");
      RETURN(WHY("Failed to pass received MDP frame to client"));
    } else {
      /* No socket is bound, ignore the packet ... except for magic sockets */
      switch(mdp->out.dst.port) {
      case MDP_PORT_VOMP:
	RETURN(vomp_mdp_received(mdp));
      case MDP_PORT_KEYMAPREQUEST:
	/* Either respond with the appropriate SAS, or record this one if it
	   verifies out okay. */
	if (debug & DEBUG_MDPREQUESTS)
	  DEBUG("MDP_PORT_KEYMAPREQUEST");
	RETURN(keyring_mapping_request(keyring,mdp));
      case MDP_PORT_DNALOOKUP: /* attempt to resolve DID to SID */
	{
	  int cn=0,in=0,kp=0;
	  char did[64+1];
	  int pll=mdp->out.payload_length;
	  if (pll>64) pll=64;
	  /* get did from the packet */
	  if (mdp->out.payload_length<1) {
	    RETURN(WHY("Empty DID in DNA resolution request")); }
	  bcopy(&mdp->out.payload[0],&did[0],pll);
	  did[pll]=0;
	  
	  if (debug & DEBUG_MDPREQUESTS)
	    DEBUG("MDP_PORT_DNALOOKUP");
	  
	  int results=0;
	  while(keyring_find_did(keyring,&cn,&in,&kp,did))
	    {
	      /* package DID and Name into reply (we include the DID because
		 it could be a wild-card DID search, but the SID is implied 
		 in the source address of our reply). */
	      if (keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key_len > DID_MAXSIZE) 
		/* skip excessively long DID records */
		continue;
	      const unsigned char *packedSid = keyring->contexts[cn]->identities[in]->keypairs[0]->public_key;
	      const char *unpackedDid = (const char *) keyring->contexts[cn]->identities[in]->keypairs[kp]->private_key;
	      const char *name = (const char *)keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key;
	      // URI is sid://SIDHEX/DID
	      strbuf b = strbuf_alloca(SID_STRLEN + DID_MAXSIZE + 10);
	      strbuf_puts(b, "sid://");
	      strbuf_tohex(b, packedSid, SID_SIZE);
	      strbuf_puts(b, "/local/");
	      strbuf_puts(b, unpackedDid);
	      overlay_mdp_dnalookup_reply(&mdp->out.src, packedSid, strbuf_str(b), unpackedDid, name);
	      kp++;
	      results++;
	    }
	  if (!results) {
	    /* No local results, so see if servald has been configured to use
	       a DNA-helper that can provide additional mappings.  This provides
	       a generalised interface for resolving telephone numbers into URIs.
	       The first use will be for resolving DIDs to SIP addresses for
	       OpenBTS boxes run by the OTI/Commotion project. 

	       The helper is run asynchronously, and the replies will be delivered
	       when results become available, so this function will return
	       immediately, so as not to cause blockages and delays in servald.
	    */
	    dna_helper_enqueue(mdp, did, mdp->out.src.sid);
	    monitor_tell_formatted(MONITOR_DNAHELPER, "LOOKUP:%s:%d:%s\n", alloca_tohex_sid(mdp->out.src.sid), mdp->out.src.port, did);
	  }
	  RETURN(0);
	}
	break;
      case MDP_PORT_ECHO: /* well known ECHO port for TCP/UDP and now MDP */
	{
	  /* Echo is easy: we swap the sender and receiver addresses (and thus port
	     numbers) and send the frame back. */

	  /* Swap addresses */
	  overlay_mdp_swap_src_dst(mdp);

	  /* Prevent echo:echo connections and the resulting denial of service from triggering endless pongs. */
	  if (mdp->out.dst.port==MDP_PORT_ECHO) {
	    RETURN(WHY("echo loop averted"));
	  }
	  /* If the packet was sent to broadcast, then replace broadcast address
	     with our local address. For now just responds with first local address */
	  if (is_broadcast(mdp->out.src.sid))
	    {
	      if (my_subscriber)		  
		bcopy(my_subscriber->sid,
		      mdp->out.src.sid,SID_SIZE);
	      else
		/* No local addresses, so put all zeroes */
		bzero(mdp->out.src.sid,SID_SIZE);
	    }

	  /* queue frame for delivery */	  
	  overlay_mdp_dispatch(mdp,0 /* system generated */,
			       NULL,0);
	  
	  /* and switch addresses back around in case the caller was planning on
	     using MDP structure again (this happens if there is a loop-back reply
	     and the frame needs sending on, as happens with broadcasts.  MDP ping
	     is a simple application where this occurs). */
	  overlay_mdp_swap_src_dst(mdp);
	  
	}
	break;
      default:
	/* Unbound socket.  We won't be sending ICMP style connection refused
	   messages, partly because they are a waste of bandwidth. */
	RETURN(WHYF("Received packet for which no listening process exists (MDP ports: src=%d, dst=%d",
		    mdp->out.src.port,mdp->out.dst.port));
      }
    }
    break;
  default:
    RETURN(WHYF("We should only see MDP_TX frames here (MDP message type = 0x%x)",
		mdp->packetTypeAndFlags));
  }

  RETURN(0);
}
static int restful_rhizome_insert_end(struct http_request *hr)
{
  httpd_request *r = (httpd_request *) hr;
  if (!r->u.insert.received_manifest)
    return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
  if (!r->u.insert.received_payload)
    return http_response_form_part(r, "Missing", PART_PAYLOAD, NULL, 0);
  // Fill in the missing manifest fields and ensure payload and manifest are consistent.
  assert(r->manifest != NULL);
  assert(r->u.insert.write.file_length != RHIZOME_SIZE_UNSET);
  switch (r->u.insert.payload_status) {
    case RHIZOME_PAYLOAD_STATUS_ERROR:
      return 500;
    case RHIZOME_PAYLOAD_STATUS_NEW:
      if (r->manifest->filesize == RHIZOME_SIZE_UNSET)
	rhizome_manifest_set_filesize(r->manifest, r->u.insert.write.file_length);
      // fall through
    case RHIZOME_PAYLOAD_STATUS_STORED:
      // TODO: check that stored hash matches received payload's hash
      // fall through
    case RHIZOME_PAYLOAD_STATUS_EMPTY:
      assert(r->manifest->filesize != RHIZOME_SIZE_UNSET);
      if (r->u.insert.payload_size == r->manifest->filesize)
	break;
      // fall through
    case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
      {
	strbuf msg = strbuf_alloca(200);
	strbuf_sprintf(msg, "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")", r->u.insert.payload_size, r->manifest->filesize);
	http_request_simple_response(&r->http, 403, strbuf_str(msg));
	return 403;
      }
    case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
      http_request_simple_response(&r->http, 403, "Payload hash contradicts manifest");
      return 403;
    case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
      http_request_simple_response(&r->http, 403, "Missing bundle secret");
      return 403;
    default:
      FATALF("payload_status = %d", r->u.insert.payload_status);
  }
  // Finalise the manifest and add it to the store.
  if (r->manifest->filesize) {
    if (!r->manifest->has_filehash)
      rhizome_manifest_set_filehash(r->manifest, &r->u.insert.write.id);
    else
      assert(cmp_rhizome_filehash_t(&r->u.insert.write.id, &r->manifest->filehash) == 0);
  }
  if (!rhizome_manifest_validate(r->manifest) || r->manifest->malformed) {
    http_request_simple_response(&r->http, 403, "Manifest is malformed");
    return 403;
  }
  if (!r->manifest->haveSecret) {
    http_request_simple_response(&r->http, 403, "Missing bundle secret");
    return 403;
  }
  rhizome_manifest *mout = NULL;
  int result;
  switch (rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new)) {
    case RHIZOME_BUNDLE_STATUS_NEW:
      result = 201;
      if (mout && mout != r->manifest)
	rhizome_manifest_free(mout);
      mout = NULL;
      break;
    case RHIZOME_BUNDLE_STATUS_SAME:
    case RHIZOME_BUNDLE_STATUS_OLD:
    case RHIZOME_BUNDLE_STATUS_DUPLICATE:
      result = 200;
      break;
    case RHIZOME_BUNDLE_STATUS_INVALID:
      result = 403;
      break;
    case RHIZOME_BUNDLE_STATUS_ERROR:
    default:
      result = 500;
      break;
  }
  if (mout && mout != r->manifest) {
    rhizome_manifest_free(r->manifest);
    r->manifest = mout;
  }
  if (result >= 400)
    return result;
  rhizome_authenticate_author(r->manifest);
  r->http.render_extra_headers = render_manifest_headers;
  http_request_response_static(&r->http, result, "rhizome-manifest/text",
      (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
    );
  return 0;
}
示例#16
0
void rhizome_direct_http_dispatch(rhizome_direct_sync_request *r)
{
  DEBUGF("Dispatch size_high=%lld",r->cursor->size_high);
  rhizome_direct_transport_state_http *state = r->transport_specific_state;

  int sock=socket(AF_INET, SOCK_STREAM, 0);
  if (sock==-1) {
    WHY_perror("socket");    
    goto end;
  } 

  struct hostent *hostent;
  hostent = gethostbyname(state->host);
  if (!hostent) {
    DEBUGF("could not resolve hostname");
    goto end;
  }

  struct sockaddr_in addr;  
  addr.sin_family = AF_INET;     
  addr.sin_port = htons(state->port);   
  addr.sin_addr = *((struct in_addr *)hostent->h_addr);
  bzero(&(addr.sin_zero),8);     

  if (connect(sock,(struct sockaddr *)&addr,sizeof(struct sockaddr)) == -1) {
    WHY_perror("connect");
    close(sock);
    goto end;
  }
 
  char boundary[20];
  char buffer[8192];

  strbuf bb = strbuf_local(boundary, sizeof boundary);
  strbuf_sprintf(bb, "%08lx%08lx", random(), random());
  assert(!strbuf_overrun(bb));
  strbuf content_preamble = strbuf_alloca(200);
  strbuf content_postamble = strbuf_alloca(40);
  strbuf_sprintf(content_preamble,
      "--%s\r\n"
      "Content-Disposition: form-data; name=\"data\"; filename=\"IHAVEs\"\r\n"
      "Content-Type: application/octet-stream\r\n"
      "\r\n",
      boundary
    );
  strbuf_sprintf(content_postamble, "\r\n--%s--\r\n", boundary);
  assert(!strbuf_overrun(content_preamble));
  assert(!strbuf_overrun(content_postamble));
  int content_length = strbuf_len(content_preamble)
		     + r->cursor->buffer_offset_bytes
		     + r->cursor->buffer_used
		     + strbuf_len(content_postamble);
  strbuf request = strbuf_local(buffer, sizeof buffer);
  strbuf_sprintf(request,
      "POST /rhizome/enquiry HTTP/1.0\r\n"
      "Content-Length: %d\r\n"
      "Content-Type: multipart/form-data; boundary=%s\r\n"
      "\r\n%s",
      content_length, boundary, strbuf_str(content_preamble)
    );
  assert(!strbuf_overrun(request));

  /* TODO: Refactor this code so that it uses our asynchronous framework.
   */
  int len = strbuf_len(request);
  int sent=0;
  while(sent<len) {
    DEBUGF("write(%d, %s, %d)", sock, alloca_toprint(-1, &buffer[sent], len-sent), len-sent);
    int count=write(sock,&buffer[sent],len-sent);
    if (count == -1) {
      if (errno==EPIPE) goto rx;
      WHYF_perror("write(%d)", len - sent);
      close(sock);
      goto end;
    }
    sent+=count;
  }

  len=r->cursor->buffer_offset_bytes+r->cursor->buffer_used;
  sent=0;
  while(sent<len) {
    int count=write(sock,&r->cursor->buffer[sent],len-sent);
    if (count == -1) {
      if (errno == EPIPE)
	goto rx;
      WHYF_perror("write(%d)", count);
      close(sock);
      goto end;
    }
    sent+=count;
  }

  strbuf_reset(request);
  strbuf_puts(request, strbuf_str(content_postamble));
  len = strbuf_len(request);
  sent=0;
  while(sent<len) {
    DEBUGF("write(%d, %s, %d)", sock, alloca_toprint(-1, &buffer[sent], len-sent), len-sent);
    int count=write(sock,&buffer[sent],len-sent);
    if (count == -1) {
      if (errno==EPIPE) goto rx;
      WHYF_perror("write(%d)", len - sent);
      close(sock);
      goto end;
    }
    sent+=count;
  }

  struct http_response_parts parts;
 rx:
  /* request sent, now get response back. */
  if (receive_http_response(sock, buffer, sizeof buffer, &parts) == -1) {
    close(sock);
    goto end;
  }

  /* For some reason the response data gets overwritten during a push,
     so we need to copy it, and use the copy instead. */
  unsigned char *actionlist=alloca(parts.content_length);
  bcopy(parts.content_start, actionlist, parts.content_length);
  dump("response", actionlist, parts.content_length);

  /* We now have the list of (1+RHIZOME_BAR_PREFIX_BYTES)-byte records that indicate
     the list of BAR prefixes that differ between the two nodes.  We can now action
     those which are relevant, i.e., based on whether we are pushing, pulling or 
     synchronising (both).

     I am currently undecided as to whether it is cleaner to have some general
     rhizome direct function for doing that, or whether it just adds unnecessary
     complication, and the responses should just be handled in here.

     For now, I am just going to implement it in here, and we can generalise later.
  */
  int i;
  for(i=10;i<content_length;i+=(1+RHIZOME_BAR_PREFIX_BYTES))
    {
      int type=actionlist[i];
      unsigned long long 
	bid_prefix_ll=rhizome_bar_bidprefix_ll((unsigned char *)&actionlist[i+1]);
      DEBUGF("%s %016llx* @ 0x%x",type==1?"push":"pull",bid_prefix_ll,i);
      if (type==2&&r->pullP) {
	/* Need to fetch manifest.  Once we have the manifest, then we can
	   use our normal bundle fetch routines from rhizome_fetch.c	 

	   Generate a request like: GET /rhizome/manifestbybar/<hex of bar>
	   and add it to our list of HTTP fetch requests, then watch
	   until the request is finished.  That will give us the manifest.
	   Then as noted above, we can use that to pull the file down using
	   existing routines.
	*/
	if (!rhizome_fetch_request_manifest_by_prefix
	    (&addr,&actionlist[i+1],RHIZOME_BAR_PREFIX_BYTES,
	     1 /* import, getting file if needed */))
	  {
	    /* Fetching the manifest, and then using it to see if we want to 
	       fetch the file for import is all handled asynchronously, so just
	       wait for it to finish. */
	    while(rhizome_file_fetch_queue_count) fd_poll();
	  }
	
      } else if (type==1&&r->pushP) {
	/* Form up the POST request to submit the appropriate bundle. */

	/* Start by getting the manifest, which is the main thing we need, and also
	   gives us the information we need for sending any associated file. */
	rhizome_manifest 
	  *m=rhizome_direct_get_manifest(&actionlist[i+1],
					 RHIZOME_BAR_PREFIX_BYTES);
	if (!m) {
	  WHY("This should never happen.  The manifest exists, but when I went looking for it, it doesn't appear to be there.");
	  goto next_item;
	}

	/* Get filehash and size from manifest if present */
	const char *id = rhizome_manifest_get(m, "id", NULL, 0);
	DEBUGF("bundle id = '%s'",id);
	const char *hash = rhizome_manifest_get(m, "filehash", NULL, 0);
	DEBUGF("bundle file hash = '%s'",hash);
	long long filesize = rhizome_manifest_get_ll(m, "filesize");
	DEBUGF("file size = %lld",filesize);

	/* We now have everything we need to compose the POST request and send it.
	 */
	char *template="POST /rhizome/import HTTP/1.0\r\n"
示例#17
0
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;
}