예제 #1
0
파일: main.c 프로젝트: cobookman/mongoose
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("%p conn from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) ev_data;
      cs_stat_t st;
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      if (mg_vcmp(&hm->uri, "/upload") == 0 ||
          (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) {
        mg_send(nc, upload_form, strlen(upload_form));
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, hm, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      break;
    }
    case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
      LOG(LL_INFO, ("%p switching to data mode", nc));
      nc->handler = data_conn_handler;
      nc->ev_timer_time = mg_time(); /* Immediately */
      break;
    }
    case MG_EV_TIMER: {
      data_collect();
      nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001);
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      struct mg_http_multipart_part *mp =
          (struct mg_http_multipart_part *) ev_data;
      if (ev == MG_EV_HTTP_PART_BEGIN) {
        LOG(LL_INFO, ("Begin file upload: %s", mp->file_name));
      } else if (ev == MG_EV_HTTP_PART_END) {
        LOG(LL_INFO, ("End file upload: %s", mp->file_name));
      }
      mg_file_upload_handler(nc, ev, ev_data, upload_fname);
    }
  }
}
예제 #2
0
/*
 * Creates a new session for the user.
 */
static struct session *create_session(const char *user,
                                      const struct http_message *hm) {
  /* Find first available slot or use the oldest one. */
  struct session *s = NULL;
  struct session *oldest_s = s_sessions;
  for (int i = 0; i < NUM_SESSIONS; i++) {
    if (s_sessions[i].id == 0) {
      s = &s_sessions[i];
      break;
    }
    if (s_sessions[i].last_used < oldest_s->last_used) {
      oldest_s = &s_sessions[i];
    }
  }
  if (s == NULL) {
    destroy_session(oldest_s);
    printf("Evicted %" INT64_X_FMT "/%s\n", oldest_s->id, oldest_s->user);
    s = oldest_s;
  }
  /* Initialize new session. */
  s->created = s->last_used = mg_time();
  s->user = strdup(user);
  s->lucky_number = rand();
  /* Create an ID by putting various volatiles into a pot and stirring. */
  cs_sha1_ctx ctx;
  cs_sha1_init(&ctx);
  cs_sha1_update(&ctx, (const unsigned char *) hm->message.p, hm->message.len);
  cs_sha1_update(&ctx, (const unsigned char *) s, sizeof(*s));
  unsigned char digest[20];
  cs_sha1_final(digest, &ctx);
  s->id = *((uint64_t *) digest);
  return s;
}
예제 #3
0
static IRAM void mgos_mg_poll_cb(void *arg) {
  mgos_ints_disable();
  s_mg_polls_in_flight--;
  mgos_ints_enable();
  int timeout_ms = 0;
  if (mongoose_poll(0) == 0) {
    /* Nothing is happening now, see when next timer is due. */
    double min_timer = mg_mgr_min_timer(mgos_get_mgr());
    if (min_timer > 0) {
      /* Note: timeout_ms can get negative if a timer is past due. That's ok. */
      timeout_ms = (int) ((min_timer - mg_time()) * 1000.0);
      if (timeout_ms < 0) {
        timeout_ms = 0; /* Now */
      } else if (timeout_ms > MGOS_MONGOOSE_MAX_POLL_SLEEP_MS) {
        timeout_ms = MGOS_MONGOOSE_MAX_POLL_SLEEP_MS;
      }
    } else {
      timeout_ms = MGOS_MONGOOSE_MAX_POLL_SLEEP_MS;
    }
  } else {
    /* Things are happening, we need another poll ASAP. */
  }
  if (timeout_ms == 0) {
    mongoose_schedule_poll(false /* from_isr */);
  } else {
    os_timer_disarm(&s_mg_poll_tmr);
    /* We set repeat = true in case things get stuck for any reason. */
    os_timer_arm(&s_mg_poll_tmr, timeout_ms, 1 /* repeat */);
  }
  (void) arg;
}
예제 #4
0
파일: main.c 프로젝트: gavinying/mongoose
static void mg_init(struct mg_mgr *mgr) {
  LOG(LL_INFO, ("MG task running"));

  stop_nwp(); /* See function description in wifi.c */
  LOG(LL_INFO, ("Starting NWP..."));
  int role = sl_Start(0, 0, 0);
  if (role < 0) {
    LOG(LL_ERROR, ("Failed to start NWP"));
    return;
  }

  {
    SlVersionFull ver;
    unsigned char opt = SL_DEVICE_GENERAL_VERSION;
    unsigned char len = sizeof(ver);

    memset(&ver, 0, sizeof(ver));
    sl_DevGet(SL_DEVICE_GENERAL_CONFIGURATION, &opt, &len,
              (unsigned char *) (&ver));
    LOG(LL_INFO, ("NWP v%d.%d.%d.%d started, host v%d.%d.%d.%d",
                  ver.NwpVersion[0], ver.NwpVersion[1], ver.NwpVersion[2],
                  ver.NwpVersion[3], SL_MAJOR_VERSION_NUM, SL_MINOR_VERSION_NUM,
                  SL_VERSION_NUM, SL_SUB_VERSION_NUM));
  }

  GPIO_IF_LedToggle(MCU_RED_LED_GPIO);

  data_init_sensors(TMP006_ADDR, BM222_ADDR);

  sl_fs_init();

#if defined(WIFI_STA_SSID)
  if (!wifi_setup_sta(WIFI_STA_SSID, WIFI_STA_PASS)) {
    LOG(LL_ERROR, ("Error setting up WiFi station"));
  }
#elif defined(WIFI_AP_SSID)
  if (!wifi_setup_ap(WIFI_AP_SSID, WIFI_AP_PASS, WIFI_AP_CHAN)) {
    LOG(LL_ERROR, ("Error setting up WiFi AP"));
  }
#else
#error WiFi not configured
#endif

  /* We don't need SimpleLink's web server. */
  sl_NetAppStop(SL_NET_APP_HTTP_SERVER_ID);

  const char *err = "";
  struct mg_bind_opts opts;
  memset(&opts, 0, sizeof(opts));
  opts.error_string = &err;

  struct mg_connection *nc = mg_bind_opt(mgr, "80", mg_ev_handler, opts);
  if (nc != NULL) {
    mg_set_protocol_http_websocket(nc);
    nc->ev_timer_time = mg_time(); /* Start data collection */
  } else {
    LOG(LL_ERROR, ("Failed to create listener: %s", err));
  }
}
예제 #5
0
파일: main.c 프로젝트: 12019/mongoose
static void mg_ev_handler(struct mg_connection *nc, int ev, void *p) {
  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("%p conn from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) p;
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, (struct http_message *) p, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      break;
    }
    case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
      LOG(LL_INFO, ("%p switching to data mode", nc));
      nc->handler = data_conn_handler;
      nc->ev_timer_time = mg_time(); /* Immediately */
      break;
    }
    case MG_EV_TIMER: {
      data_collect();
      nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001);
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      mg_file_upload_handler(nc, ev, p, upload_fname);
    }
  }
}
예제 #6
0
파일: data.c 프로젝트: cesanta/mongoose
void data_collect(void) {
  double volt = tmp006_read_sensor_voltage(s_tmp006_addr);
  double temp = tmp006_read_die_temp(s_tmp006_addr);
  if (volt != TMP006_INVALID_READING && temp != TMP006_INVALID_READING) {
    s_temp_data.temp = temp;
    s_temp_data.volt = volt;
    s_temp_data.ts = mg_time();
    LOG(LL_DEBUG, ("V = %lf mV, T = %lf C", volt, temp));
  }
  bm222_get_data(s_accel_ctx);
}
예제 #7
0
/* Cleans up sessions that have been idle for too long. */
void check_sessions() {
  double threshold = mg_time() - SESSION_TTL;
  for (int i = 0; i < NUM_SESSIONS; i++) {
    struct session *s = &s_sessions[i];
    if (s->id != 0 && s->last_used < threshold) {
      fprintf(stderr, "Session %" INT64_X_FMT " (%s) closed due to idleness.\n",
              s->id, s->user);
      destroy_session(s);
    }
  }
}
예제 #8
0
int main(void) {
  struct mg_mgr mgr;
  struct mg_connection *nc;
  srand(mg_time());

  mg_mgr_init(&mgr, NULL);
  nc = mg_bind(&mgr, s_http_port, ev_handler);

  mg_set_protocol_http_websocket(nc);
  s_http_server_opts.document_root = ".";
  mg_register_http_endpoint(nc, "/login.html", login_handler);
  mg_register_http_endpoint(nc, "/logout", logout_handler);
  mg_set_timer(nc, mg_time() + SESSION_CHECK_INTERVAL);

  printf("Starting web server on port %s\n", s_http_port);
  for (;;) {
    mg_mgr_poll(&mgr, 1000);
  }
  mg_mgr_free(&mgr);

  return 0;
}
예제 #9
0
bool RestServ::check_sessions()
{
    auto threshold = mg_time() - SESSION_TTL;

    for (auto iter = session_list_.begin(); iter != session_list_.end(); ++iter)
    {
        if ( (*iter)->last_used < threshold )
        {
            iter = session_list_.erase(iter);
        }
    }
    return true;
}
예제 #10
0
파일: data.c 프로젝트: cesanta/mongoose
void data_conn_handler(struct mg_connection *nc, int ev, void *ev_data) {
  struct conn_state *cs = (struct conn_state *) nc->user_data;
  if (cs == NULL) {
    cs = (struct conn_state *) calloc(1, sizeof(*cs));
    nc->user_data = cs;
  }
  switch (ev) {
    case MG_EV_POLL:
    case MG_EV_TIMER: {
      const double now = mg_time();
      const unsigned char led = GPIO_IF_LedStatus(MCU_RED_LED_GPIO);
      /* Send if there was a change or repeat last data every second. */
      if (s_temp_data.volt != cs->td.volt || s_temp_data.temp != cs->td.temp ||
          now > cs->last_sent_td + 1.0) {
        memcpy(&cs->td, &s_temp_data, sizeof(cs->td));
        send_temp(nc, &cs->td);
        cs->last_sent_td = now;
      }
      if (led != cs->led || now > cs->last_sent_led + 1.0) {
        send_led(nc, now, led);
        cs->led = led;
        cs->last_sent_led = now;
      }
      if (s_accel_ctx != NULL) {
        const struct bm222_sample *ls =
            s_accel_ctx->data + s_accel_ctx->last_index;
        if (cs->last_sent_acc == 0) {
          send_acc_sample(nc, ls);
          cs->last_sent_acc = ls->ts;
        } else {
          double last_sent_ts =
              send_acc_data_since(nc, s_accel_ctx, cs->last_sent_acc);
          if (last_sent_ts > 0) cs->last_sent_acc = last_sent_ts;
        }
      }
      nc->ev_timer_time = now + 0.05;
      break;
    }
    case MG_EV_WEBSOCKET_FRAME: {
      struct websocket_message *wm = (struct websocket_message *) ev_data;
      process_command(nc, wm->data, wm->size);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      free(cs);
      break;
    }
  }
}
예제 #11
0
파일: bm222.c 프로젝트: daniper/mongoose
bool bm222_get_data(struct bm222_ctx *ctx) {
  unsigned char reg = BM222_REG_FIFO_STATUS;
  unsigned char val;
  if (I2C_IF_ReadFrom(ctx->addr, &reg, 1, &val, 1) < 0) return false;
  unsigned char len = (val & 0x7f);
  unsigned char overflow = (val & 0x80 ? 1 : 0);
  LOG(LL_DEBUG, ("FIFO len: %d (ovf? %d)", len, overflow));
  reg = BM222_REG_FIFO_DATA;
  int8_t fifo[32 * 6], *v = fifo;
  if (I2C_IF_ReadFrom(ctx->addr, &reg, 1, (unsigned char *) fifo, len * 6) <
      0) {
    return false;
  }
  double now = mg_time();
  /* Of potentially multiple samples, pick one with maximum difference. */
  int32_t max_d = 0;
  bool sampled = false;
  struct bm222_sample *ls = ctx->data + ctx->last_index, *s = NULL;
  if (ls->ts == 0) {
    /* The very first sample. */
    ls->ts = now;
    ls->x = v[1];
    ls->y = v[3];
    ls->z = v[5];
    v += 6;
    len--;
    sampled = true;
    s = ls;
  }
  for (; len > 0; v += 6, len--) {
    int32_t dx = ((int32_t) v[1]) - ((int32_t) ls->x);
    int32_t dy = ((int32_t) v[3]) - ((int32_t) ls->y);
    int32_t dz = ((int32_t) v[5]) - ((int32_t) ls->z);
    int32_t d = dx * dx + dy * dy + dz * dz;
    if ((d > 2 && d > max_d) || (!sampled && now - ls->ts > 1.0)) {
      if (!sampled) {
        ctx->last_index = (ctx->last_index + 1) % BM222_NUM_SAMPLES;
        s = ctx->data + ctx->last_index;
        sampled = true;
      }
      s->ts = now;
      s->x = v[1];
      s->y = v[3];
      s->z = v[5];
      if (d > max_d) max_d = d;
      LOG(LL_VERBOSE_DEBUG, ("dx %d dy %d dz %d d %d", dx, dy, dz, d));
    }
  }
  return (overflow ? bm222_fifo_init(ctx) : true); /* Clear the ovf flag. */
}
static s32_t failfs_write(spiffs *fs, u32_t addr, u32_t size, u8_t *src) {
  struct mount_info *m = (struct mount_info *) fs->user_data;
  _i32 r;
  DBG(("failfs_write %d @ %d, cidx %d # %llu, fh %d, valid %d, rw %d",
       (int) size, (int) addr, m->cidx, m->seq, (int) m->fh, m->valid, m->rw));
  if (!m->valid) return SPIFFS_ERR_NOT_WRITABLE;
  if (!m->rw) {
    /* Remount rw. */
    if (fs_switch_container(m, 0, 0) != 0) return SPIFFS_ERR_NOT_WRITABLE;
  }
  r = sl_FsWrite(m->fh, addr, src, size);
  DBG(("write %d", (int) r));
  m->last_write = mg_time();
  return (r == size) ? SPIFFS_OK : SPIFFS_ERR_NOT_WRITABLE;
}
예제 #13
0
파일: main.c 프로젝트: 12019/mongoose
static void mg_task(void *arg) {
  LOG(LL_INFO, ("MG task running"));
  GPIO_IF_LedToggle(MCU_RED_LED_GPIO);

  osi_MsgQCreate(&s_v7_q, "MG", sizeof(struct event), 32 /* len */);

  sl_Start(NULL, NULL, NULL);

  data_init_sensors(TMP006_ADDR, BM222_ADDR);

  cc3200_fs_init();

#if defined(WIFI_STA_SSID)
  if (!wifi_setup_sta(WIFI_STA_SSID, WIFI_STA_PASS)) {
    LOG(LL_ERROR, ("Error setting up WiFi station"));
  }
#elif defined(WIFI_AP_SSID)
  if (!wifi_setup_ap(WIFI_AP_SSID, WIFI_AP_PASS, WIFI_AP_CHAN)) {
    LOG(LL_ERROR, ("Error setting up WiFi AP"));
  }
#else
#error WiFi not configured
#endif

  /* We don't need SimpleLink's web server. */
  sl_NetAppStop(SL_NET_APP_HTTP_SERVER_ID);

  mg_mgr_init(&mg_mgr, NULL);

  const char *err = "";
  struct mg_bind_opts opts;
  memset(&opts, 0, sizeof(opts));
  opts.error_string = &err;

  struct mg_connection *nc = mg_bind(&mg_mgr, "80", mg_ev_handler);
  if (nc != NULL) {
    mg_set_protocol_http_websocket(nc);
    nc->ev_timer_time = mg_time(); /* Start data collection */
  } else {
    LOG(LL_ERROR, ("Failed to create listener: %s", err));
  }

  while (1) {
    struct event e;
    mg_mgr_poll(&mg_mgr, 0);
    if (osi_MsgQRead(&s_v7_q, &e, 1) != OSI_OK) continue;
  }
}
예제 #14
0
파일: mg_util.c 프로젝트: cesanta/mongoose
void mg_hexdump_connection(struct mg_connection *nc, const char *path,
                           const void *buf, int num_bytes, int ev) {
  FILE *fp = NULL;
  char src[60], dst[60];
  const char *tag = NULL;
  switch (ev) {
    case MG_EV_RECV:
      tag = "<-";
      break;
    case MG_EV_SEND:
      tag = "->";
      break;
    case MG_EV_ACCEPT:
      tag = "<A";
      break;
    case MG_EV_CONNECT:
      tag = "C>";
      break;
    case MG_EV_CLOSE:
      tag = "XX";
      break;
  }
  if (tag == NULL) return; /* Don't log MG_EV_TIMER, etc */

  if (strcmp(path, "-") == 0) {
    fp = stdout;
  } else if (strcmp(path, "--") == 0) {
    fp = stderr;
#if MG_ENABLE_FILESYSTEM
  } else {
    fp = mg_fopen(path, "a");
#endif
  }
  if (fp == NULL) return;

  mg_conn_addr_to_str(nc, src, sizeof(src),
                      MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
  mg_conn_addr_to_str(nc, dst, sizeof(dst), MG_SOCK_STRINGIFY_IP |
                                                MG_SOCK_STRINGIFY_PORT |
                                                MG_SOCK_STRINGIFY_REMOTE);
  fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc,
          src, tag, dst, (int) num_bytes);
  if (num_bytes > 0) {
    mg_hexdumpf(fp, buf, num_bytes);
  }
  if (fp != stdout && fp != stderr) fclose(fp);
}
예제 #15
0
/*
 * Parses the session cookie and returns a pointer to the session struct
 * or NULL if not found.
 */
