Beispiel #1
0
static int example_initiate(noit_module_t *self, noit_check_t *check,
                            noit_check_t *cause) {
  struct example_check_info *ci = check->closure;
  const char *limit = "0";
  struct timeval now, diff;

  BAIL_ON_RUNNING_CHECK(check);
  check->flags |= NP_RUNNING;

  mtev_hash_retrieve(check->config, "limit", strlen("limit"), (void **)&limit);
  ci->limit = atoi(limit);

  mtev_gettimeofday(&now, NULL);
  sub_timeval(now, check->last_fire_time, &diff);
  noit_stats_set_whence(check, &now);
  noit_stats_set_duration(check, diff.tv_sec * 1000 + diff.tv_usec / 1000);
  noit_stats_set_available(check, NP_AVAILABLE);
  noit_stats_set_status(check, "hello world");

  if(ci->limit) {
    int value = (int)(lrand48() % ci->limit);
    noit_stats_set_metric(check, "random", METRIC_INT32, &value);
    noit_stats_set_state(check, NP_GOOD);
  }
  else {
    noit_stats_set_metric(check, "random", METRIC_INT32, NULL);
    noit_stats_set_state(check, NP_BAD);
  }

  noit_check_set_stats(check);
  check->flags &= ~NP_RUNNING;

  return 0;
}
static int noit_rabbimq_connect(iep_thread_driver_t *dr) {
  struct amqp_driver *driver = (struct amqp_driver *)dr;

  if(!driver->connection) {
    int sidx = driver->nconnects++ % driver->nhosts;
    struct timeval timeout;
    amqp_rpc_reply_t r, *rptr;

    mtevL(mtev_error, "AMQP connect: %s:%d\n",
          driver->hostname[sidx], driver->port);
    BUMPSTAT(connects);
    driver->hostidx = sidx;
    timeout.tv_sec = driver->heartbeat;
    timeout.tv_usec = 0;
    driver->sockfd = amqp_open_socket(driver->hostname[sidx], driver->port, &timeout);
    if(driver->sockfd < 0) {
      mtevL(mtev_error, "AMQP connect failed: %s:%d\n",
            driver->hostname[sidx], driver->port);
      return -1;
    }
    if(setsockopt(driver->sockfd, SOL_SOCKET, SO_SNDBUF, &desired_sndbuf, sizeof(desired_sndbuf)) < 0)
      mtevL(mtev_debug, "rabbitmq: setsockopt(SO_SNDBUF, %ld) -> %s\n", (long int)desired_sndbuf, strerror(errno));
    if(setsockopt(driver->sockfd, SOL_SOCKET, SO_RCVBUF, &desired_rcvbuf, sizeof(desired_rcvbuf)) < 0)
      mtevL(mtev_debug, "rabbitmq: setsockopt(SO_RCVBUF, %ld) -> %s\n", (long int)desired_rcvbuf, strerror(errno));
    driver->has_error = 0;
    driver->connection = amqp_new_connection();
    amqp_set_basic_return_cb(driver->connection, noit_rabbitmq_brcb, driver);
    amqp_set_sockfd(driver->connection, driver->sockfd);
    r = amqp_login(driver->connection,
                   driver->vhost, 0, 131072, driver->heartbeat,
                   AMQP_SASL_METHOD_PLAIN,
                   driver->username, driver->password);
    if(r.reply_type != AMQP_RESPONSE_NORMAL) {
      mtevL(mtev_error, "AMQP login failed\n");
      amqp_connection_close(driver->connection, AMQP_REPLY_SUCCESS);
      amqp_destroy_connection(driver->connection);
      if(driver->sockfd >= 0) close(driver->sockfd);
      driver->sockfd = -1;
      driver->connection = NULL;
      return -1;
    }

    amqp_channel_open(driver->connection, 1);
    rptr = amqp_get_rpc_reply();
    if(rptr->reply_type != AMQP_RESPONSE_NORMAL) {
      mtevL(mtev_error, "AMQP channe_open failed\n");
      amqp_connection_close(driver->connection, AMQP_REPLY_SUCCESS);
      amqp_destroy_connection(driver->connection);
      if(driver->sockfd >= 0) close(driver->sockfd);
      driver->sockfd = -1;
      driver->connection = NULL;
      return -1;
    }
    mtev_gettimeofday(&driver->last_hb, NULL);
    return 0;
  }
  /* 1 means already connected */
  return 1;
}
Beispiel #3
0
static void ping_icmp_log_results(noit_module_t *self, noit_check_t *check) {
  struct check_info *data;
  double avail = 0.0, min = MAXFLOAT, max = 0.0, avg = 0.0, cnt;
  int avail_needed = 100;
  const char *config_val = NULL;
  int i, points = 0;
  char human_buffer[256];
  struct timeval now, duration;

  data = (struct check_info *)check->closure;
  for(i=0; i<data->expected_count; i++) {
    if(data->turnaround[i] >= 0.0) {
      points++;
      avg += data->turnaround[i];
      if(data->turnaround[i] > max) max = data->turnaround[i];
      if(data->turnaround[i] < min) min = data->turnaround[i];
    }
  }
  cnt = data->expected_count;
  if(points == 0) {
    min = 0.0 / 0.0;
    max = 0.0 / 0.0;
    avg = 0.0 / 0.0;
  }
  else {
    avail = (float)points /cnt;
    avg /= (float)points;
  }

  if(mtev_hash_retr_str(check->config, "avail_needed", strlen("avail_needed"),
                        &config_val))
    avail_needed = atoi(config_val);

  snprintf(human_buffer, sizeof(human_buffer),
           "cnt=%d,avail=%0.0f,min=%0.4f,max=%0.4f,avg=%0.4f",
           (int)cnt, 100.0*avail, min, max, avg);
  mtevL(nldeb, "ping_icmp(%s) [%s]\n", check->target_ip, human_buffer);

  mtev_gettimeofday(&now, NULL);
  sub_timeval(now, check->last_fire_time, &duration);
  noit_stats_set_whence(check, &now);
  noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);
  noit_stats_set_available(check, (avail > 0.0) ? NP_AVAILABLE : NP_UNAVAILABLE);
  noit_stats_set_state(check, (avail < ((float)avail_needed / 100.0)) ? NP_BAD : NP_GOOD);
  noit_stats_set_status(check, human_buffer);
  noit_stats_set_metric(check, "count",
                        METRIC_INT32, &data->expected_count);
  avail *= 100.0;
  noit_stats_set_metric(check, "available", METRIC_DOUBLE, &avail);
  noit_stats_set_metric(check, "minimum",
                        METRIC_DOUBLE, avail > 0.0 ? &min : NULL);
  noit_stats_set_metric(check, "maximum",
                        METRIC_DOUBLE, avail > 0.0 ? &max : NULL);
  noit_stats_set_metric(check, "average",
                        METRIC_DOUBLE, avail > 0.0 ? &avg : NULL);
  noit_check_set_stats(check);
}
Beispiel #4
0
static int
statsd_submit(noit_module_t *self, noit_check_t *check,
              noit_check_t *cause) {
  statsd_closure_t *ccl;
  struct timeval now, duration;
  statsd_mod_config_t *conf;

  conf = noit_module_get_userdata(self);
  if(!conf->primary_active) conf->check = NULL;
  if(0 == memcmp(conf->primary, check->checkid, sizeof(uuid_t))) {
    conf->check = check;
    if(NOIT_CHECK_DISABLED(check) || NOIT_CHECK_KILLED(check)) {
      conf->check = NULL;
      return 0;
    }
  }

  /* We are passive, so we don't do anything for transient checks */
  if(check->flags & NP_TRANSIENT) return 0;

  if(!check->closure) {
    ccl = check->closure = calloc(1, sizeof(*ccl));
    ccl->self = self;
  } else {
    // Don't count the first run
    char human_buffer[256];
    ccl = (statsd_closure_t*)check->closure;
    mtev_gettimeofday(&now, NULL);
    sub_timeval(now, check->last_fire_time, &duration);
    noit_stats_set_whence(check, &now);
    noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);

    snprintf(human_buffer, sizeof(human_buffer),
             "dur=%ld,run=%d,stats=%d", duration.tv_sec * 1000 + duration.tv_usec / 1000,
             check->generation, ccl->stats_count);
    mtevL(nldeb, "statsd(%s) [%s]\n", check->target, human_buffer);

    // Not sure what to do here
    noit_stats_set_available(check, (ccl->stats_count > 0) ?
        NP_AVAILABLE : NP_UNAVAILABLE);
    noit_stats_set_state(check, (ccl->stats_count > 0) ?
        NP_GOOD : NP_BAD);
    noit_stats_set_status(check, human_buffer);
    if(check->last_fire_time.tv_sec)
      noit_check_passive_set_stats(check);

    memcpy(&check->last_fire_time, &now, sizeof(duration));
  }
  ccl->stats_count = 0;
  return 0;
}
static void noit_rabbitmq_heartbeat(struct amqp_driver *dr) {
  struct timeval n, d;
  if(!dr->connection) return;
  mtev_gettimeofday(&n, NULL);
  sub_timeval(n, dr->last_hb, &d);
  if(d.tv_sec >= dr->heartbeat) {
    amqp_frame_t f;
    f.frame_type = AMQP_FRAME_HEARTBEAT;
    f.channel = 0;
    amqp_send_frame(dr->connection, &f);
    mtevL(mtev_debug, "amqp -> hearbeat\n");
    memcpy(&dr->last_hb, &n, sizeof(n));
  }
}
void noit_metric_director_init() {
  nthreads = eventer_loop_concurrency();
  mtevAssert(nthreads > 0);
  thread_queues = calloc(sizeof(*thread_queues),nthreads);
  check_interests = calloc(sizeof(*check_interests),nthreads);
  if(mtev_fq_handle_message_hook_register_available())
    mtev_fq_handle_message_hook_register("metric-director", handle_fq_message, NULL);
  mtev_log_line_hook_register("metric-director", handle_log_line, NULL);

  eventer_t e = eventer_alloc();
  e->mask = EVENTER_TIMER;
  e->callback = prune_old_dedupe_hashes;
  mtev_gettimeofday(&e->whence, NULL);
  e->whence.tv_sec += 2;
  eventer_add_timed(e);
}
static int
noit_console_show_check(mtev_console_closure_t ncct,
                        int argc, char **argv,
                        mtev_console_state_t *state, void *closure) {
  int i, cnt;
  mtev_conf_t_userdata_t *info;
  char xpath[1024];
  xmlXPathObjectPtr pobj = NULL;
  xmlXPathContextPtr xpath_ctxt = NULL;

  mtev_conf_xml_xpath(NULL, &xpath_ctxt);
  if(argc > 1) {
    nc_printf(ncct, "requires zero or one arguments\n");
    return -1;
  }

  info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
  /* We many not be in conf-t mode -- that's fine */
  if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info,
                                argc ? argv[0] : NULL)) {
    nc_printf(ncct, "could not find check '%s'\n", argv[0]);
    return -1;
  }

  pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
  if(!pobj || pobj->type != XPATH_NODESET ||
     xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
    nc_printf(ncct, "no checks found\n");
    goto out;
  }
  cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
  if(info && cnt != 1) {
    nc_printf(ncct, "Ambiguous check specified\n");
    goto out;
  }
  for(i=0; i<cnt; i++) {
    mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
    const char *k;
    int klen;
    void *data;
    uuid_t checkid;
    noit_check_t *check;
    mtev_hash_table *config;
    xmlNodePtr node, anode, mnode = NULL;
    char *uuid_conf;
    char *module, *value;

    node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
    uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
    if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
      nc_printf(ncct, "%s has invalid or missing UUID!\n",
                (char *)xmlGetNodePath(node) + strlen("/noit"));
      continue;
    }
    nc_printf(ncct, "==== %s ====\n", uuid_conf);
    xmlFree(uuid_conf);

