Esempio n. 1
0
File: client.c Progetto: ahans/lcm
static int
do_echo_test(void)
{
    int maxlen = 10000;
    int minlen = 10;
    g_echo_data = malloc(maxlen);
    lcm_subscription_t* subs = lcm_subscribe(g_lcm, "TEST_ECHO_REPLY", echo_handler, NULL);
    g_echo_response_count = 0;

    int iter;
    for(iter=0; iter<100; iter++)
    {
        g_echo_msg_len = rand() % (maxlen - minlen) + minlen;
        int i;
        for(i=0; i<g_echo_msg_len; i++)
            g_echo_data[i] = rand() % 256;

        lcm_publish(g_lcm, "TEST_ECHO", g_echo_data, g_echo_msg_len);

        if(!_lcm_handle_timeout(g_lcm, 500) || (g_echo_response_count != iter+1))
        {
            info("echo test failed to receive response on iteration %d\n", iter);
            free(g_echo_data);
            return 0;
        }
    }

    info("%-32s : PASSED\n", "echo test");
    lcm_unsubscribe(g_lcm, subs);
    free(g_echo_data);
    return 1;
}
Esempio n. 2
0
/**
 * Subscribes to a channel. Requires the user to specify a Lua handler (which
 * is called by generic_lcm_handler). The whole process is somewhat involved.
 *
 * In Lua 5.1, the handlers are stored in the LCM userdata's environment.
 *
 * Subscriptions are managed by the LCM userdata, and the user is not given
 * access to the subscription userdata. This subscribe method returns
 * subscription reference number, which is used to unsubscribe later. If the
 * user does not manually unsubscribe, the subscription will automatically
 * terminate during garbage collection of the LCM userdata.
 *
 * @see lcm_subscribe
 *
 * @pre The Lua arguments on the stack:
 *     A LCM userdata (self), a string containing the channel, and a function
 *     for the handler.
 *
 * @post The Lua return values on the stack:
 *     A subscription reference number.
 *
 * @param L The Lua state.
 * @return The number of return values on the Lua stack.
 */