static struct session *get_session(struct http_message *hm) {
  struct mg_str *cookie_header = mg_get_http_header(hm, "cookie");
  if (cookie_header == NULL) return NULL;
  char ssid[21];
  if (!mg_http_parse_header(cookie_header, SESSION_COOKIE_NAME, ssid,
                            sizeof(ssid))) {
    return NULL;
  }
  uint64_t sid = strtoull(ssid, NULL, 16);
  for (int i = 0; i < NUM_SESSIONS; i++) {
    if (s_sessions[i].id == sid) {
      s_sessions[i].last_used = mg_time();
      return &s_sessions[i];
    }
  }
  return NULL;
}
예제 #16
0
sj_timer_id sj_set_timer(struct timer_info *ti, int msecs, int repeat) {
  struct mg_connection *c;
  struct mg_add_sock_opts opts;
  do {
    ti->id = s_next_timer_id++;
  } while (ti->id == SJ_INVALID_TIMER_ID || sj_find_timer(ti->id) != NULL);
  ti->interval_ms = (repeat ? msecs : -1);
  memset(&opts, 0, sizeof(opts));
  opts.user_data = ti;
  c = mg_add_sock_opt(&sj_mgr, INVALID_SOCKET, sj_timer_handler, opts);
  if (c == NULL) {
    free(ti);
    return 0;
  }
  c->ev_timer_time = mg_time() + (msecs / 1000.0);
  mongoose_schedule_poll();
  return 1;
}
예제 #17
0
/* Main event handler. */
static void ev_handler(struct mg_connection *nc, int ev, void *p) {
  switch (ev) {
    case MG_EV_HTTP_REQUEST: {
      struct http_message *hm = (struct http_message *) p;
      struct session *s = get_session(hm);
      /* Ask the user to log in if they did not present a valid cookie. */
      if (s == NULL) {
        mg_printf(nc,
                  "HTTP/1.0 302 Found\r\n"
                  "Location: /login.html\r\n"
                  "\r\n"
                  "Please log in");
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      /*
       * Serve the page that was requested.
       * Save session in user_data for use by SSI calls.
       */
      fprintf(stderr, "%s (sid %" INT64_X_FMT ") requested %.*s\n", s->user,
              s->id, (int) hm->uri.len, hm->uri.p);
      nc->user_data = s;
      mg_serve_http(nc, (struct http_message *) p, s_http_server_opts);
      break;
    }
    case MG_EV_SSI_CALL: {
      /* Expand variables in a page by using session data. */
      const char *var = (const char *) p;
      const struct session *s = (const struct session *) nc->user_data;
      if (strcmp(var, "user") == 0) {
        mg_printf_html_escape(nc, "%s", s->user);
      } else if (strcmp(var, "lucky_number") == 0) {
        mg_printf_html_escape(nc, "%d", s->lucky_number);
      }
      break;
    }
    case MG_EV_TIMER: {
      /* Perform session maintenance. */
      check_sessions();
      mg_set_timer(nc, mg_time() + SESSION_CHECK_INTERVAL);
      break;
    }
  }
}
예제 #18
0
파일: main.c 프로젝트: cobookman/mongoose
static void mg_init(struct mg_mgr *mgr) {
  LOG(LL_INFO, ("MG task running"));

  stop_nwp(); /* See function description in wifi.c */
  int role = sl_Start(0, 0, 0);
  if (role < 0) {
    LOG(LL_ERROR, ("Failed to start NWP"));
    return;
  }
  LOG(LL_INFO, ("NWP started"));
  GPIO_IF_LedToggle(MCU_RED_LED_GPIO);

  data_init_sensors(TMP006_ADDR, BM222_ADDR);

  sl_fs_init();

#if defined(WIFI_STA_SSID)
  if (!wifi_setup_sta(WIFI_STA_SSID, WIFI_STA_PASS)) {
    LOG(LL_ERROR, ("Error setting up WiFi station"));
  }
#elif defined(WIFI_AP_SSID)
  if (!wifi_setup_ap(WIFI_AP_SSID, WIFI_AP_PASS, WIFI_AP_CHAN)) {
    LOG(LL_ERROR, ("Error setting up WiFi AP"));
  }
#else
#error WiFi not configured
#endif

  /* We don't need SimpleLink's web server. */
  sl_NetAppStop(SL_NET_APP_HTTP_SERVER_ID);

  const char *err = "";
  struct mg_bind_opts opts;
  memset(&opts, 0, sizeof(opts));
  opts.error_string = &err;

  struct mg_connection *nc = mg_bind(mgr, "80", mg_ev_handler);
  if (nc != NULL) {
    mg_set_protocol_http_websocket(nc);
    nc->ev_timer_time = mg_time(); /* Start data collection */
  } else {
    LOG(LL_ERROR, ("Failed to create listener: %s", err));
  }
}
예제 #19
0
uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
  struct mg_connection *nc;
  double now;
  double min_timer = 0;
  int num_timers = 0;
  mg_ev_mgr_lwip_process_signals(mgr);
  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }
    if (nc->send_mbuf.len > 0
#if MG_ENABLE_SSL
        || (nc->flags & MG_F_WANT_WRITE)
#endif
            ) {
      int can_send = 0;
      /* We have stuff to send, but can we? */
      if (nc->flags & MG_F_UDP) {
        /* UDP is always ready for sending. */
        can_send = (cs->pcb.udp != NULL);
      } else {
        can_send = (cs->pcb.tcp != NULL && cs->pcb.tcp->snd_buf > 0);
      }
      /* We want and can send, request a poll immediately. */
      if (can_send) return 0;
    }
  }
  uint32_t timeout_ms = ~0;
  now = mg_time();
  if (num_timers > 0) {
    /* If we have a timer that is past due, do a poll ASAP. */
    if (min_timer < now) return 0;
    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
    if (timer_timeout_ms < timeout_ms) {
      timeout_ms = timer_timeout_ms;
    }
  }
  return timeout_ms;
}
예제 #20
0
std::shared_ptr<Session> RestServ::push_session(HttpMessage data)
{
    char user[64]{0x00}, pass[64]{0x00};
    auto ul = mg_get_http_var(&(data.get()->body), "user", user, sizeof(user));
    auto pl = mg_get_http_var(&(data.get()->body), "pass", pass, sizeof(pass));

    auto s = std::make_shared<Session>();

    s->created = s->last_used = mg_time();
    s->user = std::string(user, ul);
    s->pass = std::string(pass, pl);

    s->id = std::hash<std::shared_ptr<Session>>()(s);
    std::string&& seed = std::string(user) + std::to_string(s->id);
    s->id = std::hash<std::string>()(seed);

    session_list_.push_back(s);

    return s;
}
예제 #21
0
std::shared_ptr<Session> RestServ::get_from_session_list(HttpMessage data)
{
    mg_str* cookie_header = mg_get_http_header(data.get(), "cookie");;
    if (cookie_header == nullptr) 
        return nullptr;

    char ssid[32]{0x00};
    if (!mg_http_parse_header(cookie_header, SESSION_COOKIE_NAME, ssid, sizeof(ssid)))
        return nullptr;

    auto sid = std::stoul(ssid, nullptr, 10);

    auto ret = std::find_if(session_list_.begin(), session_list_.end(), [&sid](std::shared_ptr<Session> p){
            return sid == p->id;
            });

    if (ret == session_list_.end())
        return nullptr;

    (*ret)->last_used = mg_time();
    return *ret;
}
예제 #22
0
uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
  struct mg_connection *nc;
  double now = mg_time();
  double min_timer = 0;
  int num_timers = 0;
  mg_ev_mgr_lwip_process_signals(mgr);
  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }
  }
  uint32_t timeout_ms = ~0;
  if (num_timers > 0) {
    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
    if (timer_timeout_ms < timeout_ms) {
      timeout_ms = timer_timeout_ms;
    }
  }
  return timeout_ms;
}
예제 #23
0
static void mg_rpc_channel_ws_out_reconnect(struct mg_rpc_channel *ch) {
  struct mg_rpc_channel_ws_out_data *chd =
      (struct mg_rpc_channel_ws_out_data *) ch->channel_data;
  if (chd->reconnect_interval > chd->cfg->reconnect_interval_max) {
    chd->reconnect_interval = chd->cfg->reconnect_interval_max;
  }
  if (chd->reconnect_interval == 0) return;
  LOG(LL_DEBUG, ("reconnect in %d", chd->reconnect_interval));

  /* Set reconnect timer */
  {
    struct mg_add_sock_opts opts;
    struct mg_connection *c;
    memset(&opts, 0, sizeof(opts));
    opts.user_data = ch;
    c = mg_add_sock_opt(chd->mgr, INVALID_SOCKET, reconnect_ev_handler, opts);
    if (c != NULL) {
      c->ev_timer_time = mg_time() + chd->reconnect_interval;
      chd->reconnect_interval *= 2;
    }
    chd->fake_timer_connection = c;
  }
}
예제 #24
0
static void sj_timer_handler(struct mg_connection *c, int ev, void *p) {
  struct timer_info *ti = (struct timer_info *) c->user_data;
  (void) p;
  if (ti == NULL) return;
  switch (ev) {
    case MG_EV_TIMER: {
      if (c->flags & MG_F_CLOSE_IMMEDIATELY) break;
      if (ti->v7 != NULL) sj_invoke_cb0(ti->v7, ti->js_cb);
      if (ti->cb != NULL) ti->cb(ti->arg);
      if (ti->interval_ms > 0) {
        c->ev_timer_time = mg_time() + ti->interval_ms / 1000.0;
      } else {
        c->flags |= MG_F_CLOSE_IMMEDIATELY;
      }
      break;
    }
    case MG_EV_CLOSE: {
      if (ti->v7 != NULL) v7_disown(ti->v7, &ti->js_cb);
      free(ti);
      c->user_data = NULL;
      break;
    }
  }
}
예제 #25
0
파일: main.c 프로젝트: gavinying/mongoose
static void mg_ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
  switch (ev) {
    case MG_EV_ACCEPT: {
      char addr[32];
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO, ("%p conn from %s", nc, addr));
      break;
    }
    case MG_EV_HTTP_REQUEST: {
      char addr[32];
      struct http_message *hm = (struct http_message *) ev_data;
      cs_stat_t st;
      mg_conn_addr_to_str(nc, addr, sizeof(addr), MG_SOCK_STRINGIFY_REMOTE |
                                                      MG_SOCK_STRINGIFY_IP |
                                                      MG_SOCK_STRINGIFY_PORT);
      LOG(LL_INFO,
          ("HTTP request from %s: %.*s %.*s", addr, (int) hm->method.len,
           hm->method.p, (int) hm->uri.len, hm->uri.p));
      if (mg_vcmp(&hm->uri, "/upload") == 0 ||
          (mg_vcmp(&hm->uri, "/") == 0 && mg_stat("SL:index.html", &st) != 0)) {
        mg_send(nc, upload_form, strlen(upload_form));
        nc->flags |= MG_F_SEND_AND_CLOSE;
        break;
      }
      struct mg_serve_http_opts opts;
      memset(&opts, 0, sizeof(opts));
      opts.document_root = "SL:";
      mg_serve_http(nc, hm, opts);
      break;
    }
    case MG_EV_CLOSE: {
      LOG(LL_INFO, ("%p closed", nc));
      break;
    }
    case MG_EV_WEBSOCKET_HANDSHAKE_DONE: {
      LOG(LL_INFO, ("%p switching to data mode", nc));
      nc->handler = data_conn_handler;
      nc->ev_timer_time = mg_time(); /* Immediately */
      break;
    }
    case MG_EV_TIMER: {
      data_collect();
      nc->ev_timer_time = mg_time() + (DATA_COLLECTION_INTERVAL_MS * 0.001);
      break;
    }
    /* SimpleLink FS requires pre-declaring max file size. We use Content-Length
     * for that purpose - it will not exactly match file size, but is guaranteed
     * to exceed it and should be close enough. */
    case MG_EV_HTTP_MULTIPART_REQUEST: {
      struct http_message *hm = (struct http_message *) ev_data;
      struct mg_str *cl_header = mg_get_http_header(hm, "Content-Length");
      intptr_t cl = -1;
      if (cl_header != NULL && cl_header->len < 20) {
        char buf[20];
        memcpy(buf, cl_header->p, cl_header->len);
        buf[cl_header->len] = '\0';
        cl = atoi(buf);
        if (cl < 0) cl = -1;
      }
      nc->user_data = (void *) cl;
      break;
    }
    case MG_EV_HTTP_PART_BEGIN:
    case MG_EV_HTTP_PART_DATA:
    case MG_EV_HTTP_PART_END: {
      struct mg_http_multipart_part *mp =
          (struct mg_http_multipart_part *) ev_data;
      if (ev == MG_EV_HTTP_PART_BEGIN) {
        LOG(LL_INFO, ("Begin file upload: %s", mp->file_name));
      } else if (ev == MG_EV_HTTP_PART_END) {
        LOG(LL_INFO, ("End file upload: %s", mp->file_name));
      }
      mg_file_upload_handler(nc, ev, ev_data, upload_fname);
    }
  }
}
static _i32 fs_switch_container(struct mount_info *m, _u32 mask_begin,
                                _u32 mask_len) {
  int r;
  int new_cidx = m->cidx ^ 1;
  _i32 old_fh = m->fh, new_fh;
  _u8 *buf;
  _u32 offset, len, buf_size;
  LOG(LL_DEBUG, ("%s %d -> %d", m->cpfx, m->cidx, new_cidx));
  if (old_fh > 0 && m->rw) {
    /*
     * During the switch the destination container will be unusable.
     * If switching from a writeable container (likely in response to an erase),
     * close the old container first to make it safe and reopen for reading.
     */
    fs_close_container(m);
    old_fh = -1;
  }
  if (old_fh < 0) {
    _u8 fname[MAX_FS_CONTAINER_FNAME_LEN];
    fs_container_fname(m->cpfx, m->cidx, fname);
    r = sl_FsOpen(fname, FS_MODE_OPEN_READ, NULL, &old_fh);
    DBG(("fopen %s %d", m->cpfx, r));
    if (r < 0) {
      r = SPIFFS_ERR_NOT_READABLE;
      goto out_close_old;
    }
  }
  miot_wdt_feed();
  new_fh = fs_create_container(m->cpfx, new_cidx, m->fs.cfg.phys_size);
  if (new_fh < 0) {
    r = new_fh;
    goto out_close_old;
  }

  buf_size = 1024;
  buf = get_buf(&buf_size);
  if (buf == NULL) {
    r = SPIFFS_ERR_INTERNAL;
    goto out_close_new;
  }

  for (offset = 0; offset < m->fs.cfg.phys_size;) {
    len = buf_size;
    if (offset == mask_begin) {
      offset = mask_begin + mask_len;
    } else if (offset + len > mask_begin && offset < mask_begin + mask_len) {
      len = mask_begin - offset;
    }
    if (offset + len > m->fs.cfg.phys_size) {
      len = m->fs.cfg.phys_size - offset;
    }
    DBG(("copy %d @ %d", (int) len, (int) offset));
    if (len > 0) {
      r = sl_FsRead(old_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_READABLE;
        goto out_free;
      }
      r = sl_FsWrite(new_fh, offset, buf, len);
      if (r != len) {
        r = SPIFFS_ERR_NOT_WRITABLE;
        goto out_free;
      }
      offset += len;
    }
  }

  m->seq--;
  m->cidx = new_cidx;
  m->fh = new_fh;
  new_fh = -1;
  m->rw = 1;

  r = fs_write_mount_meta(m);

  m->last_write = mg_time();

out_free:
  free(buf);
out_close_new:
  if (new_fh > 0) sl_FsClose(new_fh, NULL, NULL, 0);
out_close_old:
  sl_FsClose(old_fh, NULL, NULL, 0);
  LOG((r == 0 ? LL_DEBUG : LL_ERROR), ("%d", r));
  return r;
}
예제 #27
0
time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
  struct mg_mgr *mgr = iface->mgr;
  int n = 0;
  double now = mg_time();
  struct mg_connection *nc, *tmp;
  double min_timer = 0;
  int num_timers = 0;