#define MYATTR(a,n,b) _mtev_conf_get_string(node, &(n), "@" #a, &(b))
#define INHERIT(a,n,b) \
  _mtev_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
#define SHOW_ATTR(a) do { \
  anode = NULL; \
  value = NULL; \
  INHERIT(a, anode, value); \
  nc_attr_show(ncct, #a, node, anode, value); \
  if(value != NULL) free(value); \
} while(0)

    if(!INHERIT(module, mnode, module)) module = NULL;
    if(MYATTR(name, anode, value)) {
      nc_printf(ncct, " name: %s\n", value);
      free(value);
    }
    else
      nc_printf(ncct, " name: %s [from module]\n", module ? module : "[undef]");
    nc_attr_show(ncct, "module", node, mnode, module);
    if(module) free(module);
    SHOW_ATTR(target);
    SHOW_ATTR(seq);
    SHOW_ATTR(resolve_rtype);
    SHOW_ATTR(period);
    SHOW_ATTR(timeout);
    SHOW_ATTR(oncheck);
    SHOW_ATTR(filterset);
    SHOW_ATTR(disable);
    /* Print out all the config settings */
    config = mtev_conf_get_hash(node, "config");
    while(mtev_hash_next(config, &iter, &k, &klen, &data)) {
      nc_printf(ncct, " config::%s: %s\n", k, (const char *)data);
    }
    mtev_hash_destroy(config, free, free);
    free(config);

    check = noit_poller_lookup(checkid);
    if(!check) {
      nc_printf(ncct, " ERROR: not in running system\n");
    }
    else {
      int idx = 0;
      stats_t *c;
      struct timeval *whence;
      mtev_hash_table *metrics;
      nc_printf(ncct, " target_ip: %s\n", check->target_ip);
      nc_printf(ncct, " currently: %08x ", check->flags);
      if(NOIT_CHECK_RUNNING(check)) { nc_printf(ncct, "running"); idx++; }
      if(NOIT_CHECK_KILLED(check)) nc_printf(ncct, "%skilled", idx++?",":"");
      if(!NOIT_CHECK_CONFIGURED(check)) nc_printf(ncct, "%sunconfig", idx++?",":"");
      if(NOIT_CHECK_DISABLED(check)) nc_printf(ncct, "%sdisabled", idx++?",":"");
      if(!idx) nc_printf(ncct, "idle");
      nc_write(ncct, "\n", 1);
      if (check->fire_event != NULL) {
        struct timeval now, diff;
        mtev_gettimeofday(&now, NULL);
        sub_timeval(check->fire_event->whence, now, &diff);
        nc_printf(ncct, " next run: %0.3f seconds\n",
                diff.tv_sec + (diff.tv_usec / 1000000.0));
      }
      else {
        nc_printf(ncct, " next run: unscheduled\n");
      }

      c = noit_check_get_stats_current(check);
      whence = noit_check_stats_whence(c, NULL);
      if(whence->tv_sec == 0) {
        nc_printf(ncct, " last run: never\n");
      }
      else {
        const char *status;
        struct timeval now, *then, diff;
        mtev_gettimeofday(&now, NULL);
        then = noit_check_stats_whence(c, NULL);
        sub_timeval(now, *then, &diff);
        nc_printf(ncct, " last run: %0.3f seconds ago\n",
                  diff.tv_sec + (diff.tv_usec / 1000000.0));
        nc_printf(ncct, " availability/state: %s/%s\n",
                  noit_check_available_string(noit_check_stats_available(c, NULL)),
                  noit_check_state_string(noit_check_stats_state(c, NULL)));
        status = noit_check_stats_status(c, NULL);
        nc_printf(ncct, " status: %s\n", status);
        nc_printf(ncct, " feeds: %d\n", check->feeds ? check->feeds->size : 0);
      }

      c = noit_check_get_stats_inprogress(check);
      metrics = noit_check_stats_metrics(c);
      if(mtev_hash_size(metrics) > 0) {
        nc_printf(ncct, " metrics (inprogress):\n");
        nc_print_stat_metrics(ncct, check, c);
      }
      c = noit_check_get_stats_current(check);
      metrics = noit_check_stats_metrics(c);
      if(mtev_hash_size(metrics)) {
        nc_printf(ncct, " metrics (current):\n");
        nc_print_stat_metrics(ncct, check, c);
      }
      c = noit_check_get_stats_previous(check);
      metrics = noit_check_stats_metrics(c);
      if(mtev_hash_size(metrics) > 0) {
        nc_printf(ncct, " metrics (previous):\n");
        nc_print_stat_metrics(ncct, check, c);
      }
    }
  }
 out:
  if(pobj) xmlXPathFreeObject(pobj);
  return 0;
}
static interim_journal_t *
interim_journal_get(struct sockaddr *remote, const char *remote_cn_in,
                    int storagenode_id, const char *fqdn_in) {
  void *vhash, *vij;
  mtev_hash_table *working_set;
  interim_journal_t *ij;
  struct timeval now;
  char jpath[PATH_MAX];
  char remote_str[128];
  const char *remote_cn = remote_cn_in ? remote_cn_in : "default";
  const char *fqdn = fqdn_in ? fqdn_in : "default";

  mtev_convert_sockaddr_to_buff(remote_str, sizeof(remote_str), remote);
  if(!*remote_str) strlcpy(remote_str, "default", sizeof(remote_str));

  /* Lookup the working set */
  if(!mtev_hash_retrieve(&working_sets, remote_cn, strlen(remote_cn), &vhash)) {
    working_set = calloc(1, sizeof(*working_set));
    mtev_hash_init(working_set);
    mtev_hash_store(&working_sets, strdup(remote_cn), strlen(remote_cn),
                    working_set);
  }
  else
    working_set = vhash;

  /* Lookup the interim journal within the working set */
  if(!mtev_hash_retrieve(working_set, fqdn, strlen(fqdn), &vij)) {
    ij = calloc(1, sizeof(*ij));
    mtev_gettimeofday(&now, NULL);
    snprintf(jpath, sizeof(jpath), "%s/%s/%s/%d/%08x%08x.tmp",
             basejpath, remote_str, remote_cn, storagenode_id,
             (unsigned int)now.tv_sec, (unsigned int)now.tv_usec);
    ij->remote_str = strdup(remote_str);
    ij->remote_cn = strdup(remote_cn);
    ij->fqdn = fqdn_in ? strdup(fqdn_in) : NULL;
    ij->storagenode_id = storagenode_id;
    ij->filename = strdup(jpath);
    ij->fd = open(ij->filename, O_RDWR | O_CREAT | O_EXCL, 0640);
    if(ij->fd < 0 && errno == ENOENT) {
      if(mkdir_for_file(ij->filename, 0750)) {
        mtevL(noit_error, "Failed to create dir for '%s': %s\n",
              ij->filename, strerror(errno));
        exit(-1);
      }
      ij->fd = open(ij->filename, O_RDWR | O_CREAT | O_EXCL, 0640);
    }
    if(ij->fd < 0 && errno == EEXIST) {
      /* This can only occur if we crash after before checkpointing */
      unlink(ij->filename);
      ij->fd = open(ij->filename, O_RDWR | O_CREAT | O_EXCL, 0640);
    }
    if(ij->fd < 0) {
      mtevL(noit_error, "Failed to open interim journal '%s': %s\n",
            ij->filename, strerror(errno));
      exit(-1);
    }
    mtev_hash_store(working_set, strdup(fqdn), strlen(fqdn), ij);
  }
  else
    ij = vij;

  return ij;
}
static int eventer_epoll_impl_loop() {
  struct epoll_event *epev;
  struct epoll_spec *spec;

  spec = eventer_get_spec_for_event(NULL);
  epev = malloc(sizeof(*epev) * maxfds);

#ifdef HAVE_SYS_EVENTFD_H
  if(spec->event_fd >= 0) {
    eventer_t e = eventer_alloc();
    e->callback = eventer_epoll_eventfd_read;
    e->fd = spec->event_fd;
    e->mask = EVENTER_READ;
    eventer_add(e);
  }
#endif

  while(1) {
    struct timeval __now, __sleeptime;
    int fd_cnt = 0;

    __sleeptime = eventer_max_sleeptime;

    mtev_gettimeofday(&__now, NULL);
    eventer_dispatch_timed(&__now, &__sleeptime);

    /* Handle cross_thread dispatches */
    eventer_cross_thread_process();

    /* Handle recurrent events */
    eventer_dispatch_recurrent(&__now);

    /* Now we move on to our fd-based events */
    do {
      fd_cnt = epoll_wait(spec->epoll_fd, epev, maxfds,
                          __sleeptime.tv_sec * 1000 + __sleeptime.tv_usec / 1000);
    } while(fd_cnt < 0 && errno == EINTR);
    mtevLT(eventer_deb, &__now, "debug: epoll_wait(%d, [], %d) => %d\n",
           spec->epoll_fd, maxfds, fd_cnt);
    if(fd_cnt < 0) {
      mtevLT(eventer_err, &__now, "epoll_wait: %s\n", strerror(errno));
    }
    else {
      int idx;
      /* loop once to clear */
      for(idx = 0; idx < fd_cnt; idx++) {
        struct epoll_event *ev;
        eventer_t e;
        int fd, mask = 0;

        ev = &epev[idx];

        if(ev->events & (EPOLLIN | EPOLLPRI)) mask |= EVENTER_READ;
        if(ev->events & (EPOLLOUT)) mask |= EVENTER_WRITE;
        if(ev->events & (EPOLLERR|EPOLLHUP)) mask |= EVENTER_EXCEPTION;

        fd = ev->data.fd;

        e = master_fds[fd].e;
        /* It's possible that someone removed the event and freed it
         * before we got here.
         */
        if(!e) continue;

        eventer_epoll_impl_trigger(e, mask);
      }
    }
  }
  /* NOTREACHED */
  return 0;
}
static void eventer_kqueue_impl_trigger(eventer_t e, int mask) {
  ev_lock_state_t lockstate;
  struct timeval __now;
  int oldmask, newmask;
  const char *cbname;
  int fd;
  u_int64_t start, duration;
  int cross_thread = mask & EVENTER_CROSS_THREAD_TRIGGER;

  mask = mask & ~(EVENTER_RESERVED);
  fd = e->fd;
  if(cross_thread) {
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */
  }
  if(!pthread_equal(pthread_self(), e->thr_owner)) {
    /* If we're triggering across threads, it can't be registered yet */
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */

    eventer_cross_thread_trigger(e,mask);
    return;
  }
  if(master_fds[fd].e == NULL) {
    master_fds[fd].e = e;
    e->mask = 0;
  }
  if(e != master_fds[fd].e) return;
  lockstate = acquire_master_fd(fd);
  if(lockstate == EV_ALREADY_OWNED) return;
  mtevAssert(lockstate == EV_OWNED);

  mtev_gettimeofday(&__now, NULL);
  /* We're going to lie to ourselves.  You'd think this should be:
   * oldmask = e->mask;  However, we just fired with masks[fd], so
   * kqueue is clearly looking for all of the events in masks[fd].
   * So, we combine them "just to be safe."
   */
  oldmask = e->mask | masks[fd];
  cbname = eventer_name_for_callback_e(e->callback, e);
  mtevLT(eventer_deb, &__now, "kqueue: fire on %d/%x to %s(%p)\n",
         fd, masks[fd], cbname?cbname:"???", e->callback);
  mtev_memory_begin();
  LIBMTEV_EVENTER_CALLBACK_ENTRY((void *)e, (void *)e->callback, (char *)cbname, fd, e->mask, mask);
  start = mtev_gethrtime();
  newmask = e->callback(e, mask, e->closure, &__now);
  duration = mtev_gethrtime() - start;
  LIBMTEV_EVENTER_CALLBACK_RETURN((void *)e, (void *)e->callback, (char *)cbname, newmask);
  mtev_memory_end();
  stats_set_hist_intscale(eventer_callback_latency, duration, -9, 1);
  stats_set_hist_intscale(eventer_latency_handle_for_callback(e->callback), duration, -9, 1);

  if(newmask) {
    if(!pthread_equal(pthread_self(), e->thr_owner)) {
      pthread_t tgt = e->thr_owner;
      e->thr_owner = pthread_self();
      alter_kqueue_mask(e, oldmask, 0);
      e->thr_owner = tgt;
      mtevL(eventer_deb, "moved event[%p] from t@%llx to t@%llx\n", e, (vpsized_int)pthread_self(), (vpsized_int)tgt);
      if(newmask) eventer_cross_thread_trigger(e, newmask & ~(EVENTER_EXCEPTION));
    }
    else {
      if(master_fds[fd].e != e) {
        e = master_fds[fd].e;
        mtevL(eventer_deb, "%strigger complete [event switched] %d : %x->%x\n", cross_thread ? "[X]" : "", e->fd, master_fds[fd].e->mask, newmask);
      } else {
        mtevL(eventer_deb, "%strigger complete %d : %x->%x\n", cross_thread ? "[X]" : "", e->fd, oldmask, newmask);
      }
      alter_kqueue_mask(e, (e->mask == 0 || cross_thread) ? 0 : oldmask, newmask);
      /* Set our mask */
      e->mask = newmask;
    }
  }
  else {
    /*
     * Long story long:
     *  When integrating with a few external event systems, we find
     *  it difficult to make their use of remove+add as an update
     *  as it can be recurrent in a single handler call and you cannot
     *  remove completely from the event system if you are going to
     *  just update (otherwise the eventer_t in your call stack could
     *  be stale).  What we do is perform a superficial remove, marking
     *  the mask as 0, but not eventer_remove_fd.  Then on an add, if
     *  we already have an event, we just update the mask (as we
     *  have not yet returned to the eventer's loop.
     *  This leaves us in a tricky situation when a remove is called
     *  and the add doesn't roll in, we return 0 (mask == 0) and hit
     *  this spot.  We have intended to remove the event, but it still
     *  resides at master_fds[fd].e -- even after we free it.
     *  So, in the evnet that we return 0 and the event that
     *  master_fds[fd].e == the event we're about to free... we NULL
     *  it out.
     */
    if(master_fds[fd].e == e) master_fds[fd].e = NULL;
    eventer_free(e);
  }
  release_master_fd(fd, lockstate);
}
void noit_check_resolver_init() {
  static int initialized = 0;
  int32_t cnt;
  mtev_conf_section_t *servers, *searchdomains;
  eventer_t e;

  DCLOCK();
  if(initialized != 0) {
    DCUNLOCK();
    return;
  }

  if(dns_init(NULL, 0) < 0)
    mtevL(noit_error, "dns initialization failed.\n");
  dns_ctx = dns_new(NULL);
  if(dns_init(dns_ctx, 0) != 0) {
    mtevL(noit_error, "dns initialization failed.\n");
    DCUNLOCK();
    exit(-1);
  }

  /* Optional servers */
  servers = mtev_conf_get_sections(MTEV_CONF_ROOT, "//resolver//server", &cnt);
  if(cnt) {
    int i;
    char server[128];
    dns_add_serv(dns_ctx, NULL); /* reset */
    for(i=0;i<cnt;i++) {
      if(mtev_conf_get_stringbuf(servers[i], "self::node()",
                                 server, sizeof(server))) {
        if(mtev_conf_env_off(servers[i], NULL)) {
          mtevL(noit_error, "DNS server %s environmentally ignored.\n", server);
        }
        else if(dns_add_serv(dns_ctx, server) < 0) {
          mtevL(noit_error, "Failed adding DNS server: %s\n", server);
        }
      }
    }
  }
  mtev_conf_release_sections(servers, cnt);
  searchdomains = mtev_conf_get_sections(MTEV_CONF_ROOT, "//resolver//search", &cnt);
  if(cnt) {
    int i;
    char search[128];
    dns_add_srch(dns_ctx, NULL); /* reset */
    for(i=0;i<cnt;i++) {
      if(mtev_conf_get_stringbuf(searchdomains[i], "self::node()",
                                 search, sizeof(search))) {
        if(mtev_conf_env_off(searchdomains[i], NULL)) {
          mtevL(noit_error, "DNS search %s environmentally ignored.\n", search);
        }
        else if(dns_add_srch(dns_ctx, search) < 0) {
          mtevL(noit_error, "Failed adding DNS search path: %s\n", search);
        }
        else if(dns_search_flag) dns_search_flag = 0; /* enable search */
      }
    }
  }
  mtev_conf_release_sections(searchdomains, cnt);

  if(mtev_conf_get_int32(MTEV_CONF_ROOT, "//resolver/@ndots", &cnt))
    dns_set_opt(dns_ctx, DNS_OPT_NDOTS, cnt);

  if(mtev_conf_get_int32(MTEV_CONF_ROOT, "//resolver/@ntries", &cnt))
    dns_set_opt(dns_ctx, DNS_OPT_NTRIES, cnt);

  if(mtev_conf_get_int32(MTEV_CONF_ROOT, "//resolver/@timeout", &cnt))
    dns_set_opt(dns_ctx, DNS_OPT_TIMEOUT, cnt);

  if(dns_open(dns_ctx) < 0) {
    mtevL(noit_error, "dns open failed.\n");
    DCUNLOCK();
    exit(-1);
  }
  eventer_name_callback("dns_cache_callback", dns_cache_callback);
  dns_set_tmcbck(dns_ctx, dns_cache_utm_fn, dns_ctx);
  e = eventer_alloc_fd(dns_cache_callback, dns_ctx, dns_sock(dns_ctx),
                       EVENTER_READ|EVENTER_EXCEPTION);
  eventer_add(e);

  mtev_skiplist *dc = mtev_skiplist_alloc();
  mtev_skiplist_set_compare(dc, name_lookup, name_lookup_k);
  mtev_skiplist_add_index(dc, refresh_idx, refresh_idx_k);
  ck_pr_barrier();
  nc_dns_cache = dc;

  /* maybe load it from cache */
  if(noit_resolver_cache_load_hook_exists()) {
    struct timeval now;
    char *key;
    void *data;
    int len;
    mtev_gettimeofday(&now, NULL);
    while(noit_resolver_cache_load_hook_invoke(&key, &data, &len) == MTEV_HOOK_CONTINUE) {
      dns_cache_node *n;
      n = calloc(1, sizeof(*n));
      if(dns_cache_node_deserialize(n, data, len) >= 0) {
        n->target = strdup(key);
        /* if the TTL indicates that it will expire in less than 60 seconds
         * (including stuff that should have already expired), then fudge
         * the last_updated time to make it expire some random time within
         * the next 60 seconds.
         */
        if(n->last_needed > now.tv_sec || n->last_updated > now.tv_sec) {
          dns_cache_node_free(n);
          break; /* impossible */
        }

        n->last_needed = now.tv_sec;
        if(n->last_updated + n->ttl < now.tv_sec + 60) {
          int fudge = MIN(60, n->ttl) + 1;
          n->last_updated = now.tv_sec - n->ttl + (lrand48() % fudge);
        }
        mtev_skiplist_insert(nc_dns_cache, n);
        n = NULL;
      }
      else {
        mtevL(noit_error, "Failed to deserialize resolver cache record.\n");
      }
      if(n) dns_cache_node_free(n);
      if(key) free(key);
      if(data) free(data);
    }
  }

  noit_check_resolver_loop(NULL, 0, NULL, NULL);
  register_console_dns_cache_commands();

  mtev_hash_init(&etc_hosts_cache);
  noit_check_etc_hosts_cache_refresh(NULL, 0, NULL, NULL);
  initialized = 1;
  DCUNLOCK();
}
int
noit_jlog_handler(eventer_t e, int mask, void *closure,
                     struct timeval *now) {
  eventer_t newe;
  pthread_t tid;
  pthread_attr_t tattr;
  int newmask = EVENTER_READ | EVENTER_EXCEPTION;
  acceptor_closure_t *ac = closure;
  noit_jlog_closure_t *jcl = ac->service_ctx;
  char errbuff[256];
  const char *errstr = "unknown error";

  if(mask & EVENTER_EXCEPTION || (jcl && jcl->wants_shutdown)) {
    int len, nlen;
socket_error:
    /* Exceptions cause us to simply snip the connection */
    len = strlen(errstr);
    nlen = htonl(0 - len);
    e->opset->write(e->fd, &nlen, sizeof(nlen), &newmask, e);
    e->opset->write(e->fd, errstr, strlen(errstr), &newmask, e);
    eventer_remove_fd(e->fd);
    e->opset->close(e->fd, &newmask, e);
    if(jcl) noit_jlog_closure_free(jcl);
    acceptor_closure_free(ac);
    return 0;
  }

  if(!ac->service_ctx) {
    mtev_log_stream_t ls;
    const char *logname, *type;
    int first_attempt = 1;
    char path[PATH_MAX], subscriber[256], *sub;
    jcl = ac->service_ctx = noit_jlog_closure_alloc();
    if(!mtev_hash_retr_str(ac->config,
                           "log_transit_feed_name",
                           strlen("log_transit_feed_name"),
                           &logname)) {
      errstr = "No 'log_transit_feed_name' specified in log_transit.";
      mtevL(noit_error, "%s\n", errstr);
      goto socket_error;
    }
    ls = mtev_log_stream_find(logname);
    if(!ls) {
      snprintf(errbuff, sizeof(errbuff),
               "Could not find log '%s' for log_transit.", logname);
      errstr = errbuff;
      mtevL(noit_error, "%s\n", errstr);
      goto socket_error;
    }
    type = mtev_log_stream_get_type(ls);
    if(!type || strcmp(type, "jlog")) {
      snprintf(errbuff, sizeof(errbuff),
               "Log '%s' for log_transit is not a jlog.", logname);
      errstr = errbuff;
      mtevL(noit_error, "%s\n", errstr);
      goto socket_error;
    }
    if(ac->cmd == NOIT_JLOG_DATA_FEED) {
      if(!ac->remote_cn) {
        errstr = "jlog transit started to unidentified party.";
        mtevL(noit_error, "%s\n", errstr);
        goto socket_error;
      }
      strlcpy(subscriber, ac->remote_cn, sizeof(subscriber));
      jcl->feed_stats = noit_jlog_feed_stats(subscriber);
    }
    else {
      jcl->feed_stats = noit_jlog_feed_stats("~");
      snprintf(subscriber, sizeof(subscriber),
               "~%07d", mtev_atomic_inc32(&tmpfeedcounter));
    }
    jcl->subscriber = strdup(subscriber);

    strlcpy(path, mtev_log_stream_get_path(ls), sizeof(path));
    sub = strchr(path, '(');
    if(sub) {
      char *esub = strchr(sub, ')');
      if(esub) {
        *esub = '\0';
        *sub++ = '\0';
      }
    }

    jcl->jlog = jlog_new(path);
    if(ac->cmd == NOIT_JLOG_DATA_TEMP_FEED) {
 add_sub:
      if(jlog_ctx_add_subscriber(jcl->jlog, jcl->subscriber, JLOG_END) == -1) {
        snprintf(errbuff, sizeof(errbuff),
                 "jlog reader[%s] error: %s", jcl->subscriber,
                 jlog_ctx_err_string(jcl->jlog));
        errstr = errbuff;
        mtevL(noit_error, "%s\n", errstr);
      }
    }
    if(jlog_ctx_open_reader(jcl->jlog, jcl->subscriber) == -1) {
      if(sub && !strcmp(sub, "*")) {
        if(first_attempt) {
          jlog_ctx_close(jcl->jlog);
          jcl->jlog = jlog_new(path);
          first_attempt = 0;
          goto add_sub;
        }
      }
      snprintf(errbuff, sizeof(errbuff),
               "jlog reader[%s] error: %s", jcl->subscriber,
               jlog_ctx_err_string(jcl->jlog));
      errstr = errbuff;
      mtevL(noit_error, "%s\n", errstr);
      goto socket_error;
    }
  }

  /* The jlog stuff is disk I/O and can block us.
   * We'll create a new thread to just handle this connection.
   */
  eventer_remove_fd(e->fd);
  newe = eventer_alloc();
  memcpy(newe, e, sizeof(*e));
  pthread_attr_init(&tattr);
  pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
  mtev_gettimeofday(&jcl->feed_stats->last_connection, NULL);
  mtev_atomic_inc32(&jcl->feed_stats->connections);
  if(pthread_create(&tid, &tattr, noit_jlog_thread_main, newe) == 0) {
    return 0;
  }

  /* Undo our dup */
  eventer_free(newe);
  /* Creating the thread failed, close it down and deschedule. */
  e->opset->close(e->fd, &newmask, e);
  return 0;
}
void *
noit_jlog_thread_main(void *e_vptr) {
  int mask, bytes_read, sleeptime, max_sleeptime;
  eventer_t e = e_vptr;
  acceptor_closure_t *ac = e->closure;
  noit_jlog_closure_t *jcl = ac->service_ctx;
  char inbuff[sizeof(jlog_id)];

  mtev_memory_init_thread();
  eventer_set_fd_blocking(e->fd);

  max_sleeptime = DEFAULT_MSECONDS_BETWEEN_BATCHES;
  if(ac->cmd == NOIT_JLOG_DATA_TEMP_FEED)
    max_sleeptime = DEFAULT_TRANSIENT_MSECONDS_BETWEEN_BATCHES;

  sleeptime = max_sleeptime;
  while(1) {
    jlog_id client_chkpt;
    sleeptime = MIN(sleeptime, max_sleeptime);
    jlog_get_checkpoint(jcl->jlog, ac->remote_cn, &jcl->chkpt);
    jcl->count = jlog_ctx_read_interval(jcl->jlog, &jcl->start, &jcl->finish);
    if(jcl->count < 0) {
      char idxfile[PATH_MAX];
      mtevL(noit_error, "jlog_ctx_read_interval: %s\n",
            jlog_ctx_err_string(jcl->jlog));
      switch (jlog_ctx_err(jcl->jlog)) {
        case JLOG_ERR_FILE_CORRUPT:
        case JLOG_ERR_IDX_CORRUPT:
          jlog_repair_datafile(jcl->jlog, jcl->start.log);
          jlog_repair_datafile(jcl->jlog, jcl->start.log + 1);
          mtevL(noit_error,
                "jlog reconstructed, deleting corresponding index.\n");
          STRSETDATAFILE(jcl->jlog, idxfile, jcl->start.log);
          strlcat(idxfile, INDEX_EXT, sizeof(idxfile));
          unlink(idxfile);
          STRSETDATAFILE(jcl->jlog, idxfile, jcl->start.log + 1);
          strlcat(idxfile, INDEX_EXT, sizeof(idxfile));
          unlink(idxfile);
          goto alldone;
          break;
        default:
          goto alldone;
      }
    }
    if(jcl->count > MAX_ROWS_AT_ONCE) {
      /* Artificially set down the range to make the batches a bit easier
       * to handle on the stratcond/postgres end.
       * However, we must have more data, so drop the sleeptime to 0
       */
      jcl->count = MAX_ROWS_AT_ONCE;
      jcl->finish.marker = jcl->start.marker + jcl->count;
    }
    if(jcl->count > 0) {
      sleeptime = 0;
      if(noit_jlog_push(e, jcl)) {
        goto alldone;
      }
      /* Read our jlog_id accounting for possibly short reads */
      bytes_read = 0;
      while(bytes_read < sizeof(jlog_id)) {
        int len;
        if((len = e->opset->read(e->fd, inbuff + bytes_read,
                                 sizeof(jlog_id) - bytes_read,
                                 &mask, e)) <= 0)
          goto alldone;
        bytes_read += len;
      }
      memcpy(&client_chkpt, inbuff, sizeof(jlog_id));
      /* Fix the endian */
      client_chkpt.log = ntohl(client_chkpt.log);
      client_chkpt.marker = ntohl(client_chkpt.marker);
  
      if(memcmp(&jcl->chkpt, &client_chkpt, sizeof(jlog_id))) {
        mtevL(noit_error,
              "client %s submitted invalid checkpoint %u:%u expected %u:%u\n",
              ac->remote_cn, client_chkpt.log, client_chkpt.marker,
              jcl->chkpt.log, jcl->chkpt.marker);
        goto alldone;
      }
      mtev_gettimeofday(&jcl->feed_stats->last_checkpoint, NULL);
      jlog_ctx_read_checkpoint(jcl->jlog, &jcl->chkpt);
    }
    else {
      /* we have nothing to write -- maybe we have no checks configured...
       * If this is the case "forever", the remote might disconnect and
       * we would never know. Do the painful work of detecting a
       * disconnected client.
       */
      struct pollfd pfd;
      pfd.fd = e->fd;
      pfd.events = POLLIN | POLLHUP | POLLRDNORM;
      pfd.revents = 0;
      if(poll(&pfd, 1, 0) != 0) {
        /* normally, we'd recv PEEK|DONTWAIT.  However, the client should
         * not be writing to us.  So, we know we can't have any legitimate
         * data on this socket (true even though this is SSL). So, if we're
         * here then "shit went wrong"
         */
        mtevL(noit_error, "jlog client %s disconnected while idle\n",
              ac->remote_cn);
        goto alldone;
      }
    }
    if(sleeptime) {
      usleep(sleeptime * 1000); /* us -> ms */
    }
    sleeptime += 1000; /* 1 s */
  }

 alldone:
  e->opset->close(e->fd, &mask, e);
  mtev_atomic_dec32(&jcl->feed_stats->connections);
  noit_jlog_closure_free(jcl);
  acceptor_closure_free(ac);
  mtev_memory_maintenance();
  return NULL;
}
Beispiel #14
0
static void eventer_epoll_impl_trigger(eventer_t e, int mask) {
  struct epoll_spec *spec;
  struct timeval __now;
  int fd, newmask, needs_add = 0;
  const char *cbname;
  ev_lock_state_t lockstate;
  int cross_thread = mask & EVENTER_CROSS_THREAD_TRIGGER;
  uint64_t start, duration;

  mask = mask & ~(EVENTER_RESERVED);
  fd = e->fd;
  if(cross_thread) {
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */
  }
  if(!pthread_equal(pthread_self(), e->thr_owner)) {
    /* If we're triggering across threads, it can't be registered yet */
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */

    eventer_cross_thread_trigger(e,mask);
    return;
  }
  if(master_fds[fd].e == NULL) {
    lockstate = acquire_master_fd(fd);
    if (lockstate == EV_ALREADY_OWNED) {
      /* The incoming triggered event is already owned by this thread.
       * This means our floated event completed before the current
       * event handler even exited.  So it retriggered recursively
       * from inside the event handler.
       *
       * Treat this special case the same as a cross thread trigger
       * and just queue this event to be picked up on the next loop
       */
      eventer_cross_thread_trigger(e, mask);
      return;
    }
    /*
     * If we are readding the event to the master list here, also do the needful
     * with the epoll_ctl.
     *
     * This can happen in cases where some event was floated and the float
     * completed so fast that we finished the job in the same thread 
     * that it started in.  Since we `eventer_remove_fd` before we float
     * the re-add here should replace the fd in the epoll_ctl.
     */
    master_fds[fd].e = e;
    e->mask = 0;
    struct epoll_event _ev;
    memset(&_ev, 0, sizeof(_ev));
    _ev.data.fd = fd;
    spec = eventer_get_spec_for_event(e);
    if(mask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
    if(mask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
    if(mask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);

    mtevL(eventer_deb, "epoll_ctl(%d, add, %d)\n", spec->epoll_fd, fd);
    if (epoll_ctl(spec->epoll_fd, EPOLL_CTL_ADD, fd, &_ev) != 0) {
      mtevL(mtev_error, "epoll_ctl(%d, add, %d, %d)\n", spec->epoll_fd, fd, errno);
    }
    release_master_fd(fd, lockstate);
  }
  if(e != master_fds[fd].e) {
    mtevL(mtev_error, "Incoming event: %p, does not match master list: %p\n", e, master_fds[fd].e);
    return;
  }
  lockstate = acquire_master_fd(fd);
  if(lockstate == EV_ALREADY_OWNED) {
    mtevL(eventer_deb, "Incoming event: %p already owned by this thread\n", e);
    return;
  }
  mtevAssert(lockstate == EV_OWNED);

  mtev_gettimeofday(&__now, NULL);
  cbname = eventer_name_for_callback_e(e->callback, e);
  spec = eventer_get_spec_for_event(e);
  mtevLT(eventer_deb, &__now, "epoll(%d): fire on %d/%x to %s(%p)\n",
         spec->epoll_fd, fd, mask, cbname?cbname:"???", e->callback);
  mtev_memory_begin();
  LIBMTEV_EVENTER_CALLBACK_ENTRY((void *)e, (void *)e->callback, (char *)cbname, fd, e->mask, mask);
  start = mtev_gethrtime();
  newmask = e->callback(e, mask, e->closure, &__now);
  duration = mtev_gethrtime() - start;
  LIBMTEV_EVENTER_CALLBACK_RETURN((void *)e, (void *)e->callback, (char *)cbname, newmask);
  mtev_memory_end();
  stats_set_hist_intscale(eventer_callback_latency, duration, -9, 1);
  stats_set_hist_intscale(eventer_latency_handle_for_callback(e->callback), duration, -9, 1);

  if(newmask) {
    struct epoll_event _ev;
    memset(&_ev, 0, sizeof(_ev));
    _ev.data.fd = fd;
    if(newmask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
    if(newmask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
    if(newmask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);
    if(master_fds[fd].e == NULL) {
      mtevL(mtev_debug, "eventer %s(%p) epoll asked to modify descheduled fd: %d\n",
            cbname?cbname:"???", e->callback, fd);
    } else {
      if(!pthread_equal(pthread_self(), e->thr_owner)) {
        pthread_t tgt = e->thr_owner;
        e->thr_owner = pthread_self();
        spec = eventer_get_spec_for_event(e);
        if(e->mask != 0 && !needs_add) {
          mtevL(eventer_deb, "epoll_ctl(%d, del, %d)\n", spec->epoll_fd, fd);
          if(epoll_ctl(spec->epoll_fd, EPOLL_CTL_DEL, fd, &_ev) != 0) {
            mtevFatal(mtev_error,
                      "epoll_ctl(spec->epoll_fd, EPOLL_CTL_DEL, fd, &_ev) failed; "
                      "spec->epoll_fd: %d; fd: %d; errno: %d (%s)\n",
                      spec->epoll_fd, fd, errno, strerror(errno));
          }
        }
        e->thr_owner = tgt;
        spec = eventer_get_spec_for_event(e);
        mtevL(eventer_deb, "epoll_ctl(%d, add, %d)\n", spec->epoll_fd, fd);
        mtevAssert(epoll_ctl(spec->epoll_fd, EPOLL_CTL_ADD, fd, &_ev) == 0);
        mtevL(eventer_deb, "epoll(%d) moved event[%p] from t@%d to t@%d\n", spec->epoll_fd, e, (int)pthread_self(), (int)tgt);
      }
      else {
        int epoll_rv;
        int epoll_cmd = (e->mask == 0 || needs_add) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
        spec = eventer_get_spec_for_event(e);
        mtevL(eventer_deb, "epoll_ctl(%d, %s, %d)\n", spec->epoll_fd, epoll_cmd == EPOLL_CTL_ADD ? "add" : "mod", fd);
        epoll_rv = epoll_ctl(spec->epoll_fd, epoll_cmd, fd, &_ev);
        if(epoll_rv != 0 &&
           ((epoll_cmd == EPOLL_CTL_ADD && errno == EEXIST) ||
            (epoll_cmd == EPOLL_CTL_MOD && errno == ENOENT))) {
            /* try the other way */
          epoll_cmd = (epoll_cmd == EPOLL_CTL_ADD) ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
          mtevL(eventer_deb, "retry epoll_ctl(%d, %s, %d)\n", spec->epoll_fd, epoll_cmd == EPOLL_CTL_ADD ? "add" : "mod", fd);
	  epoll_rv = epoll_ctl(spec->epoll_fd, epoll_cmd, fd, &_ev);
        }
        if(epoll_rv != 0) {
          const char *cb_name = eventer_name_for_callback_e(e->callback, e);
          mtevFatal(mtev_error,
                    "epoll_ctl(spec->epoll_fd, %s, fd, &_ev) failed; "
                    "spec->epoll_fd: %d; fd: %d; errno: %d (%s); callback: %s\n",
                    epoll_cmd == EPOLL_CTL_ADD ? "EPOLL_CTL_ADD" : "EPOLL_CTL_MOD",
                    spec->epoll_fd, fd, errno, strerror(errno), cb_name ? cb_name : "???");
        }
      }
    }
    /* Set our mask */
    e->mask = newmask;
  }
  else {
    /* see kqueue implementation for details on the next line */
    if(master_fds[fd].e == e) {

      /* if newmask == 0 the user has floated the connection.  If we get here
       * and they have not called `eventer_remove_fd` it is a misuse of mtev.
       *
       * Check if they are compliant with floats here and remove_fd if they
       * forgot to and warn in the log
       */
      spec = eventer_get_spec_for_event(e);
      struct epoll_event _ev;
      memset(&_ev, 0, sizeof(_ev));
      _ev.data.fd = fd;
      if (epoll_ctl(spec->epoll_fd, EPOLL_CTL_DEL, e->fd, &_ev) == 0) {
        mtevL(mtev_error, "WARNING: You forgot to 'eventer_remove_fd()' before returning a mask of zero.\n");
      }
      master_fds[fd].e = NULL;
    }
    eventer_free(e);
  }
  release_master_fd(fd, lockstate);
}
Beispiel #15
0
static int ping_icmp_handler(eventer_t e, int mask,
                             void *closure, struct timeval *now,
                             u_int8_t family) {
  noit_module_t *self = (noit_module_t *)closure;
  ping_icmp_data_t *ping_data;
  struct check_info *data;
  char packet[1500];
  int packet_len = sizeof(packet);
  union {
   struct sockaddr_in  in4;
   struct sockaddr_in6 in6;
  } from;
  unsigned int from_len;
  struct ping_payload *payload;

  if(family != AF_INET && family != AF_INET6) return EVENTER_READ;

  ping_data = noit_module_get_userdata(self);
  while(1) {
    struct ping_session_key k;
    int inlen;
    u_int8_t iphlen = 0;
    void *vcheck;
    noit_check_t *check;
    struct timeval tt, whence;

    from_len = sizeof(from);

    inlen = recvfrom(e->fd, packet, packet_len, 0,
                     (struct sockaddr *)&from, &from_len);
    mtev_gettimeofday(now, NULL); /* set it, as we care about accuracy */

    if(inlen < 0) {
      if(errno == EAGAIN || errno == EINTR) break;
      mtevLT(nldeb, now, "ping_icmp recvfrom: %s\n", strerror(errno));
      break;
    }

    if(family == AF_INET) {
      struct icmp *icp4;
      iphlen = ((struct ip *)packet)->ip_hl << 2;
      if((inlen-iphlen) != sizeof(struct icmp)+PING_PAYLOAD_LEN) {
        mtevLT(nldeb, now,
               "ping_icmp bad size: %d+%d\n", iphlen, inlen-iphlen); 
        continue;
      }
      icp4 = (struct icmp *)(packet + iphlen);
      payload = (struct ping_payload *)(icp4 + 1);
      if(icp4->icmp_type != ICMP_ECHOREPLY) {
        mtevLT(nldeb, now, "ping_icmp bad type: %d\n", icp4->icmp_type);
        continue;
      }
      if(icp4->icmp_id != (((vpsized_uint)self) & 0xffff)) {
        mtevLT(nldeb, now,
                 "ping_icmp not sent from this instance (%d:%d) vs. %lu\n",
                 icp4->icmp_id, ntohs(icp4->icmp_seq),
                 (unsigned long)(((vpsized_uint)self) & 0xffff));
        continue;
      }
    }
    else if(family == AF_INET6) {
      struct icmp6_hdr *icp6 = (struct icmp6_hdr *)packet;
      if((inlen) != sizeof(struct icmp6_hdr)+PING_PAYLOAD_LEN) {
        mtevLT(nldeb, now,
               "ping_icmp bad size: %d+%d\n", iphlen, inlen-iphlen); 
        continue;
      }
      payload = (struct ping_payload *)(icp6+1);
      if(icp6->icmp6_type != ICMP6_ECHO_REPLY) {
        mtevLT(nldeb, now, "ping_icmp bad type: %d\n", icp6->icmp6_type);
        continue;
      }
      if(icp6->icmp6_id != (((vpsized_uint)self) & 0xffff)) {
        mtevLT(nldeb, now,
                 "ping_icmp not sent from this instance (%d:%d) vs. %lu\n",
                 icp6->icmp6_id, ntohs(icp6->icmp6_seq),
                 (unsigned long)(((vpsized_uint)self) & 0xffff));
        continue;
      }
    }
    else {
      /* This should be unreachable */
      continue;
    }
    check = NULL;
    k.addr_of_check = payload->addr_of_check;
    uuid_copy(k.checkid, payload->checkid);
    if(mtev_hash_retrieve(ping_data->in_flight,
                          (const char *)&k, sizeof(k),
                          &vcheck))
      check = vcheck;

    /* make sure this check is from this generation! */
    if(!check) {
      char uuid_str[37];
      uuid_unparse_lower(payload->checkid, uuid_str);
      mtevLT(nldeb, now,
             "ping_icmp response for unknown check '%s'\n", uuid_str);
      continue;
    }
    if((check->generation & 0xffff) != payload->generation) {
      mtevLT(nldeb, now,
             "ping_icmp response in generation gap\n");
      continue;
    }
    data = (struct check_info *)check->closure;

    /* If there is no timeout_event, the check must have completed.
     * We have nothing to do. */
    if(!data->timeout_event) continue;

    /* Sanity check the payload */
    if(payload->check_no != data->check_no) continue;
    if(payload->check_pack_cnt != data->expected_count) continue;
    if(payload->check_pack_no >= data->expected_count) continue;

    whence.tv_sec = payload->tv_sec;
    whence.tv_usec = payload->tv_usec;
    sub_timeval(*now, whence, &tt);
    data->turnaround[payload->check_pack_no] =
      (float)tt.tv_sec + (float)tt.tv_usec / 1000000.0;
    if(ping_icmp_is_complete(self, check)) {
      ping_icmp_log_results(self, check);
      eventer_remove(data->timeout_event);
      free(data->timeout_event->closure);
      eventer_free(data->timeout_event);
      data->timeout_event = NULL;
      check->flags &= ~NP_RUNNING;
      k.addr_of_check = (vpsized_uint)check ^ random_num;
      uuid_copy(k.checkid, check->checkid);
      mtev_hash_delete(ping_data->in_flight, (const char *)&k,
                       sizeof(k), free, NULL);
    }
  }
  return EVENTER_READ;
}
Beispiel #16
0
static void eventer_epoll_impl_trigger(eventer_t e, int mask) {
  struct epoll_spec *spec;
  struct timeval __now;
  int fd, newmask;
  const char *cbname;
  ev_lock_state_t lockstate;
  int cross_thread = mask & EVENTER_CROSS_THREAD_TRIGGER;
  int added_to_master_fds = 0;
  u_int64_t start, duration;

  mask = mask & ~(EVENTER_RESERVED);
  fd = e->fd;
  if(cross_thread) {
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */
  }
  if(!pthread_equal(pthread_self(), e->thr_owner)) {
    /* If we're triggering across threads, it can't be registered yet */
    if(master_fds[fd].e != NULL) {
      mtevL(eventer_deb, "Attempting to trigger already-registered event fd: %d cross thread.\n", fd);
    }
    /* mtevAssert(master_fds[fd].e == NULL); */
    
    eventer_cross_thread_trigger(e,mask);
    return;
  }
  if(master_fds[fd].e == NULL) {
    master_fds[fd].e = e;
    e->mask = 0;
    added_to_master_fds = 1;
  }
  if(e != master_fds[fd].e) return;
  lockstate = acquire_master_fd(fd);
  if(lockstate == EV_ALREADY_OWNED) return;
  mtevAssert(lockstate == EV_OWNED);

  mtev_gettimeofday(&__now, NULL);
  cbname = eventer_name_for_callback_e(e->callback, e);
  mtevLT(eventer_deb, &__now, "epoll: fire on %d/%x to %s(%p)\n",
         fd, mask, cbname?cbname:"???", e->callback);
  mtev_memory_begin();
  LIBMTEV_EVENTER_CALLBACK_ENTRY((void *)e, (void *)e->callback, (char *)cbname, fd, e->mask, mask);
  start = mtev_gethrtime();
  newmask = e->callback(e, mask, e->closure, &__now);
  duration = mtev_gethrtime() - start;
  LIBMTEV_EVENTER_CALLBACK_RETURN((void *)e, (void *)e->callback, (char *)cbname, newmask);
  mtev_memory_end();
  stats_set_hist_intscale(eventer_callback_latency, duration, -9, 1);
  stats_set_hist_intscale(eventer_latency_handle_for_callback(e->callback), duration, -9, 1);

  if(newmask) {
    struct epoll_event _ev;
    memset(&_ev, 0, sizeof(_ev));
    _ev.data.fd = fd;
    if(newmask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI);
    if(newmask & EVENTER_WRITE) _ev.events |= (EPOLLOUT);
    if(newmask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP);
    if(master_fds[fd].e == NULL) {
      mtevL(mtev_debug, "eventer %s(%p) epoll asked to modify descheduled fd: %d\n",
            cbname?cbname:"???", e->callback, fd);
    } else {
      if(!pthread_equal(pthread_self(), e->thr_owner)) {
        pthread_t tgt = e->thr_owner;
        e->thr_owner = pthread_self();
        spec = eventer_get_spec_for_event(e);
        if(! added_to_master_fds && epoll_ctl(spec->epoll_fd, EPOLL_CTL_DEL, fd, &_ev) != 0) {
          mtevFatal(mtev_error,
                    "epoll_ctl(spec->epoll_fd, EPOLL_CTL_DEL, fd, &_ev) failed; "
                    "spec->epoll_fd: %d; fd: %d; errno: %d (%s)\n",
                    spec->epoll_fd, fd, errno, strerror(errno));
        }
        e->thr_owner = tgt;
        spec = eventer_get_spec_for_event(e);
        mtevAssert(epoll_ctl(spec->epoll_fd, EPOLL_CTL_ADD, fd, &_ev) == 0);
        mtevL(eventer_deb, "moved event[%p] from t@%d to t@%d\n", e, (int)pthread_self(), (int)tgt);
      }
      else {
        int epoll_cmd = added_to_master_fds ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;
        spec = eventer_get_spec_for_event(e);
        if(epoll_ctl(spec->epoll_fd, epoll_cmd, fd, &_ev) != 0) {
          const char *cb_name = eventer_name_for_callback_e(e->callback, e);
          mtevFatal(mtev_error,
                    "epoll_ctl(spec->epoll_fd, EPOLL_CTL_MOD, fd, &_ev) failed; "
              "spec->epoll_fd: %d; fd: %d; errno: %d (%s); callback: %s\n",
              spec->epoll_fd, fd, errno, strerror(errno), cb_name ? cb_name : "???");
        }
      }
    }
    /* Set our mask */
    e->mask = newmask;
  }
  else {
    /* see kqueue implementation for details on the next line */
    if(master_fds[fd].e == e) master_fds[fd].e = NULL;
    eventer_free(e);
  }
  release_master_fd(fd, lockstate);
}
static void
mtev_capabilities_tobuff_json(mtev_capsvc_closure_t *cl, eventer_func_t curr) {
    const char **mod_names;
    struct utsname utsn;
    char vbuff[128];
    mtev_hash_table *lc;
    mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
    int i, nmods;
    struct timeval now;
    struct dso_type *t;

    struct json_object *doc;
    struct json_object *svcs, *bi, *ri, *mods, *feat;

    /* fill out capabilities */

    /* Create an XML Document */
    doc = json_object_new_object();

    /* Fill in the document */
    mtev_build_version(vbuff, sizeof(vbuff));
    json_object_object_add(doc, "version", json_object_new_string(vbuff));

    /* Build info */
    bi = json_object_new_object();
    json_object_object_add(bi, "bitwidth", json_object_new_int(sizeof(void *)*8));
    json_object_object_add(bi, "sysname", json_object_new_string(UNAME_S));
    json_object_object_add(bi, "nodename", json_object_new_string(UNAME_N));
    json_object_object_add(bi, "release", json_object_new_string(UNAME_R));
    json_object_object_add(bi, "version", json_object_new_string(UNAME_V));
    json_object_object_add(bi, "machine", json_object_new_string(UNAME_M));
    json_object_object_add(doc, "unameBuild", bi);

    /* Run info */
    ri = json_object_new_object();
    json_object_object_add(ri, "bitwidth", json_object_new_int(sizeof(void *)*8));
    if(uname(&utsn) < 0) {
      json_object_object_add(ri, "error", json_object_new_string(strerror(errno)));
    } else {
      json_object_object_add(ri, "sysname", json_object_new_string(utsn.sysname));
      json_object_object_add(ri, "nodename", json_object_new_string(utsn.nodename));
      json_object_object_add(ri, "release", json_object_new_string(utsn.release));
      json_object_object_add(ri, "version", json_object_new_string(utsn.version));
      json_object_object_add(ri, "machine", json_object_new_string(utsn.machine));
    }
    json_object_object_add(doc, "unameRun", ri);

    /* features */
    feat = json_object_new_object();
    if(mtev_hash_size(&features)) {
      mtev_hash_iter iter2 = MTEV_HASH_ITER_ZERO;
      while(mtev_hash_adv(&features, &iter2)) {
        struct json_object *featnode;
        featnode = json_object_new_object();
        if(iter2.value.str) json_object_object_add(featnode, "version", json_object_new_string(iter2.value.str));
        json_object_object_add(feat, iter2.key.str, featnode);
      }
    }
    json_object_object_add(doc, "features", feat);

    /* time (poor man's time check) */
    mtev_gettimeofday(&now, NULL);
    snprintf(vbuff, sizeof(vbuff), "%llu%03d", (unsigned long long)now.tv_sec,
             (int)(now.tv_usec / 1000));
    json_object_object_add(doc, "current_time", json_object_new_string(vbuff));

    svcs = json_object_new_object();
    lc = mtev_listener_commands();
    while(mtev_hash_adv(lc, &iter)) {
      struct json_object *cnode, *cmds;
      char hexcode[11];
      const char *name;
      eventer_func_t *f = (eventer_func_t *)iter.key.ptr;
      mtev_hash_table *sc = (mtev_hash_table *)iter.value.ptr;
      mtev_hash_iter sc_iter = MTEV_HASH_ITER_ZERO;

      name = eventer_name_for_callback(*f);
      cnode = json_object_new_object();
      if(iter.klen == 8)
        snprintf(hexcode, sizeof(hexcode), "0x%0llx",
                 (unsigned long long int)(vpsized_uint)**f);
      else
        snprintf(hexcode, sizeof(hexcode), "0x%0x",
                 (unsigned int)(vpsized_uint)**f);
      json_object_object_add(svcs, hexcode, cnode);
      if(name) json_object_object_add(cnode, name, json_object_new_string(name));
      cmds = json_object_new_object();
      json_object_object_add(cnode, "commands", cmds);
      while(mtev_hash_adv(sc, &sc_iter)) {
        struct json_object *scnode;
        char *name_copy, *version = NULL;
        eventer_func_t *f = (eventer_func_t *)sc_iter.value.ptr;

        scnode = json_object_new_object();
        snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_iter.key.ptr));
        name = eventer_name_for_callback(*f);
        name_copy = strdup(name ? name : "[[unknown]]");
        version = strchr(name_copy, '/');
        if(version) *version++ = '\0';

        json_object_object_add(scnode, "name", json_object_new_string(name_copy));
        if(version) json_object_object_add(scnode, "version", json_object_new_string(version));
        json_object_object_add(cmds, hexcode, scnode);
        free(name_copy);
      }
    }
    json_object_object_add(doc, "services", svcs);

    mods = json_object_new_object();

#define list_modules_json(func, name) do { \
    nmods = func(&mod_names); \
    for(i=0; i<nmods; i++) { \
      struct json_object *pnode; \
      pnode = json_object_new_object(); \
      json_object_object_add(pnode, "type", json_object_new_string(name)); \
      json_object_object_add(mods, mod_names[i], pnode); \
    } \
    if(mod_names) free(mod_names); \
} while(0)
    for(t = mtev_dso_get_types(); t; t = t->next)
      list_modules_json(t->list, t->name);
    json_object_object_add(doc, "modules", mods);

    /* Write it out to a buffer and copy it for writing */
    cl->buff = strdup(json_object_to_json_string(doc));
    cl->towrite = strlen(cl->buff);

    /* Clean up after ourselves */
    json_object_put(doc);
}
static void
mtev_capabilities_tobuff(mtev_capsvc_closure_t *cl, eventer_func_t curr) {
    const char **mod_names;
    struct utsname utsn;
    char vbuff[128], bwstr[4];
    mtev_hash_table *lc;
    mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
    int i, nmods;
    struct timeval now;
    struct dso_type *t;

    xmlDocPtr xmldoc;
    xmlNodePtr root, cmds, bi, ri, mods, feat;

    /* fill out capabilities */

    /* Create an XML Document */
    xmldoc = xmlNewDoc((xmlChar *)"1.0");
    root = xmlNewDocNode(xmldoc, NULL, (xmlChar *)capabilities_namespace, NULL);
    xmlDocSetRootElement(xmldoc, root);

    /* Fill in the document */
    mtev_build_version(vbuff, sizeof(vbuff));
    xmlNewTextChild(root, NULL, (xmlChar *)"version", (xmlChar *)vbuff);

    snprintf(bwstr, sizeof(bwstr), "%d", (int)sizeof(void *)*8);
    /* Build info */
    bi = xmlNewNode(NULL, (xmlChar *)"unameBuild");
    xmlSetProp(bi, (xmlChar *)"bitwidth", (xmlChar *)bwstr);
    xmlAddChild(root, bi);
    xmlNewTextChild(bi, NULL, (xmlChar *)"sysname", (xmlChar *)UNAME_S);
    xmlNewTextChild(bi, NULL, (xmlChar *)"nodename", (xmlChar *)UNAME_N);
    xmlNewTextChild(bi, NULL, (xmlChar *)"release", (xmlChar *)UNAME_R);
    xmlNewTextChild(bi, NULL, (xmlChar *)"version", (xmlChar *)UNAME_V);
    xmlNewTextChild(bi, NULL, (xmlChar *)"machine", (xmlChar *)UNAME_M);

    /* Run info */
    ri = xmlNewNode(NULL, (xmlChar *)"unameRun");
    xmlSetProp(ri, (xmlChar *)"bitwidth", (xmlChar *)bwstr);
    xmlAddChild(root, ri);
    if(uname(&utsn) < 0) {
      xmlNewTextChild(ri, NULL, (xmlChar *)"error", (xmlChar *)strerror(errno));
    } else {
      xmlNewTextChild(ri, NULL, (xmlChar *)"sysname", (xmlChar *)utsn.sysname);
      xmlNewTextChild(ri, NULL, (xmlChar *)"nodename", (xmlChar *)utsn.nodename);
      xmlNewTextChild(ri, NULL, (xmlChar *)"release", (xmlChar *)utsn.release);
      xmlNewTextChild(ri, NULL, (xmlChar *)"version", (xmlChar *)utsn.version);
      xmlNewTextChild(ri, NULL, (xmlChar *)"machine", (xmlChar *)utsn.machine);
    }

    /* features */
    feat = xmlNewNode(NULL, (xmlChar *)"features");
    xmlAddChild(root, feat);
    if(mtev_hash_size(&features)) {
      mtev_hash_iter iter2 = MTEV_HASH_ITER_ZERO;
      while(mtev_hash_adv(&features, &iter2)) {
        xmlNodePtr featnode;
        featnode = xmlNewNode(NULL, (xmlChar *)"feature");
        xmlSetProp(featnode, (xmlChar *)"name", (xmlChar *)iter2.key.str);
        if(iter2.value.str)
          xmlSetProp(featnode, (xmlChar *)"version", (xmlChar *)iter2.value.str);
        xmlAddChild(feat, featnode);
      }
    }

    /* time (poor man's time check) */
    mtev_gettimeofday(&now, NULL);
    snprintf(vbuff, sizeof(vbuff), "%llu.%03d", (unsigned long long)now.tv_sec,
             (int)(now.tv_usec / 1000));
    xmlNewTextChild(root, NULL, (xmlChar *)"current_time", (xmlChar *)vbuff);

    cmds = xmlNewNode(NULL, (xmlChar *)"services");
    xmlAddChild(root, cmds);
    lc = mtev_listener_commands();
    while(mtev_hash_adv(lc, &iter)) {
      xmlNodePtr cnode;
      char hexcode[11];
      const char *name;
      eventer_func_t *f = (eventer_func_t *)iter.key.ptr;
      mtev_hash_table *sc = (mtev_hash_table *)iter.value.ptr;
      mtev_hash_iter sc_iter = MTEV_HASH_ITER_ZERO;

      name = eventer_name_for_callback(*f);
      cnode = xmlNewNode(NULL, (xmlChar *)"service");
      xmlSetProp(cnode, (xmlChar *)"name", name ? (xmlChar *)name : NULL);
      if(*f == curr)
        xmlSetProp(cnode, (xmlChar *)"connected", (xmlChar *)"true");
      xmlAddChild(cmds, cnode);
      while(mtev_hash_adv(sc, &sc_iter)) {
        xmlNodePtr scnode;
        char *name_copy, *version = NULL;
        eventer_func_t *f = (eventer_func_t *)sc_iter.value.ptr;

        snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_iter.key.ptr));
        name = eventer_name_for_callback(*f);
        name_copy = strdup(name ? name : "[[unknown]]");
        version = strchr(name_copy, '/');
        if(version) *version++ = '\0';

        scnode = xmlNewNode(NULL, (xmlChar *)"command");
        xmlSetProp(scnode, (xmlChar *)"name", (xmlChar *)name_copy);
        if(version)
          xmlSetProp(scnode, (xmlChar *)"version", (xmlChar *)version);
        xmlSetProp(scnode, (xmlChar *)"code", (xmlChar *)hexcode);
        xmlAddChild(cnode, scnode);
        free(name_copy);
      }
    }

    mods = xmlNewNode(NULL, (xmlChar *)"modules");
    xmlAddChild(root, mods);

