Beispiel #1
0
static void
zmqdrv_bind(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary* bin   = ev->binv[1];
    char*         bytes = bin->orig_bytes;
    uint16_t      size  = bin->orig_size - 5;
    uint32_t      idx   = ntohl(*(uint32_t*)(bytes+1));
    void*         s     = drv->get_zmq_socket(idx);
    char          addr[512];

    if (size > sizeof(addr) - 1) {
        zmqdrv_error_code(drv, E2BIG);
        return;
    }

    memcpy(addr, bytes + 5, size);
    addr[size] = '\0';

    if (idx > drv->zmq_socket_count || !s) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    } else if (addr[0] == '\0') {
        zmqdrv_error_code(drv, EINVAL);
        return;
    }

    if (zmq_bind(s, addr) < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
}
Beispiel #2
0
static void
zmqdrv_send(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    zmq_sock_info* si    = drv->get_socket_info(idx);
    uint32_t       flags = ntohl(*(uint32_t*)(bytes+5));
    void*          data  = (void *)(bytes + 9);
    size_t         size  = bin->orig_size - 9;

    if (idx > drv->zmq_socket_count || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

#ifdef ZMQDRV_DEBUG
    uint32_t events;
    size_t events_size = sizeof(events);
    zmq_getsockopt(si->socket, ZMQ_EVENTS, &events, &events_size);
    zmqdrv_fprintf("sending %p [idx=%d] %lu bytes (events=%d)\r\n", si->socket, idx, size, events);
#endif

    if (si->out_caller != 0) {
        // There's still an unwritten message pending
        zmqdrv_error_code(drv, EBUSY);
        return;
    }

    // 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, size, &zmq_free_binary, bin)) {
        zmqdrv_error_code(drv, zmq_errno());
        driver_binary_dec_refc(bin);
        return;
    }

    if (zmq_send(si->socket, &si->out_msg, flags | ZMQ_NOBLOCK) == 0) {
        zmqdrv_ok(drv);
        zmqdrv_ready_input((ErlDrvData)drv, (ErlDrvEvent)si->fd);
    } else {
        int e = zmq_errno();
        if (e == EAGAIN) {
            // No msg returned to caller - make him wait until async
            // send succeeds
            si->out_caller = driver_caller(drv->port);
            return;
        }
        zmqdrv_error_code(drv, e);
    }
    zmq_msg_close(&si->out_msg);
}
Beispiel #3
0
static void 
zmqdrv_setsockopt(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    zmq_sock_info* si    = drv->get_socket_info(idx);
    uint8_t        n     = *(uint8_t*)(bytes+sizeof(idx)+1);
    char*          p     = bytes + 1 + sizeof(idx) + 1;

    if (idx > drv->zmq_socket_count || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

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

    for (uint8_t j=0; j < n; ++j) {
        unsigned char option = *p++;
        uint64_t optvallen   = *p++;
        void*    optval      = p;

        switch (option) {
            case ZMQ_HWM:           assert(optvallen == 8);  break;
            case ZMQ_SWAP:          assert(optvallen == 8);  break;
            case ZMQ_AFFINITY:      assert(optvallen == 8);  break;
            case ZMQ_IDENTITY:      assert(optvallen < 256); break;
            case ZMQ_SUBSCRIBE:     assert(optvallen < 256); break;
            case ZMQ_UNSUBSCRIBE:   assert(optvallen < 256); break;
            case ZMQ_RATE:          assert(optvallen == 8);  break;
            case ZMQ_RECOVERY_IVL:  assert(optvallen == 8);  break;
            case ZMQ_MCAST_LOOP:    assert(optvallen == 8);  break;
            case ZMQ_SNDBUF:        assert(optvallen == 8);  break;
            case ZMQ_RCVBUF:        assert(optvallen == 8);  break;
            case ZMQ_ACTIVE:        assert(optvallen == 1);  break;
        }

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

        if (option == ZMQ_ACTIVE)
            si->active_mode = *(char*)optval;
        else if (zmq_setsockopt(si->socket, option, optval, optvallen) < 0) {
            zmqdrv_error_code(drv, zmq_errno());
            return;
        }

        p += optvallen;
    }

    zmqdrv_ok(drv);
}
Beispiel #4
0
static void 
zmqdrv_getsockopt(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    void*          s     = drv->get_zmq_socket(idx);
    uint32_t       opt   = ntohl (*(uint32_t*)(bytes+sizeof(idx)+1));

    if (opt == ZMQ_RCVMORE) {
        int64_t val;
        size_t valsz = sizeof (val);
        if (zmq_getsockopt (s, opt, &val, &valsz) < 0) {
            zmqdrv_error_code(drv, zmq_errno());
            return;
        }

        ErlDrvTermData spec[] = {
            ERL_DRV_ATOM,  am_zok,
            ERL_DRV_ATOM, (val ? am_true : am_false),
            ERL_DRV_TUPLE, 2};
        driver_send_term(drv->port, driver_caller(drv->port), spec, sizeof(spec)/sizeof(spec[0]));
        return;
    }

    zmqdrv_error(drv, "Not implemented");
}
Beispiel #5
0
static void
zmqdrv_recv(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*  bin   = ev->binv[1];
    char*          bytes = bin->orig_bytes;
    uint32_t       idx   = ntohl(*(uint32_t*)(bytes+1));
    zmq_sock_info* si    = drv->get_socket_info(idx);

    if (idx > drv->zmq_socket_count || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    if (si->active_mode) {
        zmqdrv_error_code(drv, EINVAL);
        return;
    }

    if (si->in_caller != 0) {
        // Previous recv() call in passive mode didn't complete.
        // The owner must be blocked waiting for result.
        zmqdrv_error_code(drv, EBUSY);
        return;
    }

    uint32_t events;
    size_t events_size = sizeof(events);
    zmq_getsockopt(si->socket, ZMQ_EVENTS, &events, &events_size);

    if (events == 0)
        si->in_caller = driver_caller(drv->port);
    else {
        msg_t msg;

        if (zmq_recv(si->socket, &msg, ZMQ_NOBLOCK) == 0)
            zmqdrv_ok_binary(drv, driver_caller(drv->port), zmq_msg_data(&msg), zmq_msg_size(&msg));
        else if (zmq_errno() == EAGAIN) {
            // No input available. Make the caller wait by not returning result
            si->in_caller = driver_caller(drv->port);
        } else
            zmqdrv_error_code(drv, zmq_errno());
    }
}
Beispiel #6
0
static void
zmqdrv_term(zmq_drv_t *drv, ErlIOVec *ev)
{
    if (!drv->zmq_context) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    zmqdrv_fprintf("calling zmq_term(context) ...\r\n");
    int rc = zmq_term(drv->zmq_context);
    zmqdrv_fprintf("terminated zmq context\r\n");

    if (rc < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
    drv->zmq_context = NULL;
}
Beispiel #7
0
static void
zmqdrv_init(zmq_drv_t *drv, ErlIOVec *ev)
{
    /* 
     * FIXME 
     * Use ei_decode_* to decode input from erlang VM.
     * This stuff is not documented anywhere, for now 
     * binary ErlIOVec is decoded by poking in iov struct.
     * 
     * Serge: Dhammika, ei_decode can only be used to decode
     * external binary format in the "output" callback function.
     * It's not suitable for using inside "outputv" body that
     * operates on I/O vectors unless you use term_to_binary/1
     * call to explicitely convert a term to external binary format.
     */

    uint32_t io_threads; 

    ErlDrvBinary* input = ev->binv[1];
    char* bytes = input->orig_bytes;
    io_threads  = ntohl(*(uint32_t *)(bytes + 1));

    zmqdrv_fprintf("iothreads = %u\r\n", io_threads);

    if (drv->zmq_context) {
        zmqdrv_error_code(drv, EBUSY);
        return;
    }
    
    drv->zmq_context = (void *)zmq_init(io_threads);

    if (!drv->zmq_context) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    zmqdrv_ok(drv);
}
Beispiel #8
0
static void
zmqdrv_close(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary* bin   = ev->binv[1];
    char*         bytes = bin->orig_bytes;
    uint32_t      idx   = ntohl(*(uint32_t*)(bytes+1));

    if (idx > drv->zmq_socket_count) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

    int ret = drv->del_socket(idx);

    zmqdrv_fprintf("close [idx=%d] -> %d\r\n", idx, ret);

    if (ret < 0) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }
    
    zmqdrv_ok(drv);
}
Beispiel #9
0
static void
zmqdrv_socket(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary* bin   = ev->binv[1];
    char*         bytes = bin->orig_bytes;
    int           type  = *(bytes + 1);

    void* s = zmq_socket(drv->zmq_context, type);
    if (!s) {
        zmqdrv_error_code(drv, zmq_errno());
        return;
    }

    int sig_fd;
    size_t sig_size = sizeof(sig_fd);
    zmq_getsockopt(s, ZMQ_FD, &sig_fd, &sig_size);

    if (sig_fd < 0) {
        zmqdrv_error(drv, "Invalid signaler");
        return;
    }

    // Register a new socket handle in order to avoid
    // passing actual address of socket to Erlang.  This
    // way it's more safe and also portable between 32 and
    // 64 bit OS's.
    uint32_t n = ++drv->zmq_socket_count;

    zmq_sock_info* zsi = new zmq_sock_info(s, n, driver_caller(drv->port), sig_fd);
    if (!zsi) {
        driver_failure_posix(drv->port, ENOMEM);
        return;
    }

    drv->add_socket(zsi);

    zmqdrv_fprintf("socket %p [idx=%d] owner=%ld\r\n", s, n, zsi->owner);

    ErlDrvTermData spec[] = {ERL_DRV_ATOM,  am_zok,
                             ERL_DRV_UINT,  n,
                             ERL_DRV_TUPLE, 2};
    driver_send_term(drv->port, zsi->owner, spec, sizeof(spec)/sizeof(spec[0]));
}
Beispiel #10
0
static void 
zmqdrv_getsockopt(zmq_drv_t *drv, ErlIOVec *ev)
{
    ErlDrvBinary*   bin   = ev->binv[1];
    char*           bytes = bin->orig_bytes;
    uint32_t        idx   = ntohl(*(uint32_t*)(bytes+1));
    void*           s     = drv->get_zmq_socket(idx);
    zmq_sock_info*  si    = drv->get_socket_info(idx);
    uint32_t        opt   = ntohl (*(uint32_t*)(bytes+sizeof(idx)+1));
    union {
        uint8_t  a[255];
        uint64_t ui64;
        int64_t  i64;
        int      i;
        uint32_t ui;
    } val;
    size_t vallen;

    if (idx > drv->zmq_socket_count || !s || !si) {
        zmqdrv_error_code(drv, ENODEV);
        return;
    }

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

    switch (opt) {
        case ZMQ_AFFINITY:
            vallen = sizeof(uint64_t);
            if (zmq_getsockopt(s, opt, &val.ui64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.ui64);
            break;
        case ZMQ_BACKLOG:
            vallen = sizeof(int);
            if (zmq_getsockopt(s, opt, &val.i, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i);
            break;
        case ZMQ_EVENTS:
            vallen = sizeof(uint32_t);
            if (zmq_getsockopt(s, opt, &val.ui, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.ui);
            break;
        case ZMQ_FD:
            vallen = sizeof(int);
            if (zmq_getsockopt(s, opt, &val.i, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i);
            break;
        case ZMQ_HWM:
            vallen = sizeof(uint64_t);
            if (zmq_getsockopt(s, opt, &val.ui64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.ui64);
            break;
        case ZMQ_IDENTITY:
            vallen = sizeof(val);
            if (zmq_getsockopt(s, opt, val.a, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_binary(drv, driver_caller(drv->port), val.a, vallen);
            break;
        case ZMQ_LINGER:
            vallen = sizeof(int);
            if (zmq_getsockopt(s, opt, &val.i, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_bool(drv, driver_caller(drv->port), !!val.i);
            break;
        case ZMQ_MCAST_LOOP:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_bool(drv, driver_caller(drv->port), !!val.i64);
            break;
        case ZMQ_RATE:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i64);
            break;
        case ZMQ_RCVBUF:
            vallen = sizeof(uint64_t);
            if (zmq_getsockopt(s, opt, &val.ui64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.ui64);
            break;
        case ZMQ_RCVMORE:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_bool(drv, driver_caller(drv->port), !!val.i64);
            break;
        case ZMQ_RECONNECT_IVL:
            vallen = sizeof(int);
            if (zmq_getsockopt(s, opt, &val.i, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i);
            break;
        case ZMQ_RECOVERY_IVL:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i64);
            break;
        case ZMQ_RECOVERY_IVL_MSEC:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i64);
            break;
        case ZMQ_SNDBUF:
            vallen = sizeof(uint64_t);
            if (zmq_getsockopt(s, opt, &val.ui64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.ui64);
            break;
        case ZMQ_SWAP:
            vallen = sizeof(int64_t);
            if (zmq_getsockopt(s, opt, &val.i64, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i64);
            break;
        case ZMQ_TYPE:
            vallen = sizeof(int);
            if (zmq_getsockopt(s, opt, &val.i, &vallen) < 0)
                zmqdrv_error_code(drv, zmq_errno());
            zmqdrv_ok_int64(drv, driver_caller(drv->port), val.i);
            break;
        case ZMQ_ACTIVE:
            zmqdrv_ok_atom(drv, driver_caller(drv->port),
                           (si->active_mode == 1 ? am_true :
                            (si->active_mode == 2 ? am_parts :
                             am_false)));
            break;
        default:
            zmqdrv_error(drv, "Option not implemented!");
            return;
    }
}