static int impl_lcm_subscribe(lua_State * L) {

    /* we expect 3 arguments */
    lua_settop(L, 3);

    /* get the lcm userdata */
    impl_lcm_userdata_t * lcmu = impl_lcm_checkuserdata(L, 1);

    /* get the channel */
    const char * channel = luaL_checkstring(L, 2);

    /* check the handler */
    luaL_checktype(L, 3, LUA_TFUNCTION);

    /* add a subscription table entry */
    int * ref_num_ptr;
    lcm_subscription_t ** subscription;
    void * userdata =
        impl_lcm_addtosubscriptiontable(L, 1, &ref_num_ptr, &subscription);

    /* pop subscription table */
    lua_pop(L, 1);

    /* do the actual subscribe */
    *subscription =
        lcm_subscribe(lcmu->lcm, channel, impl_lcm_c_handler, userdata);

    /* push reference number */
    lua_pushinteger(L, *ref_num_ptr);

    return 1;
}
Esempio n. 3
0
static int 
udpm_self_test (lcm_udpm_t *lcm)
{
    int success = 0;
    int status;
    // register a handler for the self test message
    lcm_subscription_t *h = lcm_subscribe (lcm->lcm, SELF_TEST_CHANNEL, 
                                           self_test_handler, &success);

    // transmit a message
    char *msg = "lcm self test";
    lcm_udpm_publish (lcm, SELF_TEST_CHANNEL, (uint8_t*)msg, strlen (msg));

    // wait one second for message to be received
    GTimeVal now, endtime;
    g_get_current_time(&now);
    endtime.tv_sec = now.tv_sec + 10;
    endtime.tv_usec = now.tv_usec;

    // periodically retransmit, just in case
    GTimeVal retransmit_interval = { 0, 100000 };
    GTimeVal next_retransmit;
    lcm_timeval_add (&now, &retransmit_interval, &next_retransmit);

    int recvfd = lcm->notify_pipe[0];

    do {
        GTimeVal selectto;
        lcm_timeval_subtract (&next_retransmit, &now, &selectto);

        fd_set readfds;
        FD_ZERO (&readfds);
        FD_SET (recvfd,&readfds);

        g_get_current_time(&now);
        if (lcm_timeval_compare (&now, &next_retransmit) > 0) {
            status = lcm_udpm_publish (lcm, SELF_TEST_CHANNEL, (uint8_t*)msg, 
                    strlen (msg));
            lcm_timeval_add (&now, &retransmit_interval, &next_retransmit);
        }

        status=select (recvfd + 1,&readfds,0,0, (struct timeval*) &selectto);
        if (status > 0 && FD_ISSET (recvfd,&readfds)) {
            lcm_udpm_handle (lcm);
        }
        g_get_current_time(&now);

    } while (! success && lcm_timeval_compare (&now, &endtime) < 0);

    lcm_unsubscribe (lcm->lcm, h);

    dbg (DBG_LCM, "LCM: self test complete\n");

    // if the self test message was received, then the handler modified the
    // value of success to be 1
    return (success == 1)?0:-1;
}
Esempio n. 4
0
Subscription*
LCM::subscribeFunction(const std::string& channel,
        void (*handler)(const ReceiveBuffer *rbuf, 
                       const std::string& channel, 
                       ContextClass context),
        ContextClass context) {
    if(!this->lcm) {
        fprintf(stderr, "LCM instance not initialized.  Ignoring call to subscribeFunction()\n");
        return NULL;
    }
    typedef LCMUntypedSubscription<ContextClass> SubsClass;
    SubsClass *sub = new SubsClass();
    sub->c_subs = lcm_subscribe(lcm, channel.c_str(), SubsClass::cb_func, sub);
    sub->handler = handler;
    sub->context = context;
    subscriptions.push_back(sub);
    return sub;
}
Esempio n. 5
0
Subscription* 
LCM::subscribe(const std::string& channel, 
    void (MessageHandlerClass::*handlerMethod)(const ReceiveBuffer* rbuf, const std::string& channel),
    MessageHandlerClass* handler)
{
    if(!this->lcm) {
        fprintf(stderr, 
            "LCM instance not initialized.  Ignoring call to subscribe()\n");
        return NULL;
    }
    LCMMHUntypedSubscription<MessageHandlerClass> *subs = 
        new LCMMHUntypedSubscription<MessageHandlerClass>();
    subs->handler = handler;
    subs->handlerMethod = handlerMethod;
    subs->c_subs = lcm_subscribe(this->lcm, channel.c_str(), 
            LCMMHUntypedSubscription<MessageHandlerClass>::cb_func, subs);
    subscriptions.push_back(subs);
    return subs;
}
Esempio n. 6
0
static PyObject *
pylcm_subscribe (PyLCMObject *lcm_obj, PyObject *args)
{
    char *channel = NULL;
    int chan_len = 0;
    PyObject *handler = NULL;
    if (!PyArg_ParseTuple (args, "s#O", &channel, &chan_len, &handler)) 
        return NULL;

    if (!channel || ! chan_len) {
        PyErr_SetString (PyExc_ValueError, "invalid channel");
        return NULL;
    }
    if (!PyCallable_Check (handler))  {
        PyErr_SetString (PyExc_ValueError, "handler is not callable");
        return NULL;
    }

    PyLCMSubscriptionObject * subs_obj = 
        (PyLCMSubscriptionObject*) PyType_GenericNew (&pylcm_subscription_type, 
                NULL, NULL);

    lcm_subscription_t *subscription = 
        lcm_subscribe (lcm_obj->lcm, channel, pylcm_msg_handler, subs_obj);
    if (!subscription) {
        Py_DECREF (subs_obj);
        Py_RETURN_NONE;
    }

    subs_obj->subscription = subscription;
    subs_obj->handler = handler;
    Py_INCREF (handler);
    subs_obj->lcm_obj = lcm_obj;

    PyList_Append (lcm_obj->all_handlers, (PyObject*)subs_obj);

    return (PyObject*)subs_obj;
}
Esempio n. 7
0
int main(int argc, char **argv)
{
    logplayer_t l;
    double speed = 1.0;
    int c;
    char *expression = NULL;
    struct option long_opts[] = {
        {"help", no_argument, 0, 'h'},          {"speed", required_argument, 0, 's'},
        {"lcm-url", required_argument, 0, 'l'}, {"verbose", no_argument, 0, 'v'},
        {"regexp", required_argument, 0, 'e'},  {0, 0, 0, 0},
    };

    char *lcmurl = NULL;
    memset(&l, 0, sizeof(logplayer_t));
    while ((c = getopt_long(argc, argv, "hp:s:ve:l:", long_opts, 0)) >= 0) {
        switch (c) {
        case 's':
            speed = strtod(optarg, NULL);
            break;
        case 'l':
            free(lcmurl);
            lcmurl = strdup(optarg);
            break;
        case 'v':
            l.verbose = 1;
            break;
        case 'e':
            expression = strdup(optarg);
            break;
        case 'h':
        default:
            usage(argv[0]);
            return 1;
        };
    }

    if (optind != argc - 1) {
        usage(argv[0]);
        return 1;
    }

    char *file = argv[optind];
    printf("Using playback speed %f\n", speed);
    if (!expression)
        expression = strdup(".*");
#ifndef WIN32
    char url_in[strlen(file) + 64];
#else
    char url_in[2048];
#endif
    sprintf(url_in, "file://%s?speed=%f", argv[optind], speed);
    l.lcm_in = lcm_create(url_in);
    if (!l.lcm_in) {
        fprintf(stderr, "Error: Failed to open %s\n", file);
        free(expression);
        return 1;
    }

    l.lcm_out = lcm_create(lcmurl);
    free(lcmurl);
    if (!l.lcm_out) {
        fprintf(stderr, "Error: Failed to create LCM\n");
        free(expression);
        return 1;
    }

    lcm_subscribe(l.lcm_in, expression, handler, &l);

    while (!lcm_handle(l.lcm_in)) {
        // Do nothing
    }

    lcm_destroy(l.lcm_in);
    lcm_destroy(l.lcm_out);
    free(expression);
}
Esempio n. 8
0
int LcmTunnel::on_tcp_data(GIOChannel * source, GIOCondition cond, void *user_data)
{
  int ret = TRUE;
  LcmTunnel * self = (LcmTunnel*) user_data;

  // increase buffer size if needed
  if (self->buf_sz < self->bytes_to_read) {
    assert(self->bytes_read == 0);
    self->buf = (char *) realloc(self->buf, self->bytes_to_read);
    self->buf_sz = self->bytes_to_read;
  }

  ssize_t nread = read(ssocket_get_fd(self->tcp_sock), self->buf + self->bytes_read, self->bytes_to_read
      - self->bytes_read);

  if (nread <= 0) {
    perror("tcp receive error: ");
    LcmTunnelServer::disconnectClient(self);
    return FALSE;
  }

  self->bytes_read += nread;
  assert(self->bytes_read <= self->bytes_to_read);

  if (self->bytes_read != self->bytes_to_read)
    return TRUE;

  switch (self->tunnel_state) {
  case CLIENT_MSG_SZ:
    self->bytes_to_read = ntohl(*(uint32_t*) self->buf);
    self->tunnel_state = CLIENT_MSG_DATA;
    break;
  case CLIENT_MSG_DATA:
    {
      lcm_tunnel_params_t tp_rec;
      int decode_status = lcm_tunnel_params_t_decode(self->buf, 0, self->bytes_read, &tp_rec);
      if (decode_status <= 0) {
        fprintf(stdout, "invalid request (%d)\n", decode_status);
        return FALSE;
      }
      self->tunnel_params = lcm_tunnel_params_t_copy(&tp_rec);

      if (self->udp_fd >= 0) {
        close(self->udp_fd);
      }
      self->udp_fd = -1;

      if (self->tunnel_params->udp) {
        //setup our UDP socket, and send info to client
        struct sockaddr_in client_addr;
        socklen_t addrlen = sizeof(client_addr);
        getpeername(self->tcp_sock->socket, (struct sockaddr*) &client_addr, &addrlen);
        self->server_udp_port = ntohs(client_addr.sin_port);
        client_addr.sin_port = htons(self->tunnel_params->udp_port);

        // allocate UDP socket
        self->udp_fd = socket(AF_INET, SOCK_DGRAM, 0);
        if (self->udp_fd < 0) {
          perror("allocating UDP socket");
          LcmTunnelServer::disconnectClient(self);
          return FALSE;
        }

        connect(self->udp_fd, (struct sockaddr*) &client_addr, sizeof(client_addr));

        // transmit the udp port info
        struct sockaddr_in udp_addr;
        socklen_t udp_addr_len = sizeof(udp_addr);
        memset(&udp_addr, 0, sizeof(udp_addr));
        udp_addr.sin_family = AF_INET;
        udp_addr.sin_addr.s_addr = INADDR_ANY;
        udp_addr.sin_port = 0;
        getsockname(self->udp_fd, (struct sockaddr*) &udp_addr, &udp_addr_len);
        lcm_tunnel_params_t tp_port_msg;
        tp_port_msg.channels = (char *) " ";
        tp_port_msg.udp_port = ntohs(udp_addr.sin_port);
        int msg_sz = lcm_tunnel_params_t_encoded_size(&tp_port_msg);
        uint8_t msg[msg_sz];
        lcm_tunnel_params_t_encode(msg, 0, msg_sz, &tp_port_msg);
        uint32_t msg_sz_n = htonl(msg_sz);
        if (4 != _fileutils_write_fully(ssocket_get_fd(self->tcp_sock), &msg_sz_n, 4)) {
          perror("sending subscription data");
          LcmTunnelServer::disconnectClient(self);
          return FALSE;
        }
        if (msg_sz != _fileutils_write_fully(ssocket_get_fd(self->tcp_sock), msg, msg_sz)) {
          perror("sending subscription data");
          LcmTunnelServer::disconnectClient(self);
          return FALSE;
        }

        self->udp_ioc = g_io_channel_unix_new(self->udp_fd);
        self->udp_sid = g_io_add_watch(self->udp_ioc, G_IO_IN, LcmTunnel::on_udp_data, self);

        //we're done setting up the UDP connection...Disconnect tcp socket
        self->closeTCPSocket();
        ret = false;
      }

      //get ready to receive
      self->tunnel_state = RECV_CHAN_SZ;
      self->bytes_to_read = 4;

      //      if (self->server_params->verbose)
      fprintf(stderr, "%s subscribed to \"%s\" -- ", self->name, self->tunnel_params->channels);

      if (self->udp_fd >= 0) {
        if (self->tunnel_params->fec > 1)
          fprintf(stderr, "UDP with FEC rate of %.2f and max_delay of %dms\n", self->tunnel_params->fec,
              self->tunnel_params->max_delay_ms);
        else if (self->tunnel_params->fec < -1)
          fprintf(stderr, "UDP with DUP rate of %d and max_delay of %dms\n", (int) -self->tunnel_params->fec,
              self->tunnel_params->max_delay_ms);
        else
          fprintf(stderr, "UDP with a max_delay of %dms\n", self->tunnel_params->max_delay_ms);
      }
      else {
        fprintf(stderr, "TCP with max_delay of %dms and tcp_max_age_ms of %d\n", self->tunnel_params->max_delay_ms,
            self->tunnel_params->tcp_max_age_ms);
      }

      self->init_regex(self->tunnel_params->channels);

      //subscribe to the LCM channels
      if (self->subscription) {
        lcm_unsubscribe(self->lcm, self->subscription);
      }
      self->subscription = lcm_subscribe(self->lcm, self->tunnel_params->channels, on_lcm_message, self);

    }
    break;
  case SERVER_MSG_SZ:
    self->bytes_to_read = ntohl(*(uint32_t*) self->buf);
    self->tunnel_state = SERVER_MSG_DATA;
    break;
  case SERVER_MSG_DATA:
    {
      lcm_tunnel_params_t tp_rec;
      int decode_status = lcm_tunnel_params_t_decode(self->buf, 0, self->bytes_read, &tp_rec);
      if (decode_status <= 0) {
        fprintf(stderr, "invalid request (%d)\n", decode_status);
        return FALSE;
      }
      assert(self->udp_fd>0);
      struct sockaddr_in client_addr;
      socklen_t addrlen = sizeof(client_addr);
      getpeername(self->tcp_sock->socket, (struct sockaddr*) &client_addr, &addrlen);
      self->server_udp_port = tp_rec.udp_port;
      client_addr.sin_port = htons(tp_rec.udp_port);
      //connect the udp socket
      connect(self->udp_fd, (struct sockaddr*) &client_addr, sizeof(client_addr));

      //now we can subscribe to LCM
      fprintf(stderr, "%s subscribed to \"%s\" \n", self->name, self->tunnel_params->channels);
      self->subscription = lcm_subscribe(self->lcm, self->tunnel_params->channels, on_lcm_message, self);

      //we're done setting up the UDP connection...Disconnect tcp socket
      self->closeTCPSocket();
      ret = FALSE; //don't want the TCP handler to be run again
    }
    break;
  case RECV_CHAN_SZ:
    self->bytes_to_read = ntohl(*(uint32_t*) self->buf);
    self->tunnel_state = RECV_CHAN;

    if (self->channel_sz < self->bytes_to_read + 1) {
      self->channel = (char *) realloc(self->channel, self->bytes_to_read + 1);
      self->channel_sz = self->bytes_to_read + 1;
    }
    break;
  case RECV_CHAN:
    memcpy(self->channel, self->buf, self->bytes_read);
    self->channel[self->bytes_read] = 0;

    self->bytes_to_read = 4;
    self->tunnel_state = RECV_DATA_SZ;
    break;
  case RECV_DATA_SZ:
    self->bytes_to_read = ntohl(*(uint32_t*) self->buf);
    self->tunnel_state = RECV_DATA;
    break;
  case RECV_DATA:
    if (self->verbose)
      printf("Recieved TCP message on channel \"%s\"\n", self->channel);
    LcmTunnelServer::check_and_send_to_tunnels(self->channel, self->buf, self->bytes_read, self);
    lcm_publish(self->lcm, self->channel, (uint8_t*) self->buf, self->bytes_read);

    self->bytes_to_read = 4;
    self->tunnel_state = RECV_CHAN_SZ;
    break;
  }

  self->bytes_read = 0;

  return ret;
}
Esempio n. 9
0
int LcmTunnel::connectToServer(lcm_t * lcm_, introspect_t *introspect_, GMainLoop * mainloop_, char * server_addr_str,
    int port, char * channels_to_recv, lcm_tunnel_params_t * tunnel_params_, tunnel_server_params_t * server_params_)
{ //for a client that should initiate a connection with a server

  tunnel_params = lcm_tunnel_params_t_copy(tunnel_params_);
  server_params = server_params_;
  lcm = lcm_;
  introspect = introspect_;
  mainloop = mainloop_;

  if (tunnel_params->udp) {
    // allocate UDP socket
    udp_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (udp_fd < 0) {
      perror("allocating UDP socket");
      return 0;
    }

    struct sockaddr_in udp_addr;
    socklen_t udp_addr_len = sizeof(udp_addr);
    memset(&udp_addr, 0, sizeof(udp_addr));
    udp_addr.sin_family = AF_INET;
    udp_addr.sin_addr.s_addr = INADDR_ANY;
    udp_addr.sin_port = 0;

    if (bind(udp_fd, (struct sockaddr*) &udp_addr, sizeof(udp_addr)) < 0) {
      perror("binding UDP socket");
      return 0;
    }

    getsockname(udp_fd, (struct sockaddr*) &udp_addr, &udp_addr_len);
    tunnel_params->udp_port = ntohs(udp_addr.sin_port);

    udp_ioc = g_io_channel_unix_new(udp_fd);
    udp_sid = g_io_add_watch(udp_ioc, G_IO_IN, LcmTunnel::on_udp_data, this);
  }
  else {
    udp_fd = -1;
  }

  // connect
  tcp_sock = ssocket_create();
  if (0 != ssocket_connect(tcp_sock, server_addr_str, port)) {
    perror("connecting");
    return 0;
  }
  tcp_ioc = g_io_channel_unix_new(ssocket_get_fd(tcp_sock));
  tcp_sid = g_io_add_watch(tcp_ioc, G_IO_IN, on_tcp_data, this);

  //fill out the name info
  struct sockaddr_in server_addr;
  socklen_t addrlen = sizeof(server_addr);
  getpeername(tcp_sock->socket, (struct sockaddr*) &server_addr, &addrlen);
  uint32_t server_port = ntohs(server_addr.sin_port);
  snprintf(name, sizeof(name), "%s:%d", inet_ntoa(server_addr.sin_addr), server_port);
  fprintf(stderr, "Connected to %s\n", name);

  // transmit subscription information
  lcm_tunnel_params_t * tun_params_to_send = lcm_tunnel_params_t_copy(tunnel_params);
  //put the channels the server should send in the params we're sending it.
  free(tun_params_to_send->channels);
  tun_params_to_send->channels = strdup(channels_to_recv);
  int msg_sz = lcm_tunnel_params_t_encoded_size(tun_params_to_send);
  uint8_t * msg = (uint8_t *) calloc(msg_sz, sizeof(uint8_t));
  lcm_tunnel_params_t_encode(msg, 0, msg_sz, tun_params_to_send);
  uint32_t msg_sz_n = htonl(msg_sz);
  if (4 != _fileutils_write_fully(ssocket_get_fd(tcp_sock), &msg_sz_n, 4)) {
    perror("sending subscription data");
    ssocket_destroy(tcp_sock);
    free(msg);
    lcm_tunnel_params_t_destroy(tun_params_to_send);
    return 0;
  }
  if (msg_sz != _fileutils_write_fully(ssocket_get_fd(tcp_sock), msg, msg_sz)) {
    perror("sending subscription data");
    ssocket_destroy(tcp_sock);
    free(msg);
    lcm_tunnel_params_t_destroy(tun_params_to_send);
    return 0;
  }
  lcm_tunnel_params_t_destroy(tun_params_to_send);
  free(msg);

  //set state for tcp receptions
  bytes_to_read = 4;
  bytes_read = 0;
  if (tunnel_params->udp) {
    tunnel_state = SERVER_MSG_SZ; //wait for udp port from server
  }
  else {
    tunnel_state = RECV_CHAN_SZ;
    //subscribe to the channels we want to send out
    //only subscribe if we're doing TCP, since UDP socket hasn't been setup yet
    subscription = lcm_subscribe(lcm, tunnel_params->channels, on_lcm_message, this);

  }
  return 1;
}
Esempio n. 10
0
int main(int argc, char *argv[])
{
#ifndef WIN32
    setlinebuf (stdout);
#endif

    char logpath[PATH_MAX];
    memset (logpath, 0, sizeof (logpath));

    logger_t logger;
    memset (&logger, 0, sizeof (logger));

    // set some defaults
    logger.force_overwrite = 0;
    logger.auto_increment = 0;
    logger.use_strftime = 0;
    char *chan_regex = strdup(".*");
    double max_write_queue_size_mb = DEFAULT_MAX_WRITE_QUEUE_SIZE_MB;
    logger.invert_channels = 0;
    logger.fflush_interval_ms = 100;

    char *lcmurl = NULL;
    char *optstring = "a:fic:shm:vu:";
    int c;
    struct option long_opts[] = {
        { "auto-split-hours", required_argument, 0, 'a' },
        { "auto-split-mb", required_argument, 0, 'b' },
        { "channel", required_argument, 0, 'c' },
        { "force", no_argument, 0, 'f' },
        { "increment", required_argument, 0, 'i' },
        { "lcm-url", required_argument, 0, 'l' },
        { "max-unwritten-mb", required_argument, 0, 'm' },
        { "strftime", required_argument, 0, 's' },
        { "invert-channels", no_argument, 0, 'v' },
        { "flush-interval", required_argument, 0,'u'},
        { 0, 0, 0, 0 }
    };

    while ((c = getopt_long (argc, argv, optstring, long_opts, 0)) >= 0)
    {
        switch (c) {
            case 'a':
                logger.auto_split_hours = strtod(optarg, NULL);
                logger.auto_increment = 1;
                if(logger.auto_split_hours <= 0) {
                    usage();
                    return 1;
                }
                break;
            case 'b':
                logger.auto_split_mb = strtod(optarg, NULL);
                logger.auto_increment = 1;
                if(logger.auto_split_mb <= 0) {
                    usage();
                    return 1;
                }
                break;
            case 'f':
                logger.force_overwrite = 1;
                break;
            case 'c':
                free(chan_regex);
                chan_regex = strdup(optarg);
                break;
            case 'i':
                logger.auto_increment = 1;
                break;
            case 's':
                logger.use_strftime = 1;
                break;
            case 'l':
                free(lcmurl);
                lcmurl = strdup(optarg);
                break;
            case 'v':
                logger.invert_channels = 1;
                break;
            case 'm':
                max_write_queue_size_mb = strtod(optarg, NULL);
                if(max_write_queue_size_mb <= 0) {
                    usage();
                    return 1;
                }
                break;
            case 'u':
              logger.fflush_interval_ms = atol(optarg);
              if(logger.fflush_interval_ms <= 0) {
                  usage();
                  return 1;
              }
              break;
            case 'h':
            default:
                usage();
                return 1;
        };
    }

    if (optind == argc) {
        strcpy (logger.input_fname, "lcmlog-%Y-%m-%d");
        logger.auto_increment = 1;
        logger.use_strftime = 1;
    } else if (optind == argc - 1) {
        strncpy (logger.input_fname, argv[optind], sizeof (logger.input_fname));
    } else if (optind < argc-1) {
        usage ();
        return 1;
    }

    // initialize GLib threading
    g_thread_init(NULL);

    logger.time0 = timestamp_now();
    logger.max_write_queue_size = (int64_t)(max_write_queue_size_mb * (1 << 20));

    if(0 != open_logfile(&logger))
        return 1;

    // create write thread
    logger.write_thread_exit_flag = 0;
    logger.mutex = g_mutex_new();
    logger.write_queue_size = 0;
    logger.write_queue = g_async_queue_new();
    logger.write_thread = g_thread_create(write_thread, &logger, TRUE, NULL);

    // begin logging
    logger.lcm = lcm_create (lcmurl);
    free(lcmurl);
    if (!logger.lcm) {
        fprintf (stderr, "Couldn't initialize LCM!");
        return 1;
    }

    if(logger.invert_channels) {
        // if inverting the channels, subscribe to everything and invert on the
        // callback
        lcm_subscribe(logger.lcm, ".*", message_handler, &logger);
        char *regexbuf = g_strdup_printf("^%s$", chan_regex);
#ifdef USE_GREGEX
        GError *rerr = NULL;
        logger.regex = g_regex_new(regexbuf, (GRegexCompileFlags) 0, (GRegexMatchFlags) 0, &rerr);
        if(rerr) {
            fprintf(stderr, "%s\n", rerr->message);
            g_free(regexbuf);
            return 1;
        }
#else
        if (0 != regcomp (&logger.preg, regexbuf, REG_NOSUB | REG_EXTENDED)) {
            fprintf(stderr, "bad regex!\n");
            g_free(regexbuf);
            return 1;
        }
#endif
        g_free(regexbuf);
    } else {
        // otherwise, let LCM handle the regex
        lcm_subscribe(logger.lcm, chan_regex, message_handler, &logger);
    }

    free(chan_regex);

    _mainloop = g_main_loop_new (NULL, FALSE);
    signal_pipe_glib_quit_on_kill ();
    glib_mainloop_attach_lcm (logger.lcm);

#ifdef USE_SIGHUP
    signal(SIGHUP, sighup_handler);
#endif

    // main loop
    g_main_loop_run (_mainloop);

    fprintf(stderr, "Logger exiting\n");

    // stop the write thread
    g_mutex_lock(logger.mutex);
    logger.write_thread_exit_flag = 1;
    g_mutex_unlock(logger.mutex);
    g_async_queue_push(logger.write_queue, &logger.write_thread_exit_flag);
    g_thread_join(logger.write_thread);
    g_mutex_free(logger.mutex);

    // cleanup.  This isn't strictly necessary, do it to be pedantic and so that
    // leak checkers don't complain
    glib_mainloop_detach_lcm (logger.lcm);
    lcm_destroy (logger.lcm);
    lcm_eventlog_destroy (logger.log);

    for(void *msg = g_async_queue_try_pop(logger.write_queue); msg;
            msg=g_async_queue_try_pop(logger.write_queue)) {
        if(msg == &logger.write_thread_exit_flag)
            continue;
        free(msg);
    }
    g_async_queue_unref(logger.write_queue);

    if(logger.invert_channels) {
#ifdef USE_GREGEX
        g_regex_unref(logger.regex);
#else
        regfree(&logger.preg);
#endif
    }

    return 0;
}