#if 0
  DBG(("begin poll @%u", (unsigned int) (now * 1000)));
#endif
  mg_ev_mgr_lwip_process_signals(mgr);
  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
    tmp = nc->next;
    n++;
    if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
        ((nc->flags & MG_F_SEND_AND_CLOSE) && (nc->flags & MG_F_UDP) &&
         (nc->send_mbuf.len == 0))) {
      mg_close_conn(nc);
      continue;
    }
    mg_if_poll(nc, now);
    mg_if_timer(nc, now);
#if MG_ENABLE_SSL
    if ((nc->flags & MG_F_SSL) && cs != NULL && cs->pcb.tcp != NULL &&
        cs->pcb.tcp->state == ESTABLISHED) {
      if (((nc->flags & MG_F_WANT_WRITE) ||
           ((nc->send_mbuf.len > 0) &&
            (nc->flags & MG_F_SSL_HANDSHAKE_DONE))) &&
          cs->pcb.tcp->snd_buf > 0) {
        /* Can write more. */
        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc);
        } else {
          mg_lwip_ssl_do_hs(nc);
        }
      }
      if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) {
        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc);
        } else {
          mg_lwip_ssl_do_hs(nc);
        }
      }
    } else
#endif /* MG_ENABLE_SSL */
    {
      if (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING)) {
        mg_lwip_send_more(nc);
      }
    }
    if (nc->sock != INVALID_SOCKET &&
        !(nc->flags & (MG_F_UDP | MG_F_LISTENING)) && cs->pcb.tcp != NULL &&
        cs->pcb.tcp->unsent != NULL) {
      tcpip_callback(tcp_output_tcpip, cs->pcb.tcp);
    }
    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }

    if (nc->sock != INVALID_SOCKET) {
      /* Try to consume data from cs->rx_chain */
      mg_lwip_consume_rx_chain_tcp(nc);

      /*
       * If the connection is about to close, and rx_chain is finally empty,
       * send the MG_SIG_CLOSE_CONN signal
       */
      if (cs->draining_rx_chain && cs->rx_chain == NULL) {
        mg_lwip_post_signal(MG_SIG_CLOSE_CONN, nc);
      }
    }
  }
