//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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)); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void zmqdrv_process_exit(ErlDrvData handle, ErlDrvMonitor* monitor) { zmq_drv_t* drv = reinterpret_cast<zmq_drv_t*>(handle); ErlDrvTermData pid = driver_get_monitored_process(drv->port, monitor); zmqdrv_fprintf("detected death of %lu process\r\n", pid); zmq_sock_info* si = drv->get_socket_info(pid); assert(NULL != si); zmqdrv_fprintf("force 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(pid); drv->zmq_fd_socket.erase(si->fd); //zmq_close(Socket) is called in ~zmq_sock_info delete si; }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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); }
zmq_drv_t::~zmq_drv_t() { for (zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.begin(); it != zmq_pid_sockets.end(); ++it) driver_demonitor_process(port, &it->second.monitor); for (zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.begin(); it != zmq_fd_sockets.end(); ++it) driver_select(port, (ErlDrvEvent)it->first, ERL_DRV_READ, 0); for (zmq_sock_info *it=zmq_sock_infos, *next=(it ? it->next : NULL); it; it = next) { next = it->next; delete (&*it); } zmq_sockets.clear(); zmq_idxs.clear(); zmq_pid_sockets.clear(); zmq_fd_sockets.clear(); if (zmq_context) { zmqdrv_fprintf("calling zmq_term(context) ...\r\n"); zmq_term(zmq_context); zmqdrv_fprintf("terminated zmq context\r\n"); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void zmqdrv_stop(ErlDrvData handle) { zmqdrv_fprintf("zmq port driver stopping\r\n"); delete reinterpret_cast<zmq_drv_t*>(handle); zmqdrv_fprintf("zmq port driver stopped\r\n"); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_recv(zmq_drv_t *drv, const uint8_t* bytes, size_t size) { int flags = *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; } assert(0 == si->in_caller); zmqdrv_fprintf("recv %p (flags: %d)\r\n", si->socket, flags); zmq_msg_t msg; zmq_msg_init(&msg); if (0 == zmq_recv(si->socket, &msg, flags|ZMQ_NOBLOCK)) { reply_ok_binary(drv->port, caller, zmq_msg_data(&msg), zmq_msg_size(&msg)); } else if (ZMQ_NOBLOCK != (ZMQ_NOBLOCK & flags) && EAGAIN == zmq_errno()) { // Caller requested blocking recv // No input available. Make the caller wait by not returning result zmqdrv_fprintf("recv %p blocking\r\n", si->socket); si->in_flags = flags; si->in_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(&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 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); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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); }
int zmq_drv_t::del_socket(uint32_t idx) { zmq_sock_info* s; int ret = -1; zmq_idx_socket_map_t::iterator it = zmq_sockets.find(idx); if (it == zmq_sockets.end()) { zmqdrv_fprintf("warning: socket info not found for idx %d\r\n", idx); return ret; } s = it->second; s->unlink(); if (s == zmq_sock_infos) zmq_sock_infos = s->next; zmq_sockets.erase(idx); zmq_idxs.erase(s->socket); { // Remove the socket from a list of sockets owned by pid. // If this was the last socket, demonitor pid. zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.find(s->owner); if (it != zmq_pid_sockets.end()) { it->second.sockets.erase(s); if (it->second.sockets.empty()) { driver_demonitor_process(port, &it->second.monitor); zmq_pid_sockets.erase(it); } } } { zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.find(s->fd); if (it != zmq_fd_sockets.end()) { it->second.erase(s); if (it->second.empty()) { zmq_fd_sockets.erase(it->first); driver_select(port, (ErlDrvEvent)it->first, ERL_DRV_READ, 0); zmqdrv_fprintf("unregistered sig_fd(%d) with VM\r\n", it->first); } } } delete s; return 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)); }
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_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); }
// Called when an Erlang process owning sockets died. // Perform cleanup of orphan sockets owned by pid. static void zmqdrv_process_exit(ErlDrvData handle, ErlDrvMonitor* monitor) { zmq_drv_t* drv = (zmq_drv_t *)handle; ErlDrvTermData pid = driver_get_monitored_process(drv->port, monitor); zmqdrv_fprintf("detected death of %lu process\r\n", pid); driver_demonitor_process(drv->port, monitor); // Walk through the list of sockets and close the ones // owned by pid. zmq_pid_sockets_map_t::iterator it=drv->zmq_pid_sockets.find(pid); if (it != drv->zmq_pid_sockets.end()) { zmqdrv_fprintf("pid %lu has %lu sockets to be closed\r\n", pid, it->second.sockets.size()); for(zmq_sock_set_t::iterator sit = it->second.sockets.begin(); sit != it->second.sockets.end(); ++sit) drv->del_socket((*sit)->idx); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void zmqdrv_outputv(ErlDrvData handle, ErlIOVec *ev) { zmq_drv_t* drv = reinterpret_cast<zmq_drv_t*>(handle); ErlDrvBinary* bin = ev->binv[1]; enum driver_commands cmd = (enum driver_commands)bin->orig_bytes[0]; uint8_t* bytes = (uint8_t*)&bin->orig_bytes[1]; assert(1 <= bin->orig_size); zmqdrv_fprintf("driver got command %d on thread %p\r\n", (int)cmd, erl_drv_thread_self()); switch (cmd) { case ZMQ_INIT: wrap_zmq_init(drv, bytes, bin->orig_size - 1); break; case ZMQ_TERM: wrap_zmq_term(drv); break; case ZMQ_SOCKET: wrap_zmq_socket(drv, bytes, bin->orig_size - 1); break; case ZMQ_CLOSE: wrap_zmq_close(drv); break; case ZMQ_SETSOCKOPT: wrap_zmq_setsockopt(drv, bytes, bin->orig_size - 1); break; case ZMQ_GETSOCKOPT: wrap_zmq_getsockopt(drv, bytes, bin->orig_size - 1); break; case ZMQ_BIND: wrap_zmq_bind(drv, bytes, bin->orig_size - 1); break; case ZMQ_CONNECT: wrap_zmq_connect(drv, bytes, bin->orig_size - 1); break; case ZMQ_SEND: wrap_zmq_send(drv, bytes, bin->orig_size - 1, bin); break; case ZMQ_RECV: wrap_zmq_recv(drv, bytes, bin->orig_size - 1); break; case ZMQ_POLL: wrap_zmq_poll(drv, bytes, bin->orig_size - 1); break; default : assert(true); } }
/* Erlang command, called on binary input from VM. */ static void zmqdrv_outputv(ErlDrvData handle, ErlIOVec *ev) { zmq_drv_t* drv = (zmq_drv_t *)handle; ErlDrvBinary* data = ev->binv[1]; unsigned char cmd = data->orig_bytes[0]; // First byte is the command zmqdrv_fprintf("driver got command %d on thread %p\r\n", (int)cmd, erl_drv_thread_self()); switch (cmd) { case ZMQ_INIT : zmqdrv_init(drv, ev); break; case ZMQ_TERM : zmqdrv_term(drv, ev); break; case ZMQ_SOCKET : zmqdrv_socket(drv, ev); break; case ZMQ_CLOSE : zmqdrv_close(drv, ev); break; case ZMQ_SETSOCKOPT : zmqdrv_setsockopt(drv, ev); break; case ZMQ_GETSOCKOPT : zmqdrv_getsockopt(drv, ev); break; case ZMQ_BIND : zmqdrv_bind(drv, ev); break; case ZMQ_CONNECT : zmqdrv_connect(drv, ev); break; case ZMQ_SEND : zmqdrv_send(drv, ev); break; case ZMQ_RECV : zmqdrv_recv(drv, ev); break; default : zmqdrv_error(drv, "Invalid driver command"); } }
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_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); }
void zmq_drv_t::add_socket(zmq_sock_info* s) { // Insert the new socket info to the head of the list if (zmq_sock_infos) zmq_sock_infos->prev = s; s->next = zmq_sock_infos; zmq_sock_infos = s; // Update map: idx -> socket zmq_sockets[s->idx] = s; // Update map: socket -> idx zmq_idxs[s->socket] = s; { // Update map: pid -> sockets zmq_pid_sockets_map_t::iterator it = zmq_pid_sockets.find(s->owner); if (it != zmq_pid_sockets.end()) it->second.sockets.insert(s); else { monitor_sockets_t ms; driver_monitor_process(port, s->owner, &ms.monitor); ms.sockets.insert(s); zmq_pid_sockets[s->owner] = ms; } } { // Update map: fd -> sockets zmq_fd_sockets_map_t::iterator it = zmq_fd_sockets.find(s->fd); if (it != zmq_fd_sockets.end()) it->second.insert(s); else { zmq_sock_set_t set; set.insert(s); zmq_fd_sockets[s->fd] = set; driver_select(port, (ErlDrvEvent)s->fd, ERL_DRV_READ, 1); zmqdrv_fprintf("registered sig_fd(%d) with VM\r\n", s->fd); } } }
static void zmqdrv_connect(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); uint16_t size = bin->orig_size - 5; char addr[512]; if (idx > drv->zmq_socket_count || !s) { zmqdrv_error_code(drv, ENODEV); return; } if (size > sizeof(addr) - 1) { zmqdrv_error_code(drv, E2BIG); return; } memcpy(addr, bytes + 5, size); addr[size] = '\0'; zmqdrv_fprintf("connect %s\r\n", addr); if (!addr[0]) { zmqdrv_error_code(drv, EINVAL); return; } if (zmq_connect(s, addr) < 0) { zmqdrv_error_code(drv, zmq_errno()); return; } zmqdrv_ok(drv); }
static void zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event) { zmq_drv_t *drv = (zmq_drv_t *)handle; // Get 0MQ sockets managed by application thread's signaler // identified by "event" fd. zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event); zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event); assert(it != drv->zmq_fd_sockets.end()); zmq_sock_set_t::iterator si = it->second.begin(); assert(si != it->second.end()); for (; si != it->second.end(); ++si) { zmq_socket_t s = (*si)->socket; uint32_t idx = (*si)->idx; ErlDrvTermData owner = (*si)->owner; int rc = 0; uint32_t events; size_t events_size = sizeof(events); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) { msg_t msg; rc = zmq_recv(s, &msg, ZMQ_NOBLOCK); ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller; if (rc == -1) { if (zmq_errno() != EAGAIN) { ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_UINT, idx, ERL_DRV_ATOM, error_atom(zmq_errno()), ERL_DRV_TUPLE, 2, ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } break; } if ((*si)->active_mode) { // Send message {zmq, Socket, binary()} to the owner pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_UINT, idx, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); } else { // Return result {ok, binary()} to the waiting caller's pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zok, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 2}; driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } // FIXME: add error handling zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); } zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) { // There was a pending unwritten message on this socket. // Try to write it. If the write succeeds/fails clear the ZMQ_POLLOUT // flag and notify the waiting caller of completion of operation. rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK); zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc); if (rc == 0) { zmq_msg_close(&(*si)->out_msg); // Unblock the waiting caller's pid by returning result zmqdrv_ok(drv, (*si)->out_caller); (*si)->out_caller = 0; } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning result zmq_msg_close(&(*si)->out_msg); zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno()); (*si)->out_caller = 0; } } zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static ErlDrvData zmqdrv_start(ErlDrvPort port, char* /*cmd*/) { zmqdrv_fprintf("zmq port driver started by pid %lu\r\n", driver_connected(port)); return reinterpret_cast<ErlDrvData>(new zmq_drv_t(port)); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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()); } }
/* Driver Stop, called on port close. */ static void zmqdrv_stop(ErlDrvData handle) { delete reinterpret_cast<zmq_drv_t*>(handle); zmqdrv_fprintf("driver stopped by pid\r\n"); }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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); } }
static void zmqdrv_ready_input(ErlDrvData handle, ErlDrvEvent event) { zmq_drv_t *drv = (zmq_drv_t *)handle; // Get 0MQ sockets managed by application thread's signaler // identified by "event" fd. zmq_fd_sockets_map_t::iterator it = drv->zmq_fd_sockets.find((long)event); zmqdrv_fprintf("input ready on [idx=%ld]\r\n", (long)event); assert(it != drv->zmq_fd_sockets.end()); zmq_sock_set_t::iterator si = it->second.begin(); assert(si != it->second.end()); for (; si != it->second.end(); ++si) { zmq_socket_t s = (*si)->socket; uint32_t idx = (*si)->idx; ErlDrvTermData owner = (*si)->owner; int rc = 0; uint32_t events; size_t events_size = sizeof(events); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); while (((*si)->active_mode || (*si)->in_caller) && (events & ZMQ_POLLIN)) { msg_t msg; rc = zmq_recv(s, &msg, ZMQ_NOBLOCK); ErlDrvTermData pid = (*si)->active_mode ? owner : (*si)->in_caller; if (rc == -1) { if (zmq_errno() != EAGAIN) { ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_PORT, driver_mk_port(drv->port), ERL_DRV_UINT, idx, ERL_DRV_TUPLE, 2, ERL_DRV_ATOM, am_error, ERL_DRV_ATOM, error_atom(zmq_errno()), ERL_DRV_TUPLE, 2, ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } break; } if ((*si)->active_mode == 1) { // Send message {zmq, Socket, binary()} to the owner pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zmq, ERL_DRV_PORT, driver_mk_port(drv->port), ERL_DRV_UINT, idx, ERL_DRV_TUPLE, 2, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 3}; driver_send_term(drv->port, owner, spec, sizeof(spec)/sizeof(spec[0])); } else if ((*si)->active_mode == 2) { // Send message {zmq, Socket, [binary()]} to the owner pid // where binary() is each message part std::vector<ErlDrvTermData> specv; specv.reserve(100); std::vector<msg_t*> parts; specv.push_back(ERL_DRV_ATOM); specv.push_back(am_zmq); specv.push_back(ERL_DRV_PORT); specv.push_back(driver_mk_port(drv->port)); specv.push_back(ERL_DRV_UINT); specv.push_back(idx); specv.push_back(ERL_DRV_TUPLE); specv.push_back(2); specv.push_back(ERL_DRV_BUF2BINARY); specv.push_back((ErlDrvTermData)zmq_msg_data(&msg)); specv.push_back(zmq_msg_size(&msg)); int64_t more; size_t more_size = sizeof(more); int next_count = 0; while (true) { zmq_getsockopt(s, ZMQ_RCVMORE, &more, &more_size); if (!more) break; next_count += 1; msg_t *next_part = new msg_t; parts.push_back(next_part); rc = zmq_recv(s, next_part, ZMQ_NOBLOCK); assert (!rc); // FIXME specv.push_back(ERL_DRV_BUF2BINARY); specv.push_back((ErlDrvTermData)zmq_msg_data(next_part)); specv.push_back(zmq_msg_size(next_part)); } specv.push_back(ERL_DRV_NIL); specv.push_back(ERL_DRV_LIST); specv.push_back(next_count + 2); specv.push_back(ERL_DRV_TUPLE); specv.push_back(3); driver_send_term(drv->port, owner, specv.data(), specv.size()); for (uint i = 0; i < parts.size(); i++) { delete parts[i]; } } else { // Return result {ok, binary()} to the waiting caller's pid ErlDrvTermData spec[] = {ERL_DRV_ATOM, am_zok, ERL_DRV_BUF2BINARY, (ErlDrvTermData)zmq_msg_data(&msg), zmq_msg_size(&msg), ERL_DRV_TUPLE, 2}; driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0])); (*si)->in_caller = 0; } // FIXME: add error handling zmqdrv_fprintf("received %ld byte message relayed to pid %ld\r\n", zmq_msg_size(&msg), pid); zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); } zmq_getsockopt(s, ZMQ_EVENTS, &events, &events_size); if ((*si)->out_caller != 0 && (events & ZMQ_POLLOUT)) { // There was a pending unwritten message on this socket. // Try to write it. If the write succeeds/fails clear the ZMQ_POLLOUT // flag and notify the waiting caller of completion of operation. rc = zmq_send(s, &(*si)->out_msg, (*si)->out_flags | ZMQ_NOBLOCK); zmqdrv_fprintf("resending message %p (size=%ld) on socket %p (ret=%d)\r\n", zmq_msg_data(&(*si)->out_msg), zmq_msg_size(&(*si)->out_msg), s, rc); if (rc == 0) { zmq_msg_close(&(*si)->out_msg); // Unblock the waiting caller's pid by returning result zmqdrv_ok(drv, (*si)->out_caller); (*si)->out_caller = 0; } else if (zmq_errno() != EAGAIN) { // Unblock the waiting caller's pid by returning result zmq_msg_close(&(*si)->out_msg); zmqdrv_socket_error(drv, (*si)->out_caller, idx, zmq_errno()); (*si)->out_caller = 0; } } zmqdrv_fprintf("--> socket %p events=%d\r\n", s, events); } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ static void wrap_zmq_getsockopt(zmq_drv_t *drv, const uint8_t* bytes, size_t size) { int opt = *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("getsockopt %p (opt: %d)\r\n", si->socket, opt); uint8_t optval[255] = {0}; size_t optvallen = 0; void * p = (void*)optval; switch (opt) { case ZMQ_HWM: optvallen = sizeof(uint64_t); break; case ZMQ_SWAP: optvallen = sizeof(int64_t); break; case ZMQ_AFFINITY: optvallen = sizeof(uint64_t); break; case ZMQ_IDENTITY: optvallen = sizeof(optval); break; case ZMQ_RATE: optvallen = sizeof(int64_t); break; case ZMQ_RECOVERY_IVL: optvallen = sizeof(int64_t); break; case ZMQ_RECOVERY_IVL_MSEC: optvallen = sizeof(int64_t); break; case ZMQ_MCAST_LOOP: optvallen = sizeof(int64_t); break; case ZMQ_SNDBUF: optvallen = sizeof(uint64_t); break; case ZMQ_RCVBUF: optvallen = sizeof(uint64_t); break; case ZMQ_RCVMORE: optvallen = sizeof(int64_t); break; case ZMQ_LINGER: optvallen = sizeof(int); break; case ZMQ_RECONNECT_IVL: optvallen = sizeof(int); break; case ZMQ_RECONNECT_IVL_MAX: optvallen = sizeof(int); break; case ZMQ_BACKLOG: optvallen = sizeof(int); break; case ZMQ_FD: optvallen = sizeof(int); break; case ZMQ_EVENTS: optvallen = sizeof(uint32_t); break; case ZMQ_TYPE: optvallen = sizeof(int); break; default: assert(true); } if (0 != zmq_getsockopt(si->socket, opt, p, &optvallen)) { reply_error(drv->port, caller, zmq_errno()); return; } switch (opt) { // uint64 case ZMQ_HWM: case ZMQ_AFFINITY: case ZMQ_SNDBUF: case ZMQ_RCVBUF: reply_ok_uint64(drv->port, caller, *(uint64_t*)p); break; // int64 case ZMQ_SWAP: case ZMQ_RATE: case ZMQ_RECOVERY_IVL: case ZMQ_RECOVERY_IVL_MSEC: reply_ok_int64(drv->port, caller, *(int64_t*)p); break; // bool (from int64 0/1) case ZMQ_RCVMORE: case ZMQ_MCAST_LOOP: reply_ok_atom(drv->port, caller, *(int64_t*)p ? am_true : am_false); break; // ZMQ_POLLIN|ZMQ_POLLOUT (from uint32) case ZMQ_EVENTS: reply_ok_events(drv->port, caller, *(uint32_t*)p); break; // int case ZMQ_LINGER: case ZMQ_RECONNECT_IVL: case ZMQ_RECONNECT_IVL_MAX: case ZMQ_BACKLOG: case ZMQ_FD: case ZMQ_TYPE: reply_ok_int(drv->port, caller, *(int*)p); break; // binary case ZMQ_IDENTITY: reply_ok_binary(drv->port, caller, p, optvallen); break; } }
//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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); }
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; } }