static int reply_ok_binary(descriptor_t *desc, char *ptr, int beg_offset, int length) { ErlDrvTermData msg[24]; int i = 0; int res; i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_ok); i = LOAD_BINARY(msg, i, edtk_alloced_ptr2ErlDrvBinary(ptr), beg_offset, length); i = LOAD_TUPLE(msg, i, 3); edtk_debug("%s: i = %d, ptr = 0x%lx, start = %d, end = %d", __FUNCTION__, i, ptr, beg_offset, length); res = driver_output_term(desc->port, msg, i); /* driver_output_term() incrs refc, and we're done, so decr refc */ /* ** We _know_ that "ptr" points to memory allocated by ** edtk_driver_alloc_wrapper(), so edtk_alloced_ptr2ErlDrvBinary() ** is safe in this case. If it weren't safe, then the binary ** must be returned by an xtra_return, which means we ** reply_ok_binary()) are never called! */ driver_free_binary(edtk_alloced_ptr2ErlDrvBinary(ptr)); edtk_debug("%s: res = %d", __FUNCTION__, res); return res; }
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); }
static void invoke_s1_null(void *data) { callstate_t *c = (callstate_t *) data; edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self()); null(); edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self()); }
static void cleanup_valmap_fd_index(descriptor_t *desc, int i, int do_cleanup_func) { edtk_debug("%s: i = %d, do_cleanup_func = %d", __FUNCTION__, i, do_cleanup_func); if (do_cleanup_func) { edtk_debug("%s: calling func close", __FUNCTION__); close(desc->valmap_fd[i]); } desc->valmap_fd[i] = -1; }
void edtk_driver_free_wrapper(void *p) { ErlDrvBinary *eb; edtk_debug("%s: top", __FUNCTION__); if ((eb = edtk_alloced_ptr2ErlDrvBinary(p)) != NULL) { edtk_debug("%s: eb = 0x%lx p = 0x%lx", __FUNCTION__, eb, p); driver_free_binary(eb); } }
static void invoke_s1_getfd(void *data) { callstate_t *c = (callstate_t *) data; c = c; edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self()); c->o.ret_int = my_getfd( c->i.fd ); edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self()); }
void * edtk_driver_alloc_wrapper(size_t size) { ErlDrvBinary *eb; edtk_debug("%s: top", __FUNCTION__); if ((eb = driver_alloc_binary(size)) != NULL) { edtk_debug("%s: size %lu eb = 0x%lx orig_bytes = 0x%lx", __FUNCTION__, size, eb, eb->orig_bytes); return eb->orig_bytes; } else { return NULL; } }
static int reply_ok(descriptor_t *desc) { ErlDrvTermData msg[24]; int i = 0; int res; i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_ok); i = LOAD_TUPLE(msg, i, 2); edtk_debug("reply_ok: i = %d", i); res = driver_output_term(desc->port, msg, i); edtk_debug("reply_ok: res = %d", res); return res; }
static int reply_error_atom(descriptor_t *desc, ErlDrvTermData atom) { ErlDrvTermData msg[24]; int i = 0; int res; i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_error); i = LOAD_ATOM(msg, i, atom); i = LOAD_TUPLE(msg, i, 3); edtk_debug("%s: i = %d", __FUNCTION__, i); res = driver_output_term(desc->port, msg, i); edtk_debug("%s: res = %d", __FUNCTION__, res); return res; }
static int reply_ok_num(descriptor_t *desc, unsigned long num) { ErlDrvTermData msg[24]; int i = 0; int res; i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_ok); i = LOAD_INT(msg, i, num); i = LOAD_TUPLE(msg, i, 3); edtk_debug("%s: i = %d, num = %lu", __FUNCTION__, i, num); res = driver_output_term(desc->port, msg, i); edtk_debug("%s: res = %d", __FUNCTION__, res); return res; }
static void s1_stop(ErlDrvData drv_data) { descriptor_t *desc = (descriptor_t *) drv_data; int i = 0; ErlDrvPort port; int still_in_use = 0; i = i; if (desc == NULL) { edtk_debug("%s: drv_data == NULL", __FUNCTION__); return; } edtk_debug("%s: port = %ld", __FUNCTION__, desc->port); for (i = 0; i < 32; i++) { if (desc->valmap_fd[i] != -1) { cleanup_valmap_fd_index(desc, i, 1); } } if (! still_in_use) { port = desc->port; sys_free(desc); edtk_debug("%s: port = %ld finished", __FUNCTION__, port); } else { /* ** XXX Oi, this is a sticky problem. This port is being shut ** down, but we've still got some valmaps in use. We have no ** way to tell the VM that we cannot be shut down safely right ** now, so what in the heck do we do? It seems to me that we ** have two choices: ** 1. Block the entire VM until all valmaps are idle, then ** clean them up and then return. ** 2. Create a new thread that will take care of monitoring ** the valmaps & doing their final cleanup. This stop ** function, executing in the main thread, can return ** to the VM right away. ** For the sake of simplicity, I'm going to implement #1 ** for now. This will get more complicated once EDTK supports ** private worker threads, so we won't bother getting fancy ** for now. */ edtk_debug("%s: port = %ld has %d valmaps still in use!", __FUNCTION__, desc->port, still_in_use); sleep(1); s1_stop(drv_data); /* Hope we don't run out of stack */ } }
static ErlDrvData s1_start(ErlDrvPort port, char *args) { descriptor_t *desc; int i = 0; i = i; edtk_debug("%s: starting, port = %ld, args = 0x%lx, %s", __FUNCTION__, port, (unsigned long) args, args); if ((desc = (descriptor_t *) sys_alloc(sizeof(descriptor_t))) == NULL) { return ERL_DRV_ERROR_GENERAL; } memset(desc, 0, sizeof(descriptor_t)); desc->port = port; desc->nextxid = 1; for (i = 0; i < 32; i++) { desc->valmap_fd[i] = -1; } /* TODO: Finish initializing descriptor members */ /* TODO: Take care of other port initialization tasks */ return (ErlDrvData) desc; }
static int reply_error(descriptor_t *desc, int errnum) { ErlDrvTermData msg[24]; int i = 0; int res; edtk_debug("reply_error: errnum = %d", errnum); i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_error); i = LOAD_INT(msg, i, errnum); i = LOAD_TUPLE(msg, i, 3); edtk_debug("reply_error: i = %d", i); res = driver_output_term(desc->port, msg, i); edtk_debug("reply_error: res = %d", res); return res; }
static void cleanup_valmap_fd_all(descriptor_t *desc, int do_cleanup_func) { int i; edtk_debug("%s: do_cleanup_func = %d", __FUNCTION__, do_cleanup_func); for (i = 0; i < 32; i++) { cleanup_valmap_fd_index(desc, i, do_cleanup_func); } }
static void invoke_s1_open(void *data) { callstate_t *c = (callstate_t *) data; edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self()); c->o.ret_int = my_open(c->i.filename, c->i.flags); if (c->o.ret_int >= 0) { c->o.__expect = 1; } else { c->o.__expect = 0; c->o.__expect_errval = errno; /* Danger! Do not put debugging statement before saving error val! */ edtk_debug("%s: threadid = %lx expectation failed!", __FUNCTION__, pthread_self()); } edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self()); }
static int reply_ok_valmap(descriptor_t *desc, ErlDrvTermData valmap_atom, unsigned long valmap_index) { ErlDrvTermData msg[15]; int i = 0; int res; i = LOAD_PORT(msg, i, driver_mk_port(desc->port)); i = LOAD_ATOM(msg, i, am_ok); i = LOAD_ATOM(msg, i, valmap_atom); i = LOAD_INT(msg, i, valmap_index); i = LOAD_TUPLE(msg, i, 2); i = LOAD_TUPLE(msg, i, 3); edtk_debug("reply_ok_valmap: i = %d", i); res = driver_output_term(desc->port, msg, i); edtk_debug("reply_ok_valmap: res = %d", res); return res; }
static void invoke_s1_receivefd(void *data) { callstate_t *c = (callstate_t *) data; c = c; edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self()); c->o.ret_int_t = my_receivefd( c->i.unixdom_fd ); if (c->o.ret_int_t >= 0) { c->o.__expect = 1; } else { c->o.__expect = 0; c->o.__expect_errval = errno; /* Danger! Do not put debugging statement before saving error val! */ edtk_debug("%s: threadid = %lx expectation failed!", __FUNCTION__, pthread_self()); } edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self()); }
static void s1_output(ErlDrvData drv_data, char *buf, int len) { /* ** Nobody should be calling this function because we've ** defined the "outputv" driver method, which BEAM will always ** used if it's available. I just put a debug statement in ** here so that I might actually notice if the impossible ever ** happens.... */ edtk_debug("%s: XXX someone tried to call us, silly", __FUNCTION__); }
ErlDrvBinary * edtk_alloced_ptr2ErlDrvBinary(void *p) { ErlDrvBinary stackeb; int offset = stackeb.orig_bytes - (char *) &stackeb; if (p != NULL) { edtk_debug("%s: p = 0x%lx eb = 0x%lx", __FUNCTION__, p, (ErlDrvBinary *) ((char *) p - offset)); return (ErlDrvBinary *) ((char *) p - offset); } else { return NULL; } }
static int s1_control(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen) { char *ret = *rbuf; int retlen; /* ** Nobody should be calling this function either. */ ret[0] = 1; retlen = 1; edtk_debug("%s: XXX someone tried to call us, silly", __FUNCTION__); return retlen; }
void * edtk_driver_realloc_wrapper(void *p, size_t size) { ErlDrvBinary *eb, *neweb; edtk_debug("%s: top", __FUNCTION__); if ((eb = edtk_alloced_ptr2ErlDrvBinary(p)) != NULL) { if ((neweb = driver_realloc_binary(eb, size)) != NULL) { return neweb->orig_bytes; } else { return NULL; } } else { return NULL; } }
static void s1_outputv(ErlDrvData drv_data, ErlIOVec *ev) { descriptor_t *desc = (descriptor_t *) drv_data; unsigned char cmd; int p = 0, q = 1; callstate_t *c = NULL; int do_async_call = default_async_calls; unsigned long binlen; int index; void *tmp = NULL; binlen = binlen; index = index; tmp = tmp; if (desc == NULL || ev == NULL || ev->size < 1) { edtk_debug("%s: bad arg(s)", __FUNCTION__); return; } if (! EV_GET_CHAR(ev, &cmd, &p, &q)) { edtk_debug("%s: empty command", __FUNCTION__); reply_error(desc, EINVAL); } if ((c = sys_alloc(sizeof(callstate_t))) == NULL) { reply_error(desc, ENOMEM); return; } c->cmd = cmd; c->key = NULL; c->free = sys_free; c->xid = 0; /* XXX unused right now */ c->o.__expect = 1; /* Default is that expectation is always met */ edtk_debug("%s: my threadid = %lx, cmd = %d", __FUNCTION__, pthread_self(), cmd); switch (cmd) { case S1_DEBUG: EV_GET_UINT32(ev, &edtk_debug_flag, &p, &q); reply_ok_num(desc, edtk_debug_flag); /* Immediate reply */ sys_free(c); c = NULL; break; case S1_NULL: c->invoke = invoke_s1_null; break; case S1_OPEN: c->invoke = invoke_s1_open; EV_GET_UINT32(ev, &binlen, &p, &q); c->i.filename = (char *) EV_GETPOS(ev, p, q); if (edtk_ev_forward_N(ev, binlen, &p, &q, 1) < 0) { goto error; } EV_GET_UINT32(ev, &c->i.flags, &p, &q); break; case S1_GETFD: c->invoke = invoke_s1_getfd; EV_GET_UINT32(ev, &index, &p, &q); if (desc->valmap_fd[index] == -1) { goto error; } else { edtk_debug("%s: valmap fd index %d = 0x%lx", __FUNCTION__, index, desc->valmap_fd[index]); c->i.fd = desc->valmap_fd[index]; c->i.__valmap_fd_index = index; } break; case S1_SENDFD: c->invoke = invoke_s1_sendfd; EV_GET_UINT32(ev, &c->i.unixdom_fd, &p, &q); EV_GET_UINT32(ev, &c->i.fd_to_be_sent, &p, &q); break; case S1_RECEIVEFD: c->invoke = invoke_s1_receivefd; EV_GET_UINT32(ev, &c->i.unixdom_fd, &p, &q); break; case S1_CLOSE: c->invoke = invoke_s1_close; EV_GET_UINT32(ev, &index, &p, &q); if (index == -1 && 0 == 1) { edtk_debug("%s: valmap fd index %d = default value 0x%lx", __FUNCTION__, index, -1); c->i.fd = -1; c->i.__valmap_fd_index = -1; } else if (desc->valmap_fd[index] == -1) { goto error; } else { edtk_debug("%s: valmap fd index %d = 0x%lx", __FUNCTION__, index, desc->valmap_fd[index]); c->i.fd = desc->valmap_fd[index]; c->i.__valmap_fd_index = index; } break; } if (c != NULL) { if (do_async_call) { driver_async(desc->port, c->key, c->invoke, c, c->free); } else { /* ** Execute the bottom half right away, then send the result. */ (*(c->invoke))((void *) c); s1_ready_async((ErlDrvData) desc, (ErlDrvThreadData) c); /* ** c is already freed for us by s1_ready_async() */ } } return; error: if (c != NULL) { sys_free(c); } reply_error_atom(desc, am_badarg); }