Ejemplo n.º 1
0
static void handle_monitor(ErlDrvData drv_data, ErlDrvMonitor *monitor)
{
    
    MyDrvData *data = (MyDrvData *) drv_data;
    OneMonitor *p,*o;
    for (p = data->first, o = NULL; 
	 p != NULL && driver_compare_monitors(&p->mon,monitor); 
	 o = p, p = p->next)
	;
    if (!p) {
	fprintf(stderr,"Spooky Monitor executed!\r\n");
    } else {
	 ErlDrvTermData spec[] = {
	      ERL_DRV_ATOM, driver_mk_atom("monitor_fired"),
	      ERL_DRV_PORT, driver_mk_port(data->port),
	      ERL_DRV_PID, p->pid,
	      ERL_DRV_TUPLE, TERM_DATA(3)
	 };
	if (!o) {
	    data->first = p->next;
	} else {
	    o->next = p->next;
	}
	driver_free(p);
	erl_drv_send_term(driver_mk_port(data->port), data->ipid, spec, sizeof(spec)/sizeof(ErlDrvTermData));
    }
    
    return;
}
Ejemplo n.º 2
0
void output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
{
    int res;
    ErlDrvPort port = (ErlDrvPort) drv_data;
    ErlDrvTermData msg[] = {
	ERL_DRV_PORT,	driver_mk_port(port),
	ERL_DRV_ATOM,	driver_mk_atom("caller"),
	ERL_DRV_PID,	driver_caller(port),
	ERL_DRV_TUPLE,	(ErlDrvTermData) 3
    };
    res = erl_drv_output_term(driver_mk_port(port), msg, sizeof(msg)/sizeof(ErlDrvTermData));
    if (res <= 0)
	driver_failure_atom(port, "erl_drv_output_term failed");
}
Ejemplo n.º 3
0
static int
get_grnam (pwd_drv_t *drv, char *cmd)
{
  struct group *grp = getgrnam (cmd);
  if (!grp)
    {
      fprintf (drv->log, "getgrnam returns NULL for %s\n", cmd);
      fflush (drv->log);

      return send_error (drv, "error", "unknown_name");
    }

  size_t result_count = 0;
  ErlDrvTermData *result = make_group (drv, grp, &result_count);
  if (!result)
    {
      return send_error (drv, "error", "Couldn't allocate memory");
    }

  int r = send_wrapped (driver_mk_port(drv->port),
                        result,
                        result_count);

  driver_free (result);
  return r;
}
Ejemplo n.º 4
0
static int
get_pwuid (pwd_drv_t *drv, char *cmd)
{
  __uid_t uid = atoi (cmd);
  struct passwd *pwd = getpwuid (uid);
  if (!pwd)
    {
      fprintf (drv->log, "getpwuid returns NULL for %s\n", cmd);
      fflush (drv->log);

      return send_error (drv, "error", "unknown_uid");
    }

  size_t result_count = 0;
  ErlDrvTermData *result = make_passwd (drv, pwd, &result_count);
  if (!result)
    {
      return send_error (drv, "error", "Couldn't allocate memory");
    }

  int r = send_wrapped (driver_mk_port(drv->port),
                        result,
                        result_count);

  driver_free (result);
  return r;
}
Ejemplo n.º 5
0
void
testcase_printf(TestCaseState_t *tcs, char *frmt, ...)
{
    InternalTestCaseState_t *itcs = (InternalTestCaseState_t *) tcs;
    ErlDrvTermData msg[12];
    va_list va;
    va_start(va, frmt);
#if HAVE_VSNPRINTF
    vsnprintf(itcs->comment_buf, COMMENT_BUF_SZ, frmt, va);
#else
    vsprintf(itcs->comment_buf, frmt, va);
#endif
    va_end(va);

    msg[0] = ERL_DRV_ATOM;
    msg[1] = (ErlDrvTermData) driver_mk_atom("print");

    msg[2] = ERL_DRV_PORT;
    msg[3] = driver_mk_port(itcs->port);

    msg[4] = ERL_DRV_ATOM;
    msg[5] = driver_mk_atom(itcs->visible.testcase_name);

    msg[6] = ERL_DRV_STRING;
    msg[7] = (ErlDrvTermData) itcs->comment_buf;
    msg[8] = (ErlDrvTermData) strlen(itcs->comment_buf);

    msg[9] = ERL_DRV_TUPLE;
    msg[10] = (ErlDrvTermData) 4;

    driver_output_term(itcs->port, msg, 11);
}
Ejemplo n.º 6
0
static int prepare(sqlite3_drv_t *drv, char *command, int command_size) {
  int result;
  const char *rest;
  sqlite3_stmt *statement;
  ErlDrvTermData spec[6];

#ifdef DEBUG
  fprintf(drv->log, "Preparing statement: %.*s\n", command_size, command);
  fflush(drv->log);
#endif
  result = sqlite3_prepare_v2(drv->db, command, command_size, &statement, &rest);
  if (result != SQLITE_OK) {
    return output_db_error(drv);
  } else if (statement == NULL) {
    return output_error(drv, SQLITE_MISUSE, "empty statement");
  }

  if (drv->prepared_count >= drv->prepared_alloc) {
    drv->prepared_alloc =
        (drv->prepared_alloc != 0) ? 2*drv->prepared_alloc : 4;
    drv->prepared_stmts =
        driver_realloc(drv->prepared_stmts,
                       drv->prepared_alloc * sizeof(sqlite3_stmt *));
  }
  drv->prepared_stmts[drv->prepared_count] = statement;
  drv->prepared_count++;

  spec[0] = ERL_DRV_PORT;
  spec[1] = driver_mk_port(drv->port);
  spec[2] = ERL_DRV_UINT;
  spec[3] = drv->prepared_count - 1;
  spec[4] = ERL_DRV_TUPLE;
  spec[5] = 2;
  return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
static void output(ErlDrvData drv_data,
		   char *buf, ErlDrvSizeT len)
{
    Otp9302Data *data = (Otp9302Data *) drv_data;
    ErlDrvTermData td_port = driver_mk_port(data->port);
    ErlDrvTermData td_receiver = driver_caller(data->port);
    ErlDrvTermData td_job = driver_mk_atom("job");
    unsigned int key = (unsigned int) (ErlDrvSInt) data->port;
    long id[5];
    Otp9302AsyncData *ad[5];
    int i;

    for (i = 0; i < sizeof(ad)/sizeof(ad[0]); i++) {
	ad[i] = driver_alloc(sizeof(Otp9302AsyncData));
	if (!ad[i])
	    abort();

	ad[i]->smp = data->smp;
	ad[i]->port = data->port;
	ad[i]->block = 0;
	ad[i]->refc = 1;
	ad[i]->term_data.port = td_port;
	ad[i]->term_data.receiver = td_receiver;
	ad[i]->term_data.msg = td_job;
	ad[i]->msgq = &data->msgq;
    }
    ad[0]->block = 1;
    ad[0]->term_data.msg = driver_mk_atom("block");
    ad[2]->term_data.msg = driver_mk_atom("cancel");
    ad[4]->term_data.msg = driver_mk_atom("end_of_jobs");
    for (i = 0; i < sizeof(id)/sizeof(id[0]); i++)
	id[i] = driver_async(data->port, &key, async_invoke, ad[i], driver_free);
    if (id[2] > 0)
	driver_async_cancel(id[2]);
}
Ejemplo n.º 9
0
/*
** Generate a fair async key prom an ErlDrvPort
** The port data gives a fair distribution grom port pointer
** to unsigned integer - to be used in key for driver_async below.
*/
unsigned int driver_async_port_key(ErlDrvPort port)
{
    ErlDrvTermData td = driver_mk_port(port);
    if (td == (ErlDrvTermData) NIL) {
	return 0;
    }
    return (unsigned int) (UWord) internal_port_data(td);
}
Ejemplo n.º 10
0
static ErlDrvData md4drv_start(ErlDrvPort port, char *buf)
{
    t_md4drv *md4 = (t_md4drv*) driver_alloc(sizeof(t_md4drv));

    if (md4 == NULL) return (ErlDrvData) -1;
    md4->port = port;
    md4->dport = driver_mk_port(port);
    return (ErlDrvData) md4;
}
Ejemplo n.º 11
0
static ErlDrvData iconvdrv_start(ErlDrvPort port, char *buf)
{
    t_iconvdrv *iconv = (t_iconvdrv*) driver_alloc(sizeof(t_iconvdrv));

    if (iconv == NULL) return (ErlDrvData) -1;
    iconv->port = port;
    iconv->dport = driver_mk_port(port);
    return (ErlDrvData) iconv;
}
Ejemplo n.º 12
0
static int
get_pwall (pwd_drv_t *drv)
{
  size_t pwd_count = 0;
  setpwent ();
  while (getpwent ())
    pwd_count++;
  endpwent ();

  size_t term_count = passwd_term_count ();
  size_t result_count = pwd_count * term_count;
  ErlDrvTermData *result = (ErlDrvTermData *) driver_alloc (sizeof (ErlDrvTermData) * (result_count + 3));
  if (!result)
    {
      fprintf (drv->log, "Couldn't allocate memory for result\n");
      fflush (drv->log);

      return send_error (drv, "error", "Couldn't allocate memory for result");
    }

  char **names = (char **) driver_alloc (sizeof (char *) * pwd_count);
  char **pwds  = (char **) driver_alloc (sizeof (char *) * pwd_count);

  setpwent ();

  size_t result_idx = 0;
  struct passwd *pwd = getpwent ();
  while (pwd)
    {
      fill_passwd (&result[result_idx * term_count], pwd, &names[result_idx], &pwds[result_idx]);
      result_idx++;

      pwd = getpwent ();
    }

  endpwent ();

  result[result_count++] = ERL_DRV_NIL;
  result[result_count++] = ERL_DRV_LIST;
  result[result_count++] = pwd_count + 1;

  int r = send_wrapped (driver_mk_port(drv->port),
                        result,
                        result_count);

  size_t i = 0;
  for (; i < pwd_count; ++i)
    {
      driver_free (pwds[i]);
      driver_free (names[i]);
    }

  driver_free (pwds);
  driver_free (names);
  driver_free (result);
  return r;
}
Ejemplo n.º 13
0
static inline int output_done(sqlite3_drv_t *drv) {
  // Return {Port, ok}
  ErlDrvTermData spec[] = {
      ERL_DRV_PORT, driver_mk_port(drv->port),
      ERL_DRV_ATOM, drv->atom_done,
      ERL_DRV_TUPLE, 2
  };
  return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
}
Ejemplo n.º 14
0
static void av_exit(H264Decoder *d)
{
  ErlDrvTermData reply[] = {
    ERL_DRV_ATOM, driver_mk_atom("av_decoder_closed"),
    ERL_DRV_PORT, driver_mk_port(d->port),
    ERL_DRV_TUPLE, 2
  };
  driver_output_term(d->port, reply, sizeof(reply) / sizeof(reply[0]));
  driver_exit(d->port, 0);
}
Ejemplo n.º 15
0
// Unknown Command
static int unknown(sqlite3_drv_t *drv, char *command, int command_size) {
  // Return {Port, error, unknown_command}
  ErlDrvTermData spec[] = {
      ERL_DRV_PORT, driver_mk_port(drv->port),
      ERL_DRV_ATOM, drv->atom_error,
      ERL_DRV_INT, (ErlDrvTermData) ((ErlDrvSInt) -1),
      ERL_DRV_ATOM, drv->atom_unknown_cmd,
      ERL_DRV_TUPLE, 4
  };
  return driver_output_term(drv->port, spec, sizeof(spec) / sizeof(spec[0]));
}
Ejemplo n.º 16
0
static void return_ok(bdb_drv_t* pdrv) {

    ErlDrvTermData spec[] = {ERL_DRV_ATOM, driver_mk_atom("ok")};

#if ((ERL_DRV_EXTENDED_MAJOR_VERSION == 1) || ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2) && (ERL_DRV_EXTENDED_MINOR_VERSION == 0)))
    driver_output_term(pdrv->port, spec, sizeof(spec) / sizeof(spec[0]));
#else
    ErlDrvTermData mkport = driver_mk_port(pdrv->port);
    erl_drv_output_term(mkport, spec, sizeof(spec) / sizeof(spec[0]));
#endif

}
Ejemplo n.º 17
0
static ErlDrvData inpevt_start(ErlDrvPort port, char *command)
{
    IEContext *ctx = 0;

    ctx = (IEContext*) driver_alloc(sizeof(IEContext));
    ctx->mDescriptor = -1;
    ctx->mPort = port;
    ctx->mDport = driver_mk_port(port);
    ctx->mDevice[0] = 0;
//    set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY);
    set_port_control_flags(port, 0);
    return (ErlDrvData) ctx;
}
Ejemplo n.º 18
0
static inline int output_error(
    sqlite3_drv_t *drv, int error_code, const char *error) {
  int term_count = 2, term_allocated = 13;
  ErlDrvTermData *dataset = driver_alloc(sizeof(ErlDrvTermData) * term_allocated);
  dataset[0] = ERL_DRV_PORT;
  dataset[1] = driver_mk_port(drv->port);
  return_error(drv, error_code, error, &dataset, &term_count, &term_allocated, NULL);
  term_count += 2;
  dataset[11] = ERL_DRV_TUPLE;
  dataset[12] = 2;
  driver_output_term(drv->port, dataset, term_count);
  return 0;
}
Ejemplo n.º 19
0
/* send {P, ok} to caller */
static ErlDrvSSizeT driver_send_ok(t_iconvdrv *iv)
{
    int i = 0;
    ErlDrvTermData to, spec[10];

    to = driver_caller(iv->port);

    i = LOAD_PORT(spec, i, iv->dport);
    i = LOAD_ATOM(spec, i, am_ok);
    i = LOAD_TUPLE(spec, i, 2);

    return erl_drv_send_term(driver_mk_port(iv->port), to, spec, i);
}
Ejemplo n.º 20
0
static ErlDrvData start(ErlDrvPort port, char* cmd) {
  dnssd_drv_t* retval = (dnssd_drv_t*) driver_alloc(sizeof(dnssd_drv_t));
  retval->sd_ref = NULL;
  retval->erl_port = port;
  retval->term_port = driver_mk_port(port);
  retval->term_ok = driver_mk_atom("ok");
  retval->term_error = driver_mk_atom("error");
  retval->term_enumerate = driver_mk_atom("enumerate");
  retval->term_browse = driver_mk_atom("browse");
  retval->term_resolve = driver_mk_atom("resolve");
  retval->term_register = driver_mk_atom("register");
  return (ErlDrvData) retval;
}
Ejemplo n.º 21
0
static void av_decoder_decoded(ErlDrvData drv_data, ErlDrvThreadData thread_data)
{
  H264Decoder *decoder = (H264Decoder *)drv_data;
  H264Frame *frame = (H264Frame *)thread_data;
  
  // fprintf(stderr, "Decoding finished: %p %ld\r\n", frame->yuv, frame->yuv ? frame->yuv->orig_size : -1);
  
  if(frame->yuv) {
    //    av_log(NULL,AV_LOG_WARNING,"\nPORTyuv:: %i\n",decoder->port);
    ErlDrvTermData reply[] = {
      ERL_DRV_ATOM, driver_mk_atom("yuv"),
      ERL_DRV_PORT, driver_mk_port(decoder->port),
      ERL_DRV_BINARY, (ErlDrvTermData)frame->yuv, (ErlDrvTermData)frame->yuv->orig_size, 0,
      ERL_DRV_TUPLE, 3
    };
    driver_output_term(decoder->port, reply, sizeof(reply) / sizeof(reply[0]));
    driver_free_binary(frame->yuv);
  } else if(frame->sample){
    //    av_log(NULL,AV_LOG_WARNING,"\nPORTsample:: %i\n",decoder->port);
    ErlDrvTermData reply[] = {
      ERL_DRV_ATOM, driver_mk_atom("sample"),
      ERL_DRV_PORT, driver_mk_port(decoder->port),
      ERL_DRV_BINARY, (ErlDrvTermData)frame->sample, (ErlDrvTermData)frame->sample->orig_size, 0,
      ERL_DRV_TUPLE, 3
    };
    driver_output_term(decoder->port, reply, sizeof(reply) / sizeof(reply[0]));
    driver_free_binary(frame->sample);
  } else {
    //    av_log(decoder->dec,AV_LOG_ERROR,"NOT YUV");
    av_log(NULL,AV_LOG_WARNING,"\nPORTelse:: %i\n",decoder->port);
    ErlDrvTermData reply[] = {
      ERL_DRV_ATOM, driver_mk_atom("yuv"),
      ERL_DRV_PORT, driver_mk_port(decoder->port),
      ERL_DRV_TUPLE, 2
    };
    driver_output_term(decoder->port, reply, sizeof(reply) / sizeof(reply[0]));
  }
  driver_free(frame);
}
Ejemplo n.º 22
0
static void process_unkown(bdb_drv_t *bdb_drv, ErlIOVec *ev) {
  // Return {error, unkown_command}
  ErlDrvTermData spec[] = {ERL_DRV_ATOM, driver_mk_atom("error"),
               ERL_DRV_ATOM, driver_mk_atom("uknown_command"),
               ERL_DRV_TUPLE, 2};

#if ((ERL_DRV_EXTENDED_MAJOR_VERSION == 1) || ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2) && (ERL_DRV_EXTENDED_MINOR_VERSION == 0)))
    driver_output_term(bdb_drv->port, spec, sizeof(spec) / sizeof(spec[0]));
#else
    ErlDrvTermData mkport = driver_mk_port(bdb_drv->port);
    erl_drv_output_term(mkport, spec, sizeof(spec) / sizeof(spec[0]));
#endif

}
Ejemplo n.º 23
0
static void
zmqdrv_socket_error(zmq_drv_t *drv, ErlDrvTermData pid, uint32_t idx, int err) {
    // Return {zmq, Socket::integer(), {error, Reason::atom()}}
    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(err),
         ERL_DRV_TUPLE, 2,
         ERL_DRV_TUPLE, 3};
    driver_send_term(drv->port, pid, spec, sizeof(spec)/sizeof(spec[0]));
}
Ejemplo n.º 24
0
static ErlDrvData start(ErlDrvPort port,
			char *command)
{
    async_blast_data_t *abd;

    abd = driver_alloc(sizeof(async_blast_data_t));
    if (!abd)
	return ERL_DRV_ERROR_GENERAL;

    abd->port = port;
    abd->port_id = driver_mk_port(port);
    abd->counter = 0;
    return (ErlDrvData) abd;
}
Ejemplo n.º 25
0
/* send {P, error, Error} to caller */
static ErlDrvSSizeT driver_send_error(t_iconvdrv *iv, ErlDrvTermData *am)
{
    int i = 0;
    ErlDrvTermData to, spec[8];

    to = driver_caller(iv->port);

    i = LOAD_PORT(spec, i, iv->dport);
    i = LOAD_ATOM(spec, i, am_error);
    i = LOAD_ATOM(spec, i, *am);
    i = LOAD_TUPLE(spec, i, 3);

    return erl_drv_send_term(driver_mk_port(iv->port), to, spec, i);
}
Ejemplo n.º 26
0
/* send {P, value, Bin} to caller */
static ErlDrvSSizeT driver_send_bin(t_iconvdrv *iv, ErlDrvBinary *bin, ErlDrvSizeT len)
{
    int i = 0;
    ErlDrvTermData to, spec[10];

    to = driver_caller(iv->port);

    i = LOAD_PORT(spec, i, iv->dport);
    i = LOAD_ATOM(spec, i, am_value);
    i = LOAD_BINARY(spec, i, bin, 0, len);
    i = LOAD_TUPLE(spec, i, 3);

    return erl_drv_send_term(driver_mk_port(iv->port), to, spec, i);
}
Ejemplo n.º 27
0
static void fail_term(ErlDrvTermData* msg, int len, int line)
{
    int status = erl_drv_output_term(driver_mk_port(erlang_port), msg, len);

    if (status == 1) {
	char buf[1024];
	sprintf(buf, "%s:%d: unexpected success", __FILE__, line);
	driver_failure_atom(erlang_port, buf);
    } else if (status == 0) {
	char buf[1024];
	sprintf(buf, "%s:%d: unexpected port error", __FILE__, line);
	driver_failure_atom(erlang_port, buf);
    }
}
Ejemplo n.º 28
0
static void return_error_tuple(bdb_drv_t* pdrv, char* err_msg) {

    ErlDrvTermData spec[] = {   ERL_DRV_ATOM, driver_mk_atom("error"),
                                ERL_DRV_ATOM, driver_mk_atom(err_msg),
                                ERL_DRV_TUPLE, 2};

#if ((ERL_DRV_EXTENDED_MAJOR_VERSION == 1) || ((ERL_DRV_EXTENDED_MAJOR_VERSION == 2) && (ERL_DRV_EXTENDED_MINOR_VERSION == 0)))
    driver_output_term(pdrv->port, spec, sizeof(spec) / sizeof(spec[0]));
#else
    ErlDrvTermData mkport = driver_mk_port(pdrv->port);
    erl_drv_output_term(mkport, spec, sizeof(spec) / sizeof(spec[0]));
#endif

}
Ejemplo n.º 29
0
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;
}
Ejemplo n.º 30
0
void gen_http_drv_ready_output(ErlDrvData handle, ErlDrvEvent event)
{
  HTTP* d = (HTTP*) handle;
  
  if(d->mode == REQUEST_MODE && d->state == CONNECTING_STATE) {
    accept_connection(d);
    return;
  }
  
  
  SysIOVec* vec;
  int vlen = 0;
  size_t written;
  
  vec = driver_peekq(d->port, &vlen);
  if(!vec || !vlen) {
    deactivate_write(d);
    return;
  }
  written = writev(d->socket, (const struct iovec *)vec, vlen > IOV_MAX ? IOV_MAX : vlen);
  if(vlen > IOV_MAX) {
    fprintf(stderr, "Buffer overloaded: %d, %d\r\n", vlen, (int)(driver_sizeq(d->port) - written));
  }
  if(written == -1) {
    if((errno != EWOULDBLOCK) && (errno != EINTR) && (errno != EAGAIN)) {
        // fprintf(stderr, "Error in writev: %s, %d bytes left\r\n", strerror(errno), (int)driver_sizeq(d->port));
      tcp_exit(d);
      return;
    }
  } else {
    ErlDrvSizeT rest = driver_deq(d->port, written);
    
    if(rest == 0) {
      ErlDrvTermData reply[] = {
        ERL_DRV_ATOM, atom_http,
        ERL_DRV_PORT, driver_mk_port(d->port),
        ERL_DRV_ATOM, atom_empty,
        ERL_DRV_TUPLE, 3
      };
      
      pid_list_send(d->exhausted, d->port, reply, sizeof(reply) / sizeof(reply[0]));
      pid_list_free(&d->exhausted);
      
      set_busy_port(d->port, 0);
    }
    // fprintf(stderr, "Network write: %d (%d)\r\n", (int)written, (int)rest);
    
  }
}