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); }
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); }
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); }
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"); }
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()); } }
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; }
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); }
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); }
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])); }
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; } }