void sm_resend(sm_t self, int fd) {
  sm_private_t my = self->private_state;
  sm_sendq_t sendq = ht_get_value(my->fd_to_sendq, HT_KEY(fd));
  while (sendq) {
    char *head = sendq->head;
    char *tail = sendq->tail;
    // send as much as we can without blocking
    sm_on_debug(self, "ss.sendq<%p> resume send to fd=%d len=%zd", sendq, fd,
        (tail - head));
    while (head < tail) {
      ssize_t sent_bytes = send(fd, (void*)head, (tail - head), 0);
      if (sent_bytes <= 0) {
        if (sent_bytes && errno != EWOULDBLOCK) {
          perror("sendq retry failed");
          self->remove_fd(self, fd);
          return;
        }
        break;
      }
      head += sent_bytes;
    }
    sendq->head = head;
    if (head < tail) {
      // still have stuff to send
      sm_on_debug(self, "ss.sendq<%p> defer len=%zd", sendq, (tail - head));
      break;
    }
    self->on_sent(self, fd, sendq->value, sendq->begin, tail - sendq->begin);
    sm_sendq_t nextq = sendq->next;
    ht_put(my->fd_to_sendq, HT_KEY(fd), nextq);
    if (!nextq) {
      FD_CLR(fd, my->send_fds);
    }
    int recv_fd = sendq->recv_fd;
    if (recv_fd && FD_ISSET(recv_fd, my->all_fds)) {
      // if no other sendq's match this blocked recv_fd, re-enable it
      bool found = false;
      if (ht_size(my->fd_to_sendq)) {
        sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq);
        sm_sendq_t *q;
        for (q = qs; *q && !found; q++) {
          sm_sendq_t sq;
          for (sq = *q; sq && !found; sq = sq->next) {
            found |= (sq->recv_fd == recv_fd);
          }
        }
        free(qs);
      }
      if (!found) {
        sm_on_debug(self, "ss.sendq<%p> re-enable recv_fd=%d", sendq, recv_fd);
        FD_SET(recv_fd, my->recv_fds);
        // don't FD_SET(tmp_recv_fds), since maybe there was no input
        // instead, let the next select loop pick it up
      }
    }
    sm_on_debug(self, "ss.sendq<%p> free, next=<%p>", sendq, nextq);
    sm_sendq_free(sendq);
    sendq = nextq;
  }
}
sm_status sm_send(sm_t self, int fd, const char *data, size_t length,
    void* value) {
  sm_private_t my = self->private_state;
  sm_sendq_t sendq = (sm_sendq_t)ht_get_value(my->fd_to_sendq, HT_KEY(fd));
  const char *head = data;
  const char *tail = data + length;
  if (!sendq) {
    // send as much as we can without blocking
    while (1) {
      ssize_t sent_bytes = send(fd, (void*)head, (tail - head), 0);
      if (sent_bytes <= 0) {
        if (sent_bytes && errno != EWOULDBLOCK) {
          sm_on_debug(self, "ss.failed fd=%d", fd);
          perror("send failed");
          return SM_ERROR;
        }
        break;
      }
      head += sent_bytes;
      if (head >= tail) {
        self->on_sent(self, fd, value, data, length);
        return SM_SUCCESS; // this is the typical case
      }
    }
  }
  // we can't send this now, so queue it
  int curr_recv_fd = my->curr_recv_fd;
  sm_sendq_t newq = sm_sendq_new(curr_recv_fd, value, head, tail - head);
  if (sendq) {
    while (sendq->next) {
      sendq = sendq->next;
    }
    sendq->next = newq;
  } else {
    ht_put(my->fd_to_sendq, HT_KEY(fd), newq);
    FD_SET(fd, my->send_fds);
  }
  sm_on_debug(self, "ss.sendq<%p> new fd=%d recv_fd=%d length=%zd"
      ", prev=<%p>", newq, fd, curr_recv_fd, tail - head, sendq);
  if (curr_recv_fd && FD_ISSET(curr_recv_fd, my->recv_fds)) {
    // block the current recv_fd, to prevent our sendq from growing too large.
    // At worst our recv_fds are all trying to send to the same fd, in which
    // case we'll eventually block all of them until the first blocked send
    // succeeds.
    sm_on_debug(self, "ss.sendq<%p> disable recv_fd=%d", newq, curr_recv_fd);
    FD_CLR(curr_recv_fd, my->recv_fds);
    FD_CLR(curr_recv_fd, my->tmp_recv_fds);
  }
  return SM_SUCCESS;
}
sm_status sm_add_fd(sm_t self, int fd, void *value, bool is_server) {
  sm_private_t my = self->private_state;
  if (FD_ISSET(fd, my->all_fds)) {
    return SM_ERROR;
  }
  if (ht_put(my->fd_to_value, HT_KEY(fd), value)) {
    // The above FD_ISSET(..master..) should prevent this
    return SM_ERROR;
  }
  // is_server == getsockopt(..., SO_ACCEPTCONN, ...)?
  sm_on_debug(self, "ss.add%s_fd(%d)", (is_server ? "_server" : ""), fd);
  FD_SET(fd, my->all_fds);
  FD_CLR(fd, my->send_fds); // only set if blocked
  FD_SET(fd, my->recv_fds);
  FD_CLR(fd, my->tmp_send_fds);
  FD_CLR(fd, my->tmp_recv_fds);
  FD_CLR(fd, my->tmp_fail_fds);
  if (is_server) {
    FD_SET(fd, my->server_fds);
  }
  if (fd > my->max_fd) {
    my->max_fd = fd;
  }
  return SM_SUCCESS;
}
sm_status sm_remove_fd(sm_t self, int fd) {
  sm_private_t my = self->private_state;
  if (!FD_ISSET(fd, my->all_fds)) {
    return SM_ERROR;
  }
  void *value = ht_put(my->fd_to_value, HT_KEY(fd), NULL);
  bool is_server = FD_ISSET(fd, my->server_fds);
  sm_on_debug(self, "ss.remove%s_fd(%d)", (is_server ? "_server" : ""), fd);
  sm_status ret = self->on_close(self, fd, value, is_server);
  close(fd);
  FD_CLR(fd, my->all_fds);
  if (is_server) {
    FD_CLR(fd, my->server_fds);
  }
  FD_CLR(fd, my->send_fds);
  FD_CLR(fd, my->recv_fds);
  FD_CLR(fd, my->tmp_send_fds);
  FD_CLR(fd, my->tmp_recv_fds);
  FD_CLR(fd, my->tmp_fail_fds);
  if (fd == my->max_fd) {
    while (my->max_fd >= 0 && !FD_ISSET(my->max_fd, my->all_fds)) {
      my->max_fd--;
    }
  }
  if (ht_size(my->fd_to_sendq)) {
    sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq);
    sm_sendq_t *q;
    for (q = qs; *q; q++) {
      sm_sendq_t sendq = *q;
      while (sendq) {
        if (sendq->recv_fd == fd) {
          sendq->recv_fd = 0;
          // don't abort this blocked send, even though the "cause" has ended
        }
        sendq = sendq->next;
      }
    }
    free(qs);
  }
  return ret;
}
void sm_recv(sm_t self, int fd) {
  sm_private_t my = self->private_state;
  my->curr_recv_fd = fd;
  while (1) {
    ssize_t read_bytes = recv(fd, my->tmp_buf, my->tmp_buf_length, RECV_FLAGS);
    if (read_bytes < 0) {
      if (errno != EWOULDBLOCK) {
        perror("recv failed");
        self->remove_fd(self, fd);
      }
      break;
    }
    sm_on_debug(self, "ss.recv fd=%d len=%zd", fd, read_bytes);
    void *value = ht_get_value(my->fd_to_value, HT_KEY(fd));
    if (read_bytes == 0 ||
        self->on_recv(self, fd, value, my->tmp_buf, read_bytes)) {
      self->remove_fd(self, fd);
      break;
    }
  }
  my->curr_recv_fd = 0;
}
void sm_accept(sm_t self, int fd) {
  sm_private_t my = self->private_state;
  while (1) {
    int new_fd = accept(fd, NULL, NULL);
    if (new_fd < 0) {
      if (errno != EWOULDBLOCK) {
        perror("accept failed");
        self->remove_fd(self, fd);
        return;
      }
      break;
    }
    sm_on_debug(self, "ss.accept server=%d new_client=%d",
        fd, new_fd);
    void *value = ht_get_value(my->fd_to_value, HT_KEY(fd));
    void *new_value = NULL;
    if (self->on_accept(self, fd, value, new_fd, &new_value)) {
      close(new_fd);
    } else if (self->add_fd(self, new_fd, new_value, false)) {
      self->on_close(self, new_fd, new_value, false);
      close(new_fd);
    }
  }
}
dl_status dl_recv_packet(dl_t self, const char *packet, size_t length) {
  dl_private_t my = self->private_state;

  const char *tail = packet;
  uint32_t len = dl_sscanf_uint32(tail);
  tail += 4;
  if (len != length || len < 16) {
    return DL_ERROR;
  }
  uint32_t version = dl_sscanf_uint32(tail);
  tail += 4;
  uint32_t type = dl_sscanf_uint32(tail);
  tail += 4;
  (void)dl_sscanf_uint32(tail);
  tail += 4;
  const char *xml = tail;
  size_t xml_length = length - 16;

  if (version != 1 || type != TYPE_PLIST) {
    return DL_SUCCESS; // ignore?
  }

  plist_t dict = NULL;
  plist_from_xml(xml, xml_length, &dict);
  char *message = NULL;
  if (dict) {
    plist_t node = plist_dict_get_item(dict, "MessageType");
    if (plist_get_node_type(node) == PLIST_STRING) {
      plist_get_string_val(node, &message);
    }
  }

  dl_status ret = DL_ERROR;
  if (!message) {
    ret = DL_ERROR;
  } else if (!strcmp(message, "Result")) {
    plist_t node = plist_dict_get_item(dict, "Number");
    if (node) {
      uint64_t value = 0;
      plist_get_uint_val(node, &value);
      // just an ack of our Listen?
      ret = (value ? DL_ERROR : DL_SUCCESS);
    }
  } else if (!strcmp(message, "Attached")) {
    plist_t props = plist_dict_get_item(dict, "Properties");
    if (props) {
      uint64_t device_num = 0;
      plist_t node = plist_dict_get_item(props, "DeviceID");
      plist_get_uint_val(node, &device_num);

      uint64_t product_id = 0;
      node = plist_dict_get_item(props, "ProductID");
      plist_get_uint_val(node, &product_id);

      char *device_id = NULL;
      node = plist_dict_get_item(props, "SerialNumber");
      if (node) {
        plist_get_string_val(node, &device_id);
      }

      uint64_t location = 0;
      node = plist_dict_get_item(props, "LocationID");
      plist_get_uint_val(node, &location);

      ht_t d_ht = my->device_num_to_device_id;
      ht_put(d_ht, HT_KEY(device_num), device_id);
      ret = self->on_attach(self, device_id, (int)device_num);
    }
  } else if (strcmp(message, "Detached") == 0) {
    plist_t node = plist_dict_get_item(dict, "DeviceID");
    if (node) {
      uint64_t device_num = 0;
      plist_get_uint_val(node, &device_num);

      ht_t d_ht = my->device_num_to_device_id;
      char *device_id = (char *)ht_remove(d_ht, HT_KEY(device_num));
      if (device_id) {
        ret = self->on_detach(self, device_id, (int)device_num);
        free(device_id);
      }
    }
  }
  free(message);
  plist_free(dict);
  return ret;
}