#if 0
  DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms",
       (unsigned int) (now * 1000), n, num_timers,
       (unsigned int) (min_timer * 1000), timeout_ms));
#endif
  (void) timeout_ms;
  return now;
}
예제 #28
0
time_t mg_socket_if_poll(struct mg_iface *iface, int timeout_ms) {
  struct mg_mgr *mgr = iface->mgr;
  double now = mg_time();
  double min_timer;
  struct mg_connection *nc, *tmp;
  struct timeval tv;
  fd_set read_set, write_set, err_set;
  sock_t max_fd = INVALID_SOCKET;
  int num_fds, num_ev, num_timers = 0;
#ifdef __unix__
  int try_dup = 1;
#endif

  FD_ZERO(&read_set);
  FD_ZERO(&write_set);
  FD_ZERO(&err_set);
#if MG_ENABLE_BROADCAST
  mg_add_to_set(mgr->ctl[1], &read_set, &max_fd);
#endif

  /*
   * Note: it is ok to have connections with sock == INVALID_SOCKET in the list,
   * e.g. timer-only "connections".
   */
  min_timer = 0;
  for (nc = mgr->active_connections, num_fds = 0; nc != NULL; nc = tmp) {
    tmp = nc->next;

    if (nc->sock != INVALID_SOCKET) {
      num_fds++;

#ifdef __unix__
      /* A hack to make sure all our file descriptos fit into FD_SETSIZE. */
      if (nc->sock >= (sock_t) FD_SETSIZE && try_dup) {
        int new_sock = dup(nc->sock);
        if (new_sock >= 0) {
          if (new_sock < (sock_t) FD_SETSIZE) {
            closesocket(nc->sock);
            DBG(("new sock %d -> %d", nc->sock, new_sock));
            nc->sock = new_sock;
          } else {
            closesocket(new_sock);
            DBG(("new sock is still larger than FD_SETSIZE, disregard"));
            try_dup = 0;
          }
        } else {
          try_dup = 0;
        }
      }
#endif

      if (!(nc->flags & MG_F_WANT_WRITE) &&
          nc->recv_mbuf.len < nc->recv_mbuf_limit &&
          (!(nc->flags & MG_F_UDP) || nc->listener == NULL)) {
        mg_add_to_set(nc->sock, &read_set, &max_fd);
      }

      if (((nc->flags & MG_F_CONNECTING) && !(nc->flags & MG_F_WANT_READ)) ||
          (nc->send_mbuf.len > 0 && !(nc->flags & MG_F_CONNECTING))) {
        mg_add_to_set(nc->sock, &write_set, &max_fd);
        mg_add_to_set(nc->sock, &err_set, &max_fd);
      }
    }

    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }
  }

  /*
   * If there is a timer to be fired earlier than the requested timeout,
   * adjust the timeout.
   */
  if (num_timers > 0) {
    double timer_timeout_ms = (min_timer - mg_time()) * 1000 + 1 /* rounding */;
    if (timer_timeout_ms < timeout_ms) {
      timeout_ms = (int) timer_timeout_ms;
    }
  }
  if (timeout_ms < 0) timeout_ms = 0;

  tv.tv_sec = timeout_ms / 1000;
  tv.tv_usec = (timeout_ms % 1000) * 1000;

  num_ev = select((int) max_fd + 1, &read_set, &write_set, &err_set, &tv);
  now = mg_time();
