Esempio n. 1
0
static void s1_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data)
{
    descriptor_t        *desc = (descriptor_t *) drv_data;
    callstate_t *c = (callstate_t *) thread_data;
    int                 bytes, offset, i;
    char                *p = NULL;
    unsigned long       index = 0;

    edtk_debug("%s: cmd = %d", __FUNCTION__, c->cmd);
    if (c == NULL) {
        edtk_debug("%s: c == NULL", __FUNCTION__);
        return;
    }
    switch (c->cmd) {
    case S1_NULL:
        reply_ok(desc);
        break;
    case S1_OPEN:
        if (! c->o.__expect) {
            reply_error(desc, c->o.__expect_errval);
            break;
        }
        if (find_unused_fd_index(desc, &index) < 0) {
            reply_error(desc, ENOMEM);
        } else {
            desc->valmap_fd[index] = c->o.ret_int;
            reply_ok_valmap(desc, am_valmap_fd, index);
        }
        break;
    case S1_GETFD:
        reply_ok_num(desc, c->o.ret_int);
        break;
    case S1_SENDFD:
        if (c->o.__expect) {
            reply_ok_num(desc, c->o.ret_int_t);
        } else {
            reply_error(desc, c->o.__expect_errval);
        }
        break;
    case S1_RECEIVEFD:
        if (c->o.__expect) {
            reply_ok_num(desc, c->o.ret_int_t);
        } else {
            reply_error(desc, c->o.__expect_errval);
        }
        break;
    case S1_CLOSE:
        if (! c->o.__expect) {
            reply_error(desc, c->o.__expect_errval);
            break;
        }
        cleanup_valmap_fd_index(desc, c->i.__valmap_fd_index, 0);
        reply_ok_num(desc, c->o.ret_int);
        break;
    default:
        edtk_debug("%s: bogus command, should never happen", __FUNCTION__);
        break;
    }
    sys_free(c);
}
Esempio n. 2
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_close(zmq_drv_t *drv)
{
    ErlDrvTermData caller = driver_caller(drv->port);
    zmq_sock_info* si = drv->get_socket_info(caller);

    if (!si)
    {
        reply_error(drv->port, caller, ENODEV);
        return;
    }

    zmqdrv_fprintf("close %p\r\n", si->socket);

    driver_demonitor_process(drv->port, &si->monitor);

    if (si->busy)
    {
        // Remove socket from vm polling
        driver_select(drv->port, si->fd, ERL_DRV_READ, 0);
    }

    drv->zmq_pid_socket.erase(caller);
    drv->zmq_fd_socket.erase(si->fd);

    //zmq_close(Socket) is called in ~zmq_sock_info
    delete si;

    reply_ok(drv->port, caller);
}
Esempio n. 3
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_socket(zmq_drv_t *drv, const uint8_t* bytes, size_t size)
{
    int type = *bytes;

    assert(sizeof(uint8_t) == size);

    zmqdrv_fprintf("socket (type: %d)\r\n", type);

    ErlDrvTermData caller = driver_caller(drv->port);

    if (drv->terminating)
    {
        reply_error(drv->port, caller, ETERM);
        return;
    }

    assert(NULL == drv->get_socket_info(caller));

    // Runtime validation as well in case zmq_drv.erl is used directly rather
    // than through zmq_socket.erl gen_server.
    if (NULL != drv->get_socket_info(caller))
    {
        reply_error(drv->port, caller, EBUSY);
        return;
    }

    void* s = zmq_socket(drv->zmq_context, type);

    if (!s)
    {
        reply_error(drv->port, caller, zmq_errno());
        return;
    }

    //TODO: Support Windows 'SOCKET' type?
    int fd; size_t fd_size = sizeof(fd);
    if (0 != zmq_getsockopt(s, ZMQ_FD, &fd, &fd_size))
    {
        reply_error(drv->port, caller, zmq_errno());
        zmq_close(s);
        return;
    }

    zmq_sock_info* si = new zmq_sock_info(s, (ErlDrvEvent)fd);

    if (!si)
    {
        driver_failure_posix(drv->port, ENOMEM);
        return;
    }

    driver_monitor_process(drv->port, caller, &si->monitor);
    drv->zmq_pid_socket[caller] = si;
    drv->zmq_fd_socket[si->fd] = si;

    zmqdrv_fprintf("socket %p owner %lu\r\n", si->socket, caller);

    reply_ok(drv->port, caller);
}
Esempio n. 4
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_connect(zmq_drv_t *drv, const uint8_t* bytes, size_t size)
{
    // expects the endpoint to be zero terminated
    char* endpoint  = (char*)bytes;

    // TODO: check for zero termination within size limit
    assert(sizeof(char) <= size); // Must always have at least the 0 terminating char.

    ErlDrvTermData caller = driver_caller(drv->port);

    if (drv->terminating)
    {
        reply_error(drv->port, caller, ETERM);
        return;
    }

    zmq_sock_info* si = drv->get_socket_info(caller);

    if (!si)
    {
        reply_error(drv->port, caller, ENODEV);
        return;
    }

    zmqdrv_fprintf("connect %p (endpoint: %s)\r\n", si->socket, endpoint);

    if (0 != zmq_connect(si->socket, endpoint))
    {
        reply_error(drv->port, caller, zmq_errno());
        return;
    }

    reply_ok(drv->port, caller);
}
Esempio n. 5
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_init(zmq_drv_t *drv, const uint8_t* bytes, size_t size)
{
    int io_threads  = *bytes;

    assert(sizeof(uint8_t) == size);

    zmqdrv_fprintf("init (io_threads: %d)\r\n", io_threads);

    // We only support a single zmq context, but zeromq itself supports multiple
    if (drv->zmq_context)
    {
        reply_error(drv->port, driver_caller(drv->port), EBUSY);
        return;
    }

    drv->terminating = false;
    drv->zmq_context = zmq_init(io_threads);

    if (!drv->zmq_context)
    {
        reply_error(drv->port, driver_caller(drv->port), zmq_errno());
        return;
    }

    zmqdrv_fprintf("init %p\r\n", drv->zmq_context);

    reply_ok(drv->port, driver_caller(drv->port));
}
Esempio n. 6
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_term(zmq_drv_t *drv)
{
    zmqdrv_fprintf("term %p\r\n", drv->zmq_context);

    if (0 < drv->zmq_pid_socket.size())
    {
        for (zmq_pid_socket_map_t::iterator it = drv->zmq_pid_socket.begin(); it != drv->zmq_pid_socket.end(); ++it)
        {
            zmq_sock_info* si = it->second;

            if (si->busy)
            {
                // Remove socket from erlang vm polling
                driver_select(drv->port, si->fd, ERL_DRV_READ, 0);

                if (si->out_caller)
                {
                    reply_error(drv->port, si->out_caller, ETERM);
                    si->out_caller = 0;
                    zmq_msg_close(&si->out_msg);
                }
                if (si->in_caller)
                {
                    reply_error(drv->port, si->in_caller, ETERM);
                    si->in_caller = 0;
                }
                if (si->poll_caller)
                {
                    send_events(drv->port, si->poll_caller, (uint32_t)ZMQ_POLLERR);
                    si->poll_caller = 0;
                }

                si->busy = false;
            }
        }

        // TODO: Remove if zeromq itself ever gets fixed. As zmq_term() is a
        // blocking call, and will not return until all sockets are closed,
        // so do not allow it to be called while there are open sockets.
        drv->terminating = true;
        reply_error(drv->port, driver_caller(drv->port), EAGAIN);
        return;
    }

    // cross fingers and hope zmq_term() doesn't block, else we hardlock.
    if (0 != zmq_term(drv->zmq_context))
    {
        reply_error(drv->port, driver_caller(drv->port), zmq_errno());
        return;
    }

    drv->zmq_context = NULL;

    reply_ok(drv->port, driver_caller(drv->port));
}
Esempio n. 7
0
void
erl_lua_settable(lua_drv_t *driver_data, char *buf, int index)
{
    long i;
    ei_decode_long(buf, &index, &i);

    lua_settable(driver_data->L, i);

    reply_ok(driver_data);
}
Esempio n. 8
0
void
erl_lua_concat(lua_drv_t *driver_data, char *buf, int index)
{
  long n;

  ei_decode_long(buf, &index, &n);

  lua_concat(driver_data->L, n);

  reply_ok(driver_data);
}
Esempio n. 9
0
void
erl_lua_pushinteger(lua_drv_t *driver_data, char *buf, int index)
{
  long long num;

  ei_decode_longlong(buf, &index, &num);

  lua_pushinteger(driver_data->L, num);

  reply_ok(driver_data);
}
Esempio n. 10
0
void
erl_lua_pushboolean(lua_drv_t *driver_data, char *buf, int index)
{
  int b;

  ei_decode_boolean(buf, &index, &b);

  lua_pushboolean(driver_data->L, b);

  reply_ok(driver_data);
}
Esempio n. 11
0
void
erl_lua_createtable(lua_drv_t *driver_data, char *buf, int index)
{
  long narr, nrec;

  ei_decode_long(buf, &index, &narr);
  ei_decode_long(buf, &index, &nrec);

  lua_createtable(driver_data->L, narr, nrec);

  reply_ok(driver_data);
}
Esempio n. 12
0
void
erl_lua_setglobal(lua_drv_t *driver_data, char *buf, int index)
{
  char *name;

  name = decode_string(buf, &index);

  lua_setglobal(driver_data->L, name);

  reply_ok(driver_data);
  free(name);
}
Esempio n. 13
0
void
erl_lua_call(lua_drv_t *driver_data, char *buf, int index)
{
  long args, results;

  ei_decode_long(buf, &index, &args);
  ei_decode_long(buf, &index, &results);

  lua_call(driver_data->L, args, results);

  reply_ok(driver_data);
}
Esempio n. 14
0
void
erl_lua_pushlstring(lua_drv_t *driver_data, char *buf, int index)
{
  char *str;
  int len;

  str = decode_binary(buf, &index, &len);

  lua_pushlstring(driver_data->L, str, len);

  reply_ok(driver_data);
  free(str);
}
Esempio n. 15
0
void
erl_lua_setfield(lua_drv_t *driver_data, char *buf, int index)
{
  long i;
  char *name;

  ei_decode_long(buf, &index, &i);
  name = decode_string(buf, &index);

  lua_setfield(driver_data->L, i, name);

  reply_ok(driver_data);
  free(name);
}
Esempio n. 16
0
void
erl_lual_dostring(lua_drv_t *driver_data, char *buf, int index)
{
  char *code;
  int len;

  code = decode_binary(buf, &index, &len);
  code[len] = '\0';

  if (!luaL_dostring(driver_data->L, code))
    reply_ok(driver_data);
  else
    reply_throw(driver_data, lua_tostring(driver_data->L, -1));
  free(code);
}
Esempio n. 17
0
void
erl_lua_pushnumber(lua_drv_t *driver_data, char *buf, int index)
{
  double dnum;
  long long lnum;
  int type, len;

  ei_get_type(buf, &index, &type, &len);

  switch (type) {
  case ERL_FLOAT_EXT:
    ei_decode_double(buf, &index, &dnum);
    lua_pushnumber(driver_data->L, dnum);
    break;
  default:
    ei_decode_longlong(buf, &index, &lnum);
    lua_pushnumber(driver_data->L, lnum);
    break;
  }

  reply_ok(driver_data);
}
Esempio n. 18
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_send(zmq_drv_t *drv, const uint8_t* bytes, size_t size, ErlDrvBinary* bin)
{
    int     flags     = *bytes;
    void*   data      = (void *)(bytes+sizeof(uint8_t));
    size_t  data_size = size - sizeof(uint8_t);

    assert(sizeof(uint8_t) <= size);

    ErlDrvTermData caller = driver_caller(drv->port);

    if (drv->terminating)
    {
        reply_error(drv->port, caller, ETERM);
        return;
    }

    zmq_sock_info* si = drv->get_socket_info(caller);

    if (!si)
    {
        reply_error(drv->port, caller, ENODEV);
        return;
    }

    assert(0 == si->out_caller);

    zmqdrv_fprintf("send %p (flags: %d bytes: %u)\r\n", si->socket, flags, data_size);

    // Increment the reference count on binary so that zmq can take ownership of it.
    driver_binary_inc_refc(bin);

    if (zmq_msg_init_data(&si->out_msg, data, data_size, &zmqcb_free_binary, bin))
    {
        reply_error(drv->port, caller, zmq_errno());
        driver_binary_dec_refc(bin);
        return;
    }

    if (0 == zmq_send(si->socket, &si->out_msg, flags|ZMQ_NOBLOCK))
    {
        reply_ok(drv->port, caller);
        zmq_msg_close(&si->out_msg);
    }
    else if (ZMQ_NOBLOCK != (ZMQ_NOBLOCK & flags) && EAGAIN == zmq_errno())
    {
        // Caller requested blocking send
        // Can't send right now. Make the caller wait by not returning result
        zmqdrv_fprintf("send %p blocking\r\n", si->socket);

        si->out_flags = flags;
        si->out_caller = caller;

        if (!si->busy)
        {
            driver_select(drv->port, si->fd, ERL_DRV_READ, 1);
            si->busy = true;
        }
    }
    else
    {
        reply_error(drv->port, caller, zmq_errno());
        zmq_msg_close(&si->out_msg);
    }
}
Esempio n. 19
0
int
handle_command(struct cfg *cf, struct rtpp_command *cmd)
{
    int i, verbose, rval;
    int playcount;
    char *cp, *tcp;
    char *pname, *codecs, *recording_name;
    struct rtpp_session *spa;
    int record_single_file;
    struct ul_opts *ulop;
    struct d_opts dopt;

    spa = NULL;
    recording_name = NULL;
    codecs = NULL;

    /* Step II: parse parameters that are specific to a particular op and run simple ops */
    switch (cmd->cca.op) {
    case VER_FEATURE:
        handle_ver_feature(cf, cmd);
        return 0;

    case GET_VER:
        /* This returns base version. */
        reply_number(cf, cmd, CPROTOVER);
        return 0;

    case DELETE_ALL:
        /* Delete all active sessions */
        rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog, "deleting all active sessions");
        pthread_mutex_lock(&cf->sessinfo.lock);
        for (i = 0; i < cf->sessinfo.nsessions; i++) {
            spa = cf->sessinfo.sessions[i];
            if (spa == NULL || spa->sidx[0] != i)
                continue;
            remove_session(cf, spa);
        }
        pthread_mutex_unlock(&cf->sessinfo.lock);
        reply_ok(cf, cmd);
        return 0;

    case INFO:
        handle_info(cf, cmd, &cmd->argv[0][1]);
        return 0;

    case PLAY:
        /*
         * P callid pname codecs from_tag to_tag
         *
         *   <codecs> could be either comma-separated list of supported
         *   payload types or word "session" (without quotes), in which
         *   case list saved on last session update will be used instead.
         */
        playcount = 1;
        pname = cmd->argv[2];
        codecs = cmd->argv[3];
        tcp = &(cmd->argv[0][1]);
	if (*tcp != '\0') {
	    playcount = strtol(tcp, &cp, 10);
            if (cp == tcp || *cp != '\0') {
                rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error");
                reply_error(cf, cmd, ECODE_PARSE_6);
                return 0;
            }
        }
        break;

    case COPY:
        recording_name = cmd->argv[2];
        /* Fallthrough */
    case RECORD:
        if (cmd->argv[0][1] == 'S' || cmd->argv[0][1] == 's') {
            if (cmd->argv[0][2] != '\0') {
                rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error");
                reply_error(cf, cmd, ECODE_PARSE_2);
                return 0;
            }
            record_single_file = (cf->stable->record_pcap == 0) ? 0 : 1;
        } else {
            if (cmd->argv[0][1] != '\0') {
                rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog, "command syntax error");
                reply_error(cf, cmd, ECODE_PARSE_3);
                return 0;
            }
            record_single_file = 0;
        }
        break;

    case DELETE:
        /* D[w] call_id from_tag [to_tag] */
        dopt.weak = 0;
        for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) {
            switch (*cp) {
            case 'w':
            case 'W':
                dopt.weak = 1;
                break;

            default:
                rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog,
                  "DELETE: unknown command modifier `%c'", *cp);
                reply_error(cf, cmd, ECODE_PARSE_4);
                return 0;
            }
        }
        break;

    case UPDATE:
    case LOOKUP:
        ulop = rtpp_command_ul_opts_parse(cf, cmd);
        if (ulop == NULL) {
            return 0;
        }
	break;

    case GET_STATS:
        verbose = 0;
        for (cp = cmd->argv[0] + 1; *cp != '\0'; cp++) {
            switch (*cp) {
            case 'v':
            case 'V':
                verbose = 1;
                break;

            default:
                rtpp_log_write(RTPP_LOG_ERR, cf->stable->glog,
                  "STATS: unknown command modifier `%c'", *cp);
                reply_error(cf, cmd, ECODE_PARSE_5);
                return 0;
            }
        }
        i = handle_get_stats(cf, cmd, verbose);
        if (i != 0) {
            reply_error(cf, cmd, i);
        }
        return 0;

    default:
        break;
    }

    /*
     * Record and delete need special handling since they apply to all
     * streams in the session.
     */
    switch (cmd->cca.op) {
    case DELETE:
	i = handle_delete(cf, &cmd->cca, dopt.weak);
	break;

    case RECORD:
	i = handle_record(cf, &cmd->cca, record_single_file);
	break;

    default:
	i = find_stream(cf, cmd->cca.call_id, cmd->cca.from_tag, cmd->cca.to_tag, &spa);
	if (i != -1 && cmd->cca.op != UPDATE)
	    i = NOT(i);
	break;
    }

    if (i == -1 && cmd->cca.op != UPDATE) {
	rtpp_log_write(RTPP_LOG_INFO, cf->stable->glog,
	  "%s request failed: session %s, tags %s/%s not found", cmd->cca.rname,
	  cmd->cca.call_id, cmd->cca.from_tag, cmd->cca.to_tag != NULL ? cmd->cca.to_tag : "NONE");
	if (cmd->cca.op == LOOKUP) {
            rtpp_command_ul_opts_free(ulop);
	    ul_reply_port(cf, cmd, NULL);
	    return 0;
	}
	reply_error(cf, cmd, ECODE_SESUNKN);
	return 0;
    }

    switch (cmd->cca.op) {
    case DELETE:
    case RECORD:
	reply_ok(cf, cmd);
	break;

    case NOPLAY:
	handle_noplay(cf, spa, i, cmd);
	reply_ok(cf, cmd);
	break;

    case PLAY:
	handle_noplay(cf, spa, i, cmd);
	if (strcmp(codecs, "session") == 0) {
	    if (spa->codecs[i] == NULL) {
		reply_error(cf, cmd, ECODE_INVLARG_5);
		return 0;
	    }
	    codecs = spa->codecs[i];
	}
	if (playcount != 0 && handle_play(cf, spa, i, codecs, pname, playcount, cmd) != 0) {
	    reply_error(cf, cmd, ECODE_PLRFAIL);
	    return 0;
	}
	reply_ok(cf, cmd);
	break;

    case COPY:
	if (handle_copy(cf, spa, i, recording_name, record_single_file) != 0) {
            reply_error(cf, cmd, ECODE_CPYFAIL);
            return 0;
        }
	reply_ok(cf, cmd);
	break;

    case QUERY:
	rval = handle_query(cf, cmd, spa, i);
	if (rval != 0) {
	    reply_error(cf, cmd, rval);
	}
	break;

    case LOOKUP:
    case UPDATE:
        rtpp_command_ul_handle(cf, cmd, ulop, spa, i);
	break;

    default:
	/* Programmatic error, should not happen */
	abort();
    }

    return 0;
}
Esempio n. 20
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_setsockopt(zmq_drv_t *drv, const uint8_t* bytes, size_t size)
{
    uint8_t        n = *bytes;
    const uint8_t* p =  bytes+sizeof(uint8_t);

    ErlDrvTermData caller = driver_caller(drv->port);

    if (drv->terminating)
    {
        reply_error(drv->port, caller, ETERM);
        return;
    }

    zmq_sock_info* si = drv->get_socket_info(caller);

    if (!si)
    {
        reply_error(drv->port, caller, ENODEV);
        return;
    }

    zmqdrv_fprintf("setsockopt %p (setting %d options)\r\n", si->socket, (int)n);

    for (uint8_t j = 0; j < n; ++j)
    {
        int         opt        = *p++;
        uint64_t    optvallen  = *p++;
        const void* optval     =  p;

        switch (opt)
        {
            case ZMQ_HWM:           assert(optvallen == sizeof(uint64_t));break;
            case ZMQ_SWAP:          assert(optvallen == sizeof(int64_t)); break;
            case ZMQ_AFFINITY:      assert(optvallen == sizeof(uint64_t));break;
            case ZMQ_IDENTITY:      assert(optvallen <= 255);             break;
            case ZMQ_SUBSCRIBE:     break; // While the erlang layer limits this to 255, there is no zmq limit
            case ZMQ_UNSUBSCRIBE:   break; // While the erlang layer limits this to 255, there is no zmq limit
            case ZMQ_RATE:          assert(optvallen == sizeof(int64_t)); break;
            case ZMQ_RECOVERY_IVL:  assert(optvallen == sizeof(int64_t)); break;
            case ZMQ_RECOVERY_IVL_MSEC: assert(optvallen == sizeof(int64_t)); break;
            case ZMQ_MCAST_LOOP:    assert(optvallen == sizeof(int64_t)); break;
            case ZMQ_SNDBUF:        assert(optvallen == sizeof(uint64_t));break;
            case ZMQ_RCVBUF:        assert(optvallen == sizeof(uint64_t));break;
            case ZMQ_LINGER:        assert(optvallen == sizeof(int));     break; // Erlang layer assumes 32bit
            case ZMQ_RECONNECT_IVL: assert(optvallen == sizeof(int));     break; // Erlang layer assumes 32bit
            case ZMQ_RECONNECT_IVL_MAX: assert(optvallen == sizeof(int)); break; // Erlang layer assumes 32bit
            case ZMQ_BACKLOG:       assert(optvallen == sizeof(int));     break; // Erlang layer assumes 32bit
            default:                assert(true);
        }

        assert((size_t)((uint8_t*)(p + optvallen) - bytes) <= size);

        zmqdrv_fprintf("setsockopt %p (opt: %d)\r\n", si->socket, opt);

        if (0 != zmq_setsockopt(si->socket, opt, optval, optvallen))
        {
            reply_error(drv->port, caller, zmq_errno());
            return;
        }

        p += optvallen;
    }

    reply_ok(drv->port, caller);
}
Esempio n. 21
0
void
erl_lua_pushnil(lua_drv_t *driver_data, char *buf, int index)
{
  lua_pushnil(driver_data->L);
  reply_ok(driver_data);
}
Esempio n. 22
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
wrap_zmq_poll(zmq_drv_t *drv, const uint8_t* bytes, size_t size)
{
    uint32_t events = *bytes;

    assert(sizeof(uint8_t) == size);

    ErlDrvTermData caller = driver_caller(drv->port);

    if (drv->terminating)
    {
        reply_error(drv->port, caller, ETERM);
        return;
    }

    zmq_sock_info* si = drv->get_socket_info(caller);

    if (!si)
    {
        reply_error(drv->port, caller, ENODEV);
        return;
    }

    zmqdrv_fprintf("poll %p (events: %u)\r\n", si->socket, events);

    if (0 == events)
    {
        si->poll_events = 0;
        si->poll_caller = 0;

        if (si->busy && 0 == si->in_caller && 0 == si->out_caller)
        {
            driver_select(drv->port, si->fd, ERL_DRV_READ, 0);
            si->busy = false;
        }

        reply_ok(drv->port, caller);
        return;
    }

    if (si->busy)
    {
        reply_error(drv->port, caller, EBUSY);
        return;
    }

    assert((ZMQ_POLLIN|ZMQ_POLLOUT) & events);

    uint32_t revents;
    size_t revents_size = sizeof(revents);

    if (0 == zmq_getsockopt(si->socket, ZMQ_EVENTS, &revents, &revents_size))
    {
        // reply immediately; poll event notification happens out of band.
        reply_ok(drv->port, caller);

        revents &= events;

        if (0 != revents)
        {
            send_events(drv->port, caller, revents);
        }
        else
        {
            // No matching pending event, wait for one.
            zmqdrv_fprintf("poll %p blocking\r\n", si->socket);

            si->poll_events = events;
            si->poll_caller = caller;
            si->busy = true;
            driver_select(drv->port, si->fd, ERL_DRV_READ, 1);
        }
    }
    else
    {
        // EINVAL will only occur if our getsockopt call was invalid
        assert(EINVAL != zmq_errno());

        // else the problem was with the context/socket, and should be returned
        reply_error(drv->port, caller, zmq_errno());
    }
}
Esempio n. 23
0
void
erl_lua_newtable(lua_drv_t *driver_data, char *buf, int index)
{
  lua_newtable(driver_data->L);
  reply_ok(driver_data);
}
Esempio n. 24
0
int
handle_command(struct cfg *cf, int controlfd, double dtime)
{
    int len, argc, i, pidx, asymmetric;
    int external, pf, lidx, playcount, weak, tpf;
    int fds[2], lport, n;
    socklen_t rlen;
    char buf[1024 * 8];
    char *cp, *call_id, *from_tag, *to_tag, *addr, *port, *cookie;
    char *pname, *codecs, *recording_name, *t;
    struct rtpp_session *spa, *spb;
    char **ap, *argv[10];
    const char *rname, *errmsg;
    struct sockaddr *ia[2], *lia[2];
    struct sockaddr_storage raddr;
    int requested_nsamples;
    enum {DELETE, RECORD, PLAY, NOPLAY, COPY, UPDATE, LOOKUP, QUERY} op;
    int max_argc;
    char *socket_name_u, *notify_tag;
    struct sockaddr *local_addr;
    char c;

    requested_nsamples = -1;
    ia[0] = ia[1] = NULL;
    spa = spb = NULL;
    lia[0] = lia[1] = cf->bindaddr[0];
    lidx = 1;
    fds[0] = fds[1] = -1;
    recording_name = NULL;
    socket_name_u = notify_tag = NULL;
    local_addr = NULL;
    codecs = NULL;

    if (cf->umode == 0) {
	for (;;) {
	    len = read(controlfd, buf, sizeof(buf) - 1);
	    if (len != -1 || (errno != EAGAIN && errno != EINTR))
		break;
	    sched_yield();
	}
    } else {
	rlen = sizeof(raddr);
	len = recvfrom(controlfd, buf, sizeof(buf) - 1, 0,
	  sstosa(&raddr), &rlen);
    }
    if (len == -1) {
	if (errno != EAGAIN && errno != EINTR)
	    rtpp_log_ewrite(RTPP_LOG_ERR, cf->glog, "can't read from control socket");
	return -1;
    }
    buf[len] = '\0';

    rtpp_log_write(RTPP_LOG_DBUG, cf->glog, "received command \"%s\"", buf);

    cp = buf;
    argc = 0;
    memset(argv, 0, sizeof(argv));
    for (ap = argv; (*ap = rtpp_strsep(&cp, "\r\n\t ")) != NULL;)
	if (**ap != '\0') {
	    argc++;
	    if (++ap >= &argv[10])
		break;
	}
    cookie = NULL;
    if (argc < 1 || (cf->umode != 0 && argc < 2)) {
	rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	reply_error(cf, controlfd, &raddr, rlen, cookie, 0);
	return 0;
    }

    /* Stream communication mode doesn't use cookie */
    if (cf->umode != 0) {
	cookie = argv[0];
	for (i = 1; i < argc; i++)
	    argv[i - 1] = argv[i];
	argc--;
	argv[argc] = NULL;
    } else {
	cookie = NULL;
    }

    addr = port = NULL;
    switch (argv[0][0]) {
    case 'u':
    case 'U':
	/* U[opts] callid remote_ip remote_port from_tag [to_tag] */
	op = UPDATE;
	rname = "update/create";
	break;

    case 'l':
    case 'L':
	op = LOOKUP;
	rname = "lookup";
	break;

    case 'd':
    case 'D':
	op = DELETE;
	rname = "delete";
	break;

    case 'p':
    case 'P':
	/*
	 * P callid pname codecs from_tag to_tag
	 *
	 *   <codecs> could be either comma-separated list of supported
	 *   payload types or word "session" (without quotes), in which
	 *   case list saved on last session update will be used instead.
	 */
	op = PLAY;
	rname = "play";
	playcount = 1;
	pname = argv[2];
	codecs = argv[3];
	break;

    case 'r':
    case 'R':
	op = RECORD;
	rname = "record";
	break;

    case 'c':
    case 'C':
	op = COPY;
	rname = "copy";
	break;

    case 's':
    case 'S':
	op = NOPLAY;
	rname = "noplay";
	break;

    case 'v':
    case 'V':
	if (argv[0][1] == 'F' || argv[0][1] == 'f') {
	    int i, known;
	    /*
	     * Wait for protocol version datestamp and check whether we
	     * know it.
	     */
	    if (argc != 2 && argc != 3) {
		rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		reply_error(cf, controlfd, &raddr, rlen, cookie, 2);
		return 0;
	    }
	    /*
	     * Only list 20081224 protocol mod as supported if
	     * user actually enabled notification with -n
	     */
	    if (strcmp(argv[1], "20081224") == 0 &&
	      cf->timeout_handler.socket_name == NULL) {
		reply_number(cf, controlfd, &raddr, rlen, cookie, 0);
		return 0;
	    }
	    for (known = i = 0; proto_caps[i].pc_id != NULL; ++i) {
		if (!strcmp(argv[1], proto_caps[i].pc_id)) {
		    known = 1;
		    break;
		}
	    }
	    reply_number(cf, controlfd, &raddr, rlen, cookie, known);
	    return 0;
	}
	if (argc != 1 && argc != 2) {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 2);
	    return 0;
	}
	/* This returns base version. */
	reply_number(cf, controlfd, &raddr, rlen, cookie, CPROTOVER);
	return 0;

    case 'i':
    case 'I':
	if (cookie == NULL)
	    len = sprintf(buf, "sessions created: %llu\nactive sessions: %d\n"
	      "active streams: %d\n", cf->sessions_created,
	      cf->sessions_active, cf->nsessions / 2);
	else
	    len = sprintf(buf, "%s sessions created: %llu\nactive sessions: %d\n"
	      "active streams: %d\n", cookie, cf->sessions_created,
	      cf->sessions_active, cf->nsessions / 2);
	for (i = 1; i < cf->nsessions; i++) {
	    char addrs[4][256];

	    spa = cf->sessions[i];
	    if (spa == NULL || spa->sidx[0] != i)
		continue;
	    /* RTCP twin session */
	    if (spa->rtcp == NULL) {
		spb = spa->rtp;
		buf[len++] = '\t';
	    } else {
		spb = spa->rtcp;
		buf[len++] = '\t';
		buf[len++] = 'C';
		buf[len++] = ' ';
	    }

	    addr2char_r(spb->laddr[1], addrs[0], sizeof(addrs[0]));
	    if (spb->addr[1] == NULL) {
		strcpy(addrs[1], "NONE");
	    } else {
		sprintf(addrs[1], "%s:%d", addr2char(spb->addr[1]),
		  addr2port(spb->addr[1]));
	    }
	    addr2char_r(spb->laddr[0], addrs[2], sizeof(addrs[2]));
	    if (spb->addr[0] == NULL) {
		strcpy(addrs[3], "NONE");
	    } else {
		sprintf(addrs[3], "%s:%d", addr2char(spb->addr[0]),
		  addr2port(spb->addr[0]));
	    }

	    len += sprintf(buf + len,
	      "%s/%s: caller = %s:%d/%s, callee = %s:%d/%s, "
	      "stats = %lu/%lu/%lu/%lu, ttl = %d/%d\n",
	      spb->call_id, spb->tag, addrs[0], spb->ports[1], addrs[1],
	      addrs[2], spb->ports[0], addrs[3], spa->pcount[0], spa->pcount[1],
	      spa->pcount[2], spa->pcount[3], spb->ttl[0], spb->ttl[1]);
	    if (len + 512 > sizeof(buf)) {
		doreply(cf, controlfd, buf, len, &raddr, rlen);
		len = 0;
	    }
	}
	if (len > 0)
	    doreply(cf, controlfd, buf, len, &raddr, rlen);;
	return 0;
	break;

    case 'q':
    case 'Q':
	op = QUERY;
	rname = "query";
	break;

    case 'x':
    case 'X':
        /* Delete all active sessions */
        rtpp_log_write(RTPP_LOG_INFO, cf->glog, "deleting all active sessions");
        for (i = 1; i < cf->nsessions; i++) {
	    spa = cf->sessions[i];
	    if (spa == NULL || spa->sidx[0] != i)
		continue;
	    /* Skip RTCP twin session */
	    if (spa->rtcp != NULL) {
		remove_session(cf, spa);
	    }
        }
        reply_ok(cf, controlfd, &raddr, rlen, cookie);
        return 0;
        break;

    default:
	rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command");
	reply_error(cf, controlfd, &raddr, rlen, cookie, 3);
	return 0;
    }
    call_id = argv[1];
    if (op == UPDATE || op == LOOKUP || op == PLAY) {
	max_argc = (op == UPDATE ? 8 : 6);
	if (argc < 5 || argc > max_argc) {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 4);
	    return 0;
	}
	from_tag = argv[4];
	to_tag = argv[5];
	if (op == PLAY && argv[0][1] != '\0')
	    playcount = atoi(argv[0] + 1);
	if (op == UPDATE && argc > 6) {
	    socket_name_u = argv[6];
	    if (strncmp("unix:", socket_name_u, 5) == 0)
		socket_name_u += 5;
	    if (argc == 8) {
		notify_tag = argv[7];
		len = url_unquote((uint8_t *)notify_tag, strlen(notify_tag));
		if (len == -1) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
		      "command syntax error - invalid URL encoding");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 4);
		    return 0;
		}
		notify_tag[len] = '\0';
	    }
	}
    }
    if (op == COPY) {
	if (argc < 4 || argc > 5) {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
	    return 0;
	}
	recording_name = argv[2];
	from_tag = argv[3];
	to_tag = argv[4];
    }
    if (op == DELETE || op == RECORD || op == NOPLAY || op == QUERY) {
	if (argc < 3 || argc > 4) {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
	    return 0;
	}
	from_tag = argv[2];
	to_tag = argv[3];
    }
    if (op == DELETE || op == RECORD || op == COPY || op == NOPLAY) {
	/* D, R and S commands don't take any modifiers */
	if (argv[0][1] != '\0') {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
	    return 0;
	}
    }
    if (op == UPDATE || op == LOOKUP || op == DELETE) {
	addr = argv[2];
	port = argv[3];
	/* Process additional command modifiers */
	external = 1;
	/* In bridge mode all clients are assumed to be asymmetric */
	asymmetric = (cf->bmode != 0) ? 1 : 0;
	pf = AF_INET;
	weak = 0;
	for (cp = argv[0] + 1; *cp != '\0'; cp++) {
	    switch (*cp) {
	    case 'a':
	    case 'A':
		asymmetric = 1;
		break;

	    case 'e':
	    case 'E':
		if (lidx < 0) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		lia[lidx] = cf->bindaddr[1];
		lidx--;
		break;

	    case 'i':
	    case 'I':
		if (lidx < 0) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		lia[lidx] = cf->bindaddr[0];
		lidx--;
		break;

	    case '6':
		pf = AF_INET6;
		break;

	    case 's':
	    case 'S':
		asymmetric = 0;
		break;

	    case 'w':
	    case 'W':
		weak = 1;
		break;

	    case 'z':
	    case 'Z':
		requested_nsamples = (strtol(cp + 1, &cp, 10) / 10) * 80;
		if (requested_nsamples <= 0) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		cp--;
		break;

	    case 'c':
	    case 'C':
		cp += 1;
		for (t = cp; *cp != '\0'; cp++) {
		    if (!isdigit(*cp) && *cp != ',')
			break;
		}
		if (t == cp) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		codecs = alloca(cp - t + 1);
		memcpy(codecs, t, cp - t);
		codecs[cp - t] = '\0';
		cp--;
		break;

	    case 'l':
	    case 'L':
		len = extractaddr(cp + 1, &t, &cp, &tpf);
		if (len == -1) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		c = t[len];
		t[len] = '\0';
		local_addr = host2bindaddr(cf, t, tpf, &errmsg);
		if (local_addr == NULL) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
		      "invalid local address: %s: %s", t, errmsg);
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		t[len] = c;
		cp--;
		break;

	    case 'r':
	    case 'R':
		len = extractaddr(cp + 1, &t, &cp, &tpf);
		if (len == -1) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "command syntax error");
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		c = t[len];
		t[len] = '\0';
		local_addr = alloca(sizeof(struct sockaddr_storage));
		n = resolve(local_addr, tpf, t, SERVICE, AI_PASSIVE);
		if (n != 0) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
		      "invalid remote address: %s: %s", t, gai_strerror(n));
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		if (local4remote(cf, local_addr, satoss(local_addr)) == -1) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
		      "can't find local address for remote address: %s", t);
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		local_addr = addr2bindaddr(cf, local_addr, &errmsg);
		if (local_addr == NULL) {
		    rtpp_log_write(RTPP_LOG_ERR, cf->glog,
		      "invalid local address: %s", errmsg);
		    reply_error(cf, controlfd, &raddr, rlen, cookie, 1);
		    return 0;
		}
		t[len] = c;
		cp--;
		break;

	    default:
		rtpp_log_write(RTPP_LOG_ERR, cf->glog, "unknown command modifier `%c'",
		  *cp);
		break;
	    }
	}
	if (op != DELETE && addr != NULL && port != NULL && strlen(addr) >= 7) {
	    struct sockaddr_storage tia;

	    if ((n = resolve(sstosa(&tia), pf, addr, port,
	      AI_NUMERICHOST)) == 0) {
		if (!ishostnull(sstosa(&tia))) {
		    for (i = 0; i < 2; i++) {
			ia[i] = malloc(SS_LEN(&tia));
			if (ia[i] == NULL) {
			    handle_nomem(cf, controlfd, &raddr, rlen, cookie,
			      5, ia, fds, spa, spb);
			    return 0;
			}
			memcpy(ia[i], &tia, SS_LEN(&tia));
		    }
		    /* Set port for RTCP, will work both for IPv4 and IPv6 */
		    n = ntohs(satosin(ia[1])->sin_port);
		    satosin(ia[1])->sin_port = htons(n + 1);
		}
	    } else {
		rtpp_log_write(RTPP_LOG_ERR, cf->glog, "getaddrinfo: %s",
		  gai_strerror(n));
	    }
	}
    }

    /*
     * Record and delete need special handling since they apply to all
     * streams in the session.
     */
    switch (op) {
    case DELETE:
	i = handle_delete(cf, call_id, from_tag, to_tag, weak);
	break;

    case RECORD:
	i = handle_record(cf, call_id, from_tag, to_tag);
	break;

    default:
	i = find_stream(cf, call_id, from_tag, to_tag, &spa);
	if (i != -1 && op != UPDATE)
	    i = NOT(i);
	break;
    }

    if (i == -1 && op != UPDATE) {
	rtpp_log_write(RTPP_LOG_INFO, cf->glog,
	  "%s request failed: session %s, tags %s/%s not found", rname,
	  call_id, from_tag, to_tag != NULL ? to_tag : "NONE");
	if (op == LOOKUP) {
	    for (i = 0; i < 2; i++)
		if (ia[i] != NULL)
		    free(ia[i]);
	    reply_port(cf, controlfd, &raddr, rlen, cookie, 0, lia);
	    return 0;
	}
	reply_error(cf, controlfd, &raddr, rlen, cookie, 8);
	return 0;
    }

    switch (op) {
    case DELETE:
    case RECORD:
	reply_ok(cf, controlfd, &raddr, rlen, cookie);
	return 0;

    case NOPLAY:
	handle_noplay(cf, spa, i);
	reply_ok(cf, controlfd, &raddr, rlen, cookie);
	return 0;

    case PLAY:
	handle_noplay(cf, spa, i);
	if (strcmp(codecs, "session") == 0) {
	    if (spa->codecs[i] == NULL) {
		reply_error(cf, controlfd, &raddr, rlen, cookie, 6);
		return 0;
	    }
	    codecs = spa->codecs[i];
	}
	if (playcount != 0 && handle_play(cf, spa, i, codecs, pname, playcount) != 0) {
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 6);
	    return 0;
	}
	reply_ok(cf, controlfd, &raddr, rlen, cookie);
	return 0;

    case COPY:
	handle_copy(cf, spa, i, recording_name);
	reply_ok(cf, controlfd, &raddr, rlen, cookie);
	return 0;

    case QUERY:
	handle_query(cf, controlfd, &raddr, rlen, cookie, spa, i);
	return 0;

    case LOOKUP:
    case UPDATE:
	/* those are handled below */
	break;

    default:
	/* Programmatic error, should not happen */
	abort();
    }

    pidx = 1;
    lport = 0;
    if (i != -1) {
	assert(op == UPDATE || op == LOOKUP);
	if (spa->fds[i] == -1) {
	    if (local_addr != NULL) {
		spa->laddr[i] = local_addr;
	    }
	    if (create_listener(cf, spa->laddr[i], &lport, fds) == -1) {
		rtpp_log_write(RTPP_LOG_ERR, spa->log, "can't create listener");
		reply_error(cf, controlfd, &raddr, rlen, cookie, 7);
		return 0;
	    }
	    assert(spa->fds[i] == -1);
	    spa->fds[i] = fds[0];
	    assert(spa->rtcp->fds[i] == -1);
	    spa->rtcp->fds[i] = fds[1];
	    spa->ports[i] = lport;
	    spa->rtcp->ports[i] = lport + 1;
	    spa->complete = spa->rtcp->complete = 1;
	    append_session(cf, spa, i);
	    append_session(cf, spa->rtcp, i);
	}
	if (weak)
	    spa->weak[i] = 1;
	else if (op == UPDATE)
	    spa->strong = 1;
	lport = spa->ports[i];
	lia[0] = spa->laddr[i];
	pidx = (i == 0) ? 1 : 0;
	spa->ttl_mode = cf->ttl_mode;
	spa->ttl[0] = cf->max_ttl;
	spa->ttl[1] = cf->max_ttl;
	if (op == UPDATE) {
	    rtpp_log_write(RTPP_LOG_INFO, spa->log,
	      "adding %s flag to existing session, new=%d/%d/%d",
	      weak ? ( i ? "weak[1]" : "weak[0]" ) : "strong",
	      spa->strong, spa->weak[0], spa->weak[1]);
	}
	rtpp_log_write(RTPP_LOG_INFO, spa->log,
	  "lookup on ports %d/%d, session timer restarted", spa->ports[0],
	  spa->ports[1]);
    } else {
	assert(op == UPDATE);
	rtpp_log_write(RTPP_LOG_INFO, cf->glog,
	  "new session %s, tag %s requested, type %s",
	  call_id, from_tag, weak ? "weak" : "strong");

	if (local_addr != NULL) {
	    lia[0] = lia[1] = local_addr;
	    if (lia[0] == NULL) {
		rtpp_log_write(RTPP_LOG_ERR, spa->log,
		  "can't create listener: %s", t);
		reply_error(cf, controlfd, &raddr, rlen, cookie, 10);
		return 0;
	    }
	}
	if (create_listener(cf, lia[0], &lport, fds) == -1) {
	    rtpp_log_write(RTPP_LOG_ERR, cf->glog, "can't create listener");
	    reply_error(cf, controlfd, &raddr, rlen, cookie, 10);
	    return 0;
	}

	/*
	 * Session creation. If creation is requested with weak flag,
	 * set weak[0].
	 */
	spa = malloc(sizeof(*spa));
	if (spa == NULL) {
	    handle_nomem(cf, controlfd, &raddr, rlen, cookie, 11, ia,
	      fds, spa, spb);
	    return 0;
	}
	/* spb is RTCP twin session for this one. */
	spb = malloc(sizeof(*spb));
	if (spb == NULL) {
	    handle_nomem(cf, controlfd, &raddr, rlen, cookie, 12, ia,
	      fds, spa, spb);
	    return 0;
	}
	memset(spa, 0, sizeof(*spa));
	memset(spb, 0, sizeof(*spb));
	for (i = 0; i < 2; i++) {
	    spa->fds[i] = spb->fds[i] = -1;
	    spa->last_update[i] = 0;
	    spb->last_update[i] = 0;
	}
	spa->call_id = strdup(call_id);
	if (spa->call_id == NULL) {
	    handle_nomem(cf, controlfd, &raddr, rlen, cookie, 13, ia,
	      fds, spa, spb);
	    return 0;
	}
	spb->call_id = spa->call_id;
	spa->tag = strdup(from_tag);
	if (spa->tag == NULL) {
	    handle_nomem(cf, controlfd, &raddr, rlen, cookie, 14, ia,
	      fds, spa, spb);
	    return 0;
	}
	spb->tag = spa->tag;
	for (i = 0; i < 2; i++) {
	    spa->rrcs[i] = NULL;
	    spb->rrcs[i] = NULL;
	    spa->laddr[i] = lia[i];
	    spb->laddr[i] = lia[i];
	}
	spa->strong = spa->weak[0] = spa->weak[1] = 0;
	if (weak)
	    spa->weak[0] = 1;
	else
	    spa->strong = 1;
	assert(spa->fds[0] == -1);
	spa->fds[0] = fds[0];
	assert(spb->fds[0] == -1);
	spb->fds[0] = fds[1];
	spa->ports[0] = lport;
	spb->ports[0] = lport + 1;
	spa->ttl[0] = cf->max_ttl;
	spa->ttl[1] = cf->max_ttl;
	spb->ttl[0] = -1;
	spb->ttl[1] = -1;
	spa->log = rtpp_log_open(cf, "rtpproxy", spa->call_id, 0);
	spb->log = spa->log;
	spa->rtcp = spb;
	spb->rtcp = NULL;
	spa->rtp = NULL;
	spb->rtp = spa;
	spa->sridx = spb->sridx = -1;

	append_session(cf, spa, 0);
	append_session(cf, spa, 1);
	append_session(cf, spb, 0);
	append_session(cf, spb, 1);

	hash_table_append(cf, spa);

	cf->sessions_created++;
	cf->sessions_active++;
	/*
	 * Each session can consume up to 5 open file descriptors (2 RTP,
	 * 2 RTCP and 1 logging) so that warn user when he is likely to
	 * exceed 80% mark on hard limit.
	 */
	if (cf->sessions_active > (cf->nofile_limit.rlim_max * 80 / (100 * 5)) &&
	  cf->nofile_limit_warned == 0) {
	    cf->nofile_limit_warned = 1;
	    rtpp_log_write(RTPP_LOG_WARN, cf->glog, "passed 80%% "
	      "threshold on the open file descriptors limit (%d), "
	      "consider increasing the limit using -L command line "
	      "option", (int)cf->nofile_limit.rlim_max);
	}

	rtpp_log_write(RTPP_LOG_INFO, spa->log, "new session on a port %d created, "
	  "tag %s", lport, from_tag);
	if (cf->record_all != 0) {
	    handle_copy(cf, spa, 0, NULL);
	    handle_copy(cf, spa, 1, NULL);
	}
    }

    if (op == UPDATE) {
	if (cf->timeout_handler.socket_name == NULL && socket_name_u != NULL)
	    rtpp_log_write(RTPP_LOG_ERR, spa->log, "must permit notification socket with -n");
	if (spa->timeout_data.notify_tag != NULL) {
	    free(spa->timeout_data.notify_tag);
	    spa->timeout_data.notify_tag = NULL;
	}
	if (cf->timeout_handler.socket_name != NULL && socket_name_u != NULL) {
	    if (strcmp(cf->timeout_handler.socket_name, socket_name_u) != 0) {
		rtpp_log_write(RTPP_LOG_ERR, spa->log, "invalid socket name %s", socket_name_u);
		socket_name_u = NULL;
	    } else {
		rtpp_log_write(RTPP_LOG_INFO, spa->log, "setting timeout handler");
		spa->timeout_data.handler = &cf->timeout_handler;
		spa->timeout_data.notify_tag = strdup(notify_tag);
	    }
	} else if (socket_name_u == NULL && spa->timeout_data.handler != NULL) {
	    spa->timeout_data.handler = NULL;
	    rtpp_log_write(RTPP_LOG_INFO, spa->log, "disabling timeout handler");
	}
    }

    if (ia[0] != NULL && ia[1] != NULL) {
        if (spa->addr[pidx] != NULL)
            spa->last_update[pidx] = dtime;
        if (spa->rtcp->addr[pidx] != NULL)
            spa->rtcp->last_update[pidx] = dtime;
	/*
	 * Unless the address provided by client historically
	 * cannot be trusted and address is different from one
	 * that we recorded update it.
	 */
	if (spa->untrusted_addr[pidx] == 0 && !(spa->addr[pidx] != NULL &&
	  SA_LEN(ia[0]) == SA_LEN(spa->addr[pidx]) &&
	  memcmp(ia[0], spa->addr[pidx], SA_LEN(ia[0])) == 0)) {
	    rtpp_log_write(RTPP_LOG_INFO, spa->log, "pre-filling %s's address "
	      "with %s:%s", (pidx == 0) ? "callee" : "caller", addr, port);
	    if (spa->addr[pidx] != NULL) {
	        if (spa->canupdate[pidx] == 0) {
	            if (spa->prev_addr[pidx] != NULL)
	                 free(spa->prev_addr[pidx]);
	            spa->prev_addr[pidx] = spa->addr[pidx];
	        } else {
		    free(spa->addr[pidx]);
		}
	    }
	    spa->addr[pidx] = ia[0];
	    ia[0] = NULL;
	}
	if (spa->rtcp->untrusted_addr[pidx] == 0 && !(spa->rtcp->addr[pidx] != NULL &&
	  SA_LEN(ia[1]) == SA_LEN(spa->rtcp->addr[pidx]) &&
	  memcmp(ia[1], spa->rtcp->addr[pidx], SA_LEN(ia[1])) == 0)) {
	    if (spa->rtcp->addr[pidx] != NULL) {
	        if (spa->rtcp->canupdate[pidx] == 0) {
	            if (spa->rtcp->prev_addr[pidx] != NULL)
	                free(spa->rtcp->prev_addr[pidx]);
	            spa->rtcp->prev_addr[pidx] = spa->rtcp->addr[pidx];
	        } else {
		    free(spa->rtcp->addr[pidx]);
		}
	    }
	    spa->rtcp->addr[pidx] = ia[1];
	    ia[1] = NULL;
	}
    }
    spa->asymmetric[pidx] = spa->rtcp->asymmetric[pidx] = asymmetric;
    spa->canupdate[pidx] = spa->rtcp->canupdate[pidx] = NOT(asymmetric);
    if (spa->codecs[pidx] != NULL) {
	free(spa->codecs[pidx]);
	spa->codecs[pidx] = NULL;
    }
    if (codecs != NULL)
	spa->codecs[pidx] = strdup(codecs);
    if (requested_nsamples > 0) {
	rtpp_log_write(RTPP_LOG_INFO, spa->log, "RTP packets from %s "
	  "will be resized to %d milliseconds",
	  (pidx == 0) ? "callee" : "caller", requested_nsamples / 8);
    } else if (spa->resizers[pidx].output_nsamples > 0) {
	  rtpp_log_write(RTPP_LOG_INFO, spa->log, "Resizing of RTP "
	  "packets from %s has been disabled",
	  (pidx == 0) ? "callee" : "caller");
    }
    spa->resizers[pidx].output_nsamples = requested_nsamples;

    for (i = 0; i < 2; i++)
	if (ia[i] != NULL)
	    free(ia[i]);

    assert(lport != 0);
    reply_port(cf, controlfd, &raddr, rlen, cookie, lport, lia);
    return 0;
}
Esempio n. 25
0
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
static void
zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event)
{
    zmq_drv_t *drv = reinterpret_cast<zmq_drv_t*>(handle);
    zmq_sock_info *si = drv->get_socket_info(event);

    // I'm not sure if a race condition could develop here or not
    // So let's assert and see if we ever hit it.  Hopefully not.
    assert(!drv->terminating);
    assert(NULL != si);
    assert(si->busy);

    // unregister event with erlang vm while we work with the socket
    driver_select(drv->port, si->fd, ERL_DRV_READ, 0);

    // Finish blocking recv request if input is ready
    if (si->in_caller)
    {
        zmq_msg_t msg;
        zmq_msg_init(&msg);

        if (0 == zmq_recv(si->socket, &msg, si->in_flags|ZMQ_NOBLOCK))
        {
            // Unblock the waiting caller's pid by returning result
            reply_ok_binary(drv->port, si->in_caller, zmq_msg_data(&msg), zmq_msg_size(&msg));
            si->in_caller = 0;
            si->in_flags = 0;
        }
        else if (zmq_errno() != EAGAIN)
        {
            // Unblock the waiting caller's pid by returning error
            reply_error(drv->port, si->in_caller, zmq_errno());
            si->in_caller = 0;
            si->in_flags = 0;
        }
        // else no input was ready, continue waiting

        zmq_msg_close(&msg);
    }

    // Finish blocking send request if able
    if (si->out_caller)
    {
        if (0 == zmq_send(si->socket, &si->out_msg, si->out_flags|ZMQ_NOBLOCK))
        {
            // Unblock the waiting caller's pid by returning result
            reply_ok(drv->port, si->out_caller);
            si->out_caller = 0;
            si->out_flags = 0;
            zmq_msg_close(&si->out_msg);
        }
        else if (zmq_errno() != EAGAIN)
        {
            // Unblock the waiting caller's pid by returning error
            reply_error(drv->port, si->out_caller, zmq_errno());
            si->out_caller = 0;
            si->out_flags = 0;
            zmq_msg_close(&si->out_msg);
        }
        // else not able to send, continue waiting
    }

    // Finish poll request if events available
    if (si->poll_caller)
    {
        uint32_t revents = 0;
        size_t revents_size = sizeof(revents);

        if (0 == zmq_getsockopt(si->socket, ZMQ_EVENTS, &revents, &revents_size))
        {
            revents &= si->poll_events;

            if (0 != revents)
            {
                send_events(drv->port, si->poll_caller, revents);
                si->poll_caller = 0;
                si->poll_events = 0;
            }
            // else no requested events pending, continue waiting
        }
        else
        {
            // EINVAL will only occur if our getsockopt call was invalid
            assert(EINVAL != zmq_errno());

            // send out of band event error notification
            send_events(drv->port, si->poll_caller, (uint32_t)ZMQ_POLLERR);
            si->poll_caller = 0;
            si->poll_events = 0;
        }
    }

    // reregister event with erlang vm if any pending operations exist
    if (si->poll_caller || si->in_caller || si->out_caller)
    {
        driver_select(drv->port, si->fd, ERL_DRV_READ, 1);
    }
    else
    {
        si->busy = false;
    }
}
Esempio n. 26
0
static void from_erlang(ErlDrvData drv_data, char* buf, int n)
{
    ErlDrvPort port = (ErlDrvPort) drv_data;
    char *tp;
    int i, j;
    int slavefd, fd, afd;

    switch (*buf++) {
    case 'L':   /*  Bind and listen to new af_unix socket to path */
	fd = fd_listen_path(port, buf);

	if ((i = find_free_slave()) == -1) {
	    reply_err(port);
	    return;
	}
	
	if (fd > 0) {
	    if ((tp = (char*) driver_alloc(n)) != NULL) {
		slaves[i].type = LISTEN;
		slaves[i].path = tp;
		strcpy(slaves[i].path, buf);
		slaves[i].fd = fd;
		reply_int(port, fd);
		return;
	    }
	}
	reply_err(port);
	return;

    case 'A':  /* accept the af_unix path socket */
	fd = get_int32(buf);

	if ((i = find_slave(fd)) == -1) {
	    reply_err(port);
	    return;
	}
	if ((j = find_free_slave()) == -1) {
	    reply_err(port);
	    return;
	}
	tp = slaves[i].path;	
	if ((afd = fd_accept_path(port, fd, tp)) > 0) {

	    slaves[j].path = tp;
	    slaves[j].type = ACCEPT;
	    slaves[j].fd = afd;
	    reply_int(port, afd);
	}
	else {
	    reply_err(port);
	}
	return;
    case 'l':
    case 'S':   /* send a socket to slave */
	fd = get_int32(buf);
	slavefd = get_int32(buf+4);

	if (send_fd(&fd, slavefd) == -1) 
	    reply_err(port);
	else {
	    reply_ok(port);
	}
	return;
    case 'C':  /* close */
	fd = get_int32(buf);
	if ((i = find_slave(fd)) == -1) {
	    reply_err(port);
	    return;
	}
	if (slaves[i].path && slaves[i].type == LISTEN)
	    driver_free(slaves[i].path);
	slaves[i].path = NULL;
	close(fd);
	slaves[i].fd = -1;
	reply_ok(port);
	return;
    }
    return;
}