#define list_modules(func, name) do { \
    nmods = func(&mod_names); \
    for(i=0; i<nmods; i++) { \
      xmlNodePtr pnode; \
      pnode = xmlNewNode(NULL, (xmlChar *)"module"); \
      xmlSetProp(pnode, (xmlChar *)"type", (xmlChar *)name); \
      xmlSetProp(pnode, (xmlChar *)"name", (xmlChar *)mod_names[i]); \
      xmlAddChild(mods, pnode); \
    } \
    if(mod_names) free(mod_names); \
} while(0)
    for(t = mtev_dso_get_types(); t; t = t->next)
      list_modules(t->list, t->name);

    /* Write it out to a buffer and copy it for writing */
    cl->buff = mtev_xmlSaveToBuffer(xmldoc);
    cl->towrite = strlen(cl->buff);

    /* Clean up after ourselves */
    xmlFreeDoc(xmldoc);
}
Beispiel #19
0
static int eventer_ports_impl_loop() {
    struct timeval __dyna_sleep = { 0, 0 };
    struct ports_spec *spec;
    spec = eventer_get_spec_for_event(NULL);

    while(1) {
        struct timeval __now, __sleeptime;
        struct timespec __ports_sleeptime;
        unsigned int fd_cnt = 0;
        int ret;
        port_event_t pevents[MAX_PORT_EVENTS];

        mtev_gettimeofday(&__now, NULL);

        if(compare_timeval(eventer_max_sleeptime, __dyna_sleep) < 0)
            __dyna_sleep = eventer_max_sleeptime;

        __sleeptime = __dyna_sleep;

        eventer_dispatch_timed(&__now, &__sleeptime);

        if(compare_timeval(__sleeptime, __dyna_sleep) > 0)
            __sleeptime = __dyna_sleep;

        /* Handle cross_thread dispatches */
        eventer_cross_thread_process();

        /* Handle recurrent events */
        eventer_dispatch_recurrent(&__now);

        /* Now we move on to our fd-based events */
        __ports_sleeptime.tv_sec = __sleeptime.tv_sec;
        __ports_sleeptime.tv_nsec = __sleeptime.tv_usec * 1000;
        fd_cnt = 1;

        pevents[0].portev_source = 65535; /* This is impossible */

        ret = port_getn(spec->port_fd, pevents, MAX_PORT_EVENTS, &fd_cnt,
                        &__ports_sleeptime);
        spec->wakeup_notify = 0; /* force unlock */
        /* The timeout case is a tad complex with ports.  -1/ETIME is clearly
         * a timeout.  However, it i spossible that we got that and fd_cnt isn't
         * 0, which means we both timed out and got events... WTF?
         */
        if(fd_cnt == 0 ||
                (ret == -1 && errno == ETIME && pevents[0].portev_source == 65535))
            add_timeval(__dyna_sleep, __dyna_increment, &__dyna_sleep);

        if(ret == -1 && (errno != ETIME && errno != EINTR))
            mtevLT(eventer_err, &__now, "port_getn: %s\n", strerror(errno));

        if(ret < 0)
            mtevLT(eventer_deb, &__now, "port_getn: %s\n", strerror(errno));

        mtevLT(eventer_deb, &__now, "debug: port_getn(%d, [], %d) => %d\n",
               spec->port_fd, fd_cnt, ret);

        if(pevents[0].portev_source == 65535) {
            /* the impossible still remains, which means our fd_cnt _must_ be 0 */
            fd_cnt = 0;
        }

        if(fd_cnt > 0) {
            int idx;
            /* Loop a last time to process */
            __dyna_sleep.tv_sec = __dyna_sleep.tv_usec = 0; /* reset */
            for(idx = 0; idx < fd_cnt; idx++) {
                port_event_t *pe;
                eventer_t e;
                int fd, mask;

                pe = &pevents[idx];
                if(pe->portev_source != PORT_SOURCE_FD) continue;
                fd = (int)pe->portev_object;
                mtevAssert((vpsized_int)pe->portev_user == fd);
                e = master_fds[fd].e;

                /* It's possible that someone removed the event and freed it
                 * before we got here.... bail out if we're null.
                 */
                if (!e) continue;

                mask = 0;
                if(pe->portev_events & (POLLIN | POLLHUP))
                    mask |= EVENTER_READ;
                if(pe->portev_events & (POLLOUT))
                    mask |= EVENTER_WRITE;
                if(pe->portev_events & (POLLERR | POLLHUP | POLLNVAL))
                    mask |= EVENTER_EXCEPTION;

                eventer_ports_impl_trigger(e, mask);
            }
        }
    }
    /* NOTREACHED */
    return 0;
}