#if 0
  DBG(("select @ %ld num_ev=%d of %d, timeout=%d", (long) now, num_ev, num_fds,
       timeout_ms));
#endif

#if MG_ENABLE_BROADCAST
  if (num_ev > 0 && mgr->ctl[1] != INVALID_SOCKET &&
      FD_ISSET(mgr->ctl[1], &read_set)) {
    mg_mgr_handle_ctl_sock(mgr);
  }
#endif

  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
    int fd_flags = 0;
    if (nc->sock != INVALID_SOCKET) {
      if (num_ev > 0) {
        fd_flags = (FD_ISSET(nc->sock, &read_set) &&
                            (!(nc->flags & MG_F_UDP) || nc->listener == NULL)
                        ? _MG_F_FD_CAN_READ
                        : 0) |
                   (FD_ISSET(nc->sock, &write_set) ? _MG_F_FD_CAN_WRITE : 0) |
                   (FD_ISSET(nc->sock, &err_set) ? _MG_F_FD_ERROR : 0);
      }
#if MG_LWIP
      /* With LWIP socket emulation layer, we don't get write events for UDP */
      if ((nc->flags & MG_F_UDP) && nc->listener == NULL) {
        fd_flags |= _MG_F_FD_CAN_WRITE;
      }
#endif
    }
    tmp = nc->next;
    mg_mgr_handle_conn(nc, fd_flags, now);
  }

  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
    tmp = nc->next;
    if ((nc->flags & MG_F_CLOSE_IMMEDIATELY) ||
        (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE))) {
      mg_close_conn(nc);
    }
  }

  return (time_t) now;
}
예제 #29
0
time_t mg_mgr_poll(struct mg_mgr *mgr, int timeout_ms) {
  int n = 0;
  double now = mg_time();
  struct mg_connection *nc, *tmp;
  double min_timer = 0;
  int num_timers = 0;
  DBG(("begin poll @%u, hf=%u", (unsigned int) (now * 1000),
       system_get_free_heap_size()));
  for (nc = mgr->active_connections; nc != NULL; nc = tmp) {
    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
    (void) cs;
    tmp = nc->next;
    n++;
    if (nc->flags & MG_F_CLOSE_IMMEDIATELY) {
      mg_close_conn(nc);
      continue;
    }
    mg_if_poll(nc, now);
    mg_if_timer(nc, now);
    if (nc->send_mbuf.len == 0 && (nc->flags & MG_F_SEND_AND_CLOSE) &&
        !(nc->flags & MG_F_WANT_WRITE)) {
      mg_close_conn(nc);
      continue;
    }
#ifdef SSL_KRYPTON
    if (nc->ssl != NULL && cs != NULL && cs->pcb.tcp != NULL &&
        cs->pcb.tcp->state == ESTABLISHED) {
      if (((nc->flags & MG_F_WANT_WRITE) || nc->send_mbuf.len > 0) &&
          cs->pcb.tcp->snd_buf > 0) {
        /* Can write more. */
        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_send(nc);
        } else {
          mg_lwip_ssl_do_hs(nc);
        }
      }
      if (cs->rx_chain != NULL || (nc->flags & MG_F_WANT_READ)) {
        if (nc->flags & MG_F_SSL_HANDSHAKE_DONE) {
          if (!(nc->flags & MG_F_CONNECTING)) mg_lwip_ssl_recv(nc);
        } else {
          mg_lwip_ssl_do_hs(nc);
        }
      }
    } else
#endif /* SSL_KRYPTON */
    {
      if (!(nc->flags & (MG_F_CONNECTING | MG_F_UDP))) {
        if (nc->send_mbuf.len > 0) mg_lwip_send_more(nc);
      }
    }
    if (nc->ev_timer_time > 0) {
      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
        min_timer = nc->ev_timer_time;
      }
      num_timers++;
    }
  }
  now = mg_time();
  timeout_ms = MG_POLL_INTERVAL_MS;
  if (num_timers > 0) {
    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
    if (timer_timeout_ms < timeout_ms) {
      timeout_ms = timer_timeout_ms;
    }
  }
  if (timeout_ms <= 0) timeout_ms = 1;
  DBG(("end poll @%u, %d conns, %d timers (min %u), next in %d ms",
       (unsigned int) (now * 1000), n, num_timers,
       (unsigned int) (min_timer * 1000), timeout_ms));
  os_timer_disarm(&s_poll_tmr);
  os_timer_arm(&s_poll_tmr, timeout_ms, 0 /* no repeat */);
  return now;
}