//-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~ 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); }
static void enm_add_waiter(EnmData* d, erlang_ref* ref) { EnmRecv *rcv, *cur; rcv = driver_alloc(sizeof(EnmRecv)); if (rcv == 0) { driver_failure_posix(d->port, ENOMEM); return; } memcpy(&rcv->ref, ref, sizeof *ref); rcv->rcvr = driver_caller(d->port); rcv->next = 0; driver_monitor_process(d->port, rcv->rcvr, &rcv->monitor); cur = d->waiting_recvs; if (cur == 0) d->waiting_recvs = rcv; else { while (cur) { if (cur->next != 0) cur = cur->next; else { cur->next = rcv; break; } } } }
static void crop(Cmd *cmd) { char *buff = cmd->data; int len = cmd->size; Gd *gd = cmd->gd; int index = 0; long width, height; int srcW, srcH, srcX, srcY, destX, destY, playX, playY; gdImagePtr destination = NULL; ei_decode_version(buff, &index, NULL); ei_decode_tuple_header(buff, &index, NULL); ei_decode_long(buff, &index, &width); ei_decode_long(buff, &index, &height); if (NULL == gd->image) { driver_failure_atom(gd->port, "null_image"); return; } srcW = gdImageSX(gd->image); srcH = gdImageSY(gd->image); destination = gdImageCreateTrueColor(width, height); if (NULL == destination) { driver_failure_posix(gd->port, ENOMEM); return; } gdImageFilledRectangle(destination, 0, 0, width, height, gdImageColorAllocate(destination, 255, 255, 255)); destX = (width - srcW) / 2; destY = (height - srcH) / 2; gdImageCopy(destination, gd->image, destX, destY, 0, 0, srcW, srcH); gdImageDestroy(gd->image); gd->image = destination; send_atom(gd->port, "ok"); }
static void resize(Cmd *cmd) { char *buff = cmd->data; int len = cmd->size; Gd *gd = cmd->gd; int index = 0; unsigned long width, height, srcW, srcH; gdImagePtr destination = NULL; ei_decode_version(buff, &index, NULL); ei_decode_tuple_header(buff, &index, NULL); ei_decode_ulong(buff, &index, &width); ei_decode_ulong(buff, &index, &height); if (NULL == gd->image) { driver_failure_atom(gd->port, "null_image"); return; } srcW = gdImageSX(gd->image); srcH = gdImageSY(gd->image); destination = gdImageCreateTrueColor(width, height); if (NULL == destination) { driver_failure_posix(gd->port, ENOMEM); return; } gdImageCopyResampled(destination, gd->image, 0, 0, 0, 0, width, height, srcW, srcH); gdImageDestroy(gd->image); gd->image = destination; send_atom(gd->port, "ok"); }
/* ** Timeout from driver_set_timer. */ static void trace_file_timeout(ErlDrvData handle) { TraceFileData *data = (TraceFileData *) handle; if (data->wrap) { if (wrap_file(data) < 0) { driver_failure_posix(data->port, errno); /* XXX */ return; } else { driver_set_timer(data->port, data->wrap->time); } } }
static void trace_file_output(ErlDrvData handle, char *buff, ErlDrvSizeT bufflen) { int heavy = 0; TraceFileData *data = (TraceFileData *) handle; unsigned char b[5] = ""; put_be((unsigned) bufflen, b + 1); switch (my_write(data, (unsigned char *) b, sizeof(b))) { case 1: heavy = !0; case 0: switch (my_write(data, (unsigned char *) buff, bufflen)) { case 1: heavy = !0; case 0: break; case -1: driver_failure_posix(data->port, errno); /* XXX */ return; } break; case -1: driver_failure_posix(data->port, errno); /* XXX */ return; } if (data->wrap) { TraceFileWrapData *wrap = data->wrap; /* Size limited wrapping log files */ wrap->len += sizeof(b) + bufflen; if (wrap->time == 0 && wrap->len >= wrap->size) { if (wrap_file(data) < 0) { driver_failure_posix(data->port, errno); /* XXX */ return; } heavy = !0; } } if (heavy) { set_port_control_flags(data->port, PORT_CONTROL_FLAG_HEAVY); } }
static void get_blob(Cmd *cmd) { char *buff = cmd->data; int len = cmd->size; Gd *gd = cmd->gd; int index = 0; void *imgData = NULL; int size = 0; ErlDrvBinary * bin; long quality; ei_decode_version(buff, &index, NULL); ei_decode_long(buff, &index, &quality); if (NULL == gd->image) { driver_failure_atom(gd->port, "null_image"); return; } imgData = gd->blob(gd->image, &size, quality); if (NULL == imgData) { driver_failure_posix(gd->port, ENOMEM); return; } bin = driver_alloc_binary(size); if (NULL == bin) { driver_failure_posix(gd->port, ENOMEM); return; } memcpy(bin->orig_bytes, imgData, size); gdFree(imgData); ErlDrvTermData spec[] = { ERL_DRV_PORT, driver_mk_port(gd->port), ERL_DRV_ATOM, driver_mk_atom("ok"), ERL_DRV_BINARY, bin, size, 0, ERL_DRV_TUPLE, 3}; driver_output_term(gd->port, spec, sizeof(spec) / sizeof(ERL_DRV_PORT)); driver_free_binary(bin); }
ErlDrvSSizeT control(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) { switch (command) { case 'B': /* busy */ set_busy_port((ErlDrvPort) drv_data, 1); break; case 'N': /* not busy */ set_busy_port((ErlDrvPort) drv_data, 0); break; default: driver_failure_posix((ErlDrvPort) drv_data, EINVAL); break; } return 0; }
/* ** Control message from erlang, we handle $f, which is flush. */ static int trace_file_control(ErlDrvData handle, unsigned int command, char* buff, int count, char** res, int res_size) { if (command == 'f') { TraceFileData *data = (TraceFileData *) handle; if (my_flush(data) < 0) { driver_failure_posix(data->port, errno); /* XXX */ } if (res_size < 1) { *res = my_alloc(1); } **res = '\0'; return 1; } return -1; }
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 uvc_drv_input(ErlDrvData handle, ErlDrvEvent io_event) { Uvc* d = (Uvc*) handle; struct v4l2_buffer buf; memset(&buf, 0, sizeof buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; int ret = ioctl(d->fd, VIDIOC_DQBUF, &buf); if(ret < 0) { driver_failure_posix(d->port, errno); return; } ErlDrvBinary* bin; size_t len; if(d->pixelformat == V4L2_PIX_FMT_YUYV) { len = d->width*d->height*3/2; bin = driver_alloc_binary(len); if(!d->scale_ctx) d->scale_ctx = sws_getContext( d->width, d->height, PIX_FMT_YUYV422, d->width, d->height, PIX_FMT_YUV420P, SWS_FAST_BILINEAR, NULL, NULL, NULL ); int linesize[4] = {d->width*2, 0, 0, 0}; uint8_t *src[4] = {(uint8_t *)d->buffers[buf.index].mem, 0, 0, 0}; int stride_size = d->width*d->height; uint8_t *plane[4] = {(uint8_t *)bin->orig_bytes, (uint8_t *)bin->orig_bytes+stride_size, (uint8_t *)bin->orig_bytes+stride_size+stride_size/4, NULL}; int stride[4] = {d->width, d->width/2, d->width/2, 0}; sws_scale(d->scale_ctx, (const uint8_t * const*)src, linesize, 0, d->height, plane, stride); } else { bin = driver_alloc_binary(buf.bytesused + 1024); len = add_huffman((uint8_t *)bin->orig_bytes, (uint8_t *)d->buffers[buf.index].mem, buf.bytesused); } ErlDrvUInt64 pts = buf.timestamp.tv_sec * 1000 + buf.timestamp.tv_usec / 1000; ErlDrvTermData reply[] = { ERL_DRV_ATOM, driver_mk_atom("uvc"), ERL_DRV_PORT, driver_mk_port(d->port), ERL_DRV_ATOM, driver_mk_atom(d->pixelformat == V4L2_PIX_FMT_YUYV ? "yuv" : "jpeg"), ERL_DRV_UINT64, &pts, ERL_DRV_BINARY, (ErlDrvTermData)bin, (ErlDrvTermData)len, 0, ERL_DRV_TUPLE, 5 }; // fprintf(stderr, "Event in uvc: %lu %u %u\r\n", len, (unsigned)buf.timestamp.tv_sec, (unsigned)buf.timestamp.tv_usec); driver_output_term(d->port, reply, sizeof(reply) / sizeof(reply[0])); driver_free_binary(bin); ret = video_queue_buffer(d, buf.index); if(ret < 0) { driver_failure_posix(d->port, errno); return; } }
static int uvc_drv_command(ErlDrvData handle, unsigned int command, char *buf, int len, char **rbuf, int rlen) { Uvc* d = (Uvc*) handle; switch(command) { case CMD_OPEN: { if(len != sizeof(Config)) { driver_failure_atom(d->port, "invalid_config"); return 0; } Config *cfg = (Config *)buf; char device_path[1024]; snprintf(device_path, sizeof(device_path), "/dev/video%d", cfg->device); d->fd = open(device_path, O_RDWR); if(d->fd == -1) { driver_failure_posix(d->port, errno); return 0; } struct v4l2_capability cap; memset(&cap, 0, sizeof cap); int ret; ret = ioctl(d->fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { driver_failure_posix(d->port, errno); return 0; } if(!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { driver_failure_atom(d->port, "not_a_capture_device"); return 0; } fprintf(stderr, "Setting size %dx%d\r\n", cfg->width, cfg->height); struct v4l2_format fmt; memset(&fmt, 0, sizeof fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(cfg->width > 640 || cfg->height > 480) { d->pixelformat = V4L2_PIX_FMT_MJPEG; } else { d->pixelformat = V4L2_PIX_FMT_YUYV; } d->width = cfg->width; d->height = cfg->height; fmt.fmt.pix.width = cfg->width; fmt.fmt.pix.height = cfg->height; fmt.fmt.pix.pixelformat = d->pixelformat; fmt.fmt.pix.field = V4L2_FIELD_ANY; ret = ioctl(d->fd, VIDIOC_S_FMT, &fmt); if (ret < 0) { driver_failure_posix(d->port, errno); return 0; } fprintf(stderr, "Setting fps %d\r\n", cfg->fps); struct v4l2_streamparm parm; memset(&parm, 0, sizeof parm); parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(d->fd, VIDIOC_G_PARM, &parm); if (ret < 0) { driver_failure_posix(d->port, errno); return 0; } parm.parm.capture.timeperframe.numerator = 1; parm.parm.capture.timeperframe.denominator = cfg->fps; ret = ioctl(d->fd, VIDIOC_S_PARM, &parm); if (ret < 0) { driver_failure_posix(d->port, errno); return 0; } fprintf(stderr, "Setting quality 100\r\n"); struct v4l2_jpegcompression jpeg; memset(&jpeg, 0, sizeof jpeg); jpeg.quality = 90; ret = ioctl(d->fd, VIDIOC_S_JPEGCOMP, &jpeg); if (ret < 0) { // driver_failure_posix(d->port, errno); // return 0; fprintf(stderr, "Failed to set quality\r\n"); } if(video_alloc_buffers(d) < 0) { driver_failure_posix(d->port, errno); return 0; } int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(d->fd, VIDIOC_STREAMON, &type); if(ret < 0) { driver_failure_posix(d->port, errno); return 0; } fprintf(stderr, "Capture started\r\n"); driver_select(d->port, (ErlDrvEvent)d->fd, DO_READ, 1); memcpy(*rbuf, "ok", 2); return 2; } break; default: return 0; } return 0; }
static void enm_outputv(ErlDrvData drv_data, ErlIOVec *ev) { EnmData* d = (EnmData*)drv_data; ErlIOVec qev; ErlDrvSizeT qtotal; struct nn_msghdr msghdr; int i, rc = -1, err; if (d->fd == -1 || d->protocol == NN_PULL || d->protocol == NN_SUB) return; qtotal = driver_peekqv(d->port, &qev); if (qtotal > 0) { memset(&msghdr, 0, sizeof msghdr); msghdr.msg_iov = (struct nn_iovec*)qev.iov; msghdr.msg_iovlen = qev.vsize; msghdr.msg_control = 0; err = 0; do { rc = enm_do_send(d, &msghdr, &err); if (rc < 0) { if (err == EAGAIN) { d->b.writable = 0; break; } else if (err != EINTR) { char errstr[32]; switch (enm_errno_str(err, errstr)) { case ENM_NANOMSG_ERROR: driver_failure_atom(d->port, errstr); break; case ENM_POSIX_ERROR: driver_failure_posix(d->port, err); break; case ENM_UNKNOWN_ERROR: driver_failure(d->port, err); break; } return; } } } while (err == EINTR); } /* * Do nothing if the message has no data */ if (ev->size == 0 || ev->vsize == 0 || (ev->vsize == 1 && *ev->binv == 0 && ev->iov->iov_len == 0)) return; if (d->b.writable) { memset(&msghdr, 0, sizeof msghdr); msghdr.msg_iov = (struct nn_iovec*)ev->iov; msghdr.msg_iovlen = ev->vsize; msghdr.msg_control = 0; err = 0; do { rc = enm_do_send(d, &msghdr, &err); if (rc < 0) { if (err == EAGAIN) { d->b.writable = 0; break; } else if (err != EINTR) { char errstr[32]; switch (enm_errno_str(err, errstr)) { case ENM_NANOMSG_ERROR: driver_failure_atom(d->port, errstr); break; case ENM_POSIX_ERROR: driver_failure_posix(d->port, err); break; case ENM_UNKNOWN_ERROR: driver_failure(d->port, err); break; } return; } } } while (err == EINTR); } if (rc < 0 && !d->b.writable) { rc = 0; d->b.busy = 1; set_busy_port(d->port, d->b.busy); for (i = 0; i < ev->vsize; i++) { ErlDrvBinary* bin = 0; if (ev->binv[i] != 0) { bin = ev->binv[i]; driver_binary_inc_refc(bin); } else if (ev->iov[i].iov_len > 0) { SysIOVec* vec = &ev->iov[i]; bin = driver_alloc_binary(vec->iov_len); memcpy(bin->orig_bytes, vec->iov_base, vec->iov_len); } if (bin != 0) driver_enq_bin(d->port, bin, 0, bin->orig_size); } if (!d->b.write_poll) enm_write_select(d, 1); } if (rc > 0 && d->protocol == NN_SURVEYOR && d->b.active != ENM_FALSE) enm_read_select(d, 1); }
static void echo_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { EchoDrvData* data_p = (EchoDrvData *) drv_data; ErlDrvPort port = data_p->erlang_port; switch (buf[0]) { case ECHO_DRV_OUTPUT: { driver_output(port, buf+1, len-1); break; } case ECHO_DRV_OUTPUT2: { driver_output2(port, "a", 1, buf+1, len-1); break; } case ECHO_DRV_OUTPUT_BINARY: { ErlDrvBinary *bin = driver_alloc_binary(len-1); memcpy(&bin->orig_bytes, buf+1, len-1); driver_output_binary(port, "a", 1, bin, 1, len - 2); driver_free_binary(bin); break; } case ECHO_DRV_OUTPUTV: { ErlIOVec iov; ErlDrvSizeT sz; driver_enq(port, buf + 1, len - 1); sz = driver_peekqv(port, &iov); driver_outputv(port, "a", 1, &iov, 0); driver_deq(port, sz); break; } case ECHO_DRV_SET_TIMER: { driver_set_timer(port, 10); break; } case ECHO_DRV_FAILURE_EOF: { driver_failure_eof(port); break; } case ECHO_DRV_FAILURE_ATOM: { driver_failure_atom(port, buf+1); break; } case ECHO_DRV_FAILURE_POSIX: { driver_failure_posix(port, EAGAIN); break; } case ECHO_DRV_FAILURE: { driver_failure(port, buf[1]); break; } case ECHO_DRV_OUTPUT_TERM: case ECHO_DRV_DRIVER_OUTPUT_TERM: case ECHO_DRV_SEND_TERM: case ECHO_DRV_DRIVER_SEND_TERM: { ErlDrvTermData term[] = { ERL_DRV_ATOM, driver_mk_atom("echo"), ERL_DRV_PORT, driver_mk_port(port), ERL_DRV_BUF2BINARY, (ErlDrvTermData)(buf+1), (ErlDrvTermData)(len - 1), ERL_DRV_TUPLE, 3}; switch (buf[0]) { case ECHO_DRV_OUTPUT_TERM: erl_drv_output_term(driver_mk_port(port), term, sizeof(term) / sizeof(ErlDrvTermData)); break; case ECHO_DRV_DRIVER_OUTPUT_TERM: driver_output_term(port, term, sizeof(term) / sizeof(ErlDrvTermData)); break; case ECHO_DRV_SEND_TERM: driver_send_term(port, data_p->caller, term, sizeof(term) / sizeof(ErlDrvTermData)); break; case ECHO_DRV_DRIVER_SEND_TERM: erl_drv_send_term(driver_mk_port(port), data_p->caller, term, sizeof(term) / sizeof(ErlDrvTermData)); break; } break; } case ECHO_DRV_SAVE_CALLER: data_p->caller = driver_caller(port); break; default: break; } }
static void send_term_drv_run(ErlDrvData port, char *buf, ErlDrvSizeT count) { char buf7[1024]; ErlDrvTermData spec[1024]; ErlDrvTermData* msg = spec; ErlDrvBinary* bins[15]; int bin_ix = 0; ErlDrvSInt64 s64[15]; int s64_ix = 0; ErlDrvUInt64 u64[15]; int u64_ix = 0; int i = 0; for (i=0; i<count; i++) switch (buf[i]) { case 0: msg[0] = ERL_DRV_NIL; msg += 1; break; case 1: /* Most term types inside a tuple. */ { double f = 3.1416; msg[0] = ERL_DRV_ATOM; msg[1] = driver_mk_atom("blurf"); msg[2] = ERL_DRV_INT; msg[3] = (ErlDrvTermData) 42; msg[4] = ERL_DRV_NIL; msg[5] = ERL_DRV_INT; msg[6] = (ErlDrvTermData) -42; msg[7] = ERL_DRV_TUPLE; msg[8] = (ErlDrvTermData) 0; msg[9] = ERL_DRV_PORT; msg[10] = driver_mk_port(erlang_port); msg[11] = ERL_DRV_STRING_CONS; msg[12] = (ErlDrvTermData) "abc"; msg[13] = (ErlDrvTermData) 3; msg[14] = ERL_DRV_LIST; msg[15] = (ErlDrvTermData) 3; msg[16] = ERL_DRV_STRING; msg[17] = (ErlDrvTermData) "kalle"; msg[18] = (ErlDrvTermData) 5; msg[19] = ERL_DRV_FLOAT; msg[20] = (ErlDrvTermData) &f; msg[21] = ERL_DRV_PID; msg[22] = driver_connected(erlang_port); msg[23] = ERL_DRV_MAP; msg[24] = (ErlDrvTermData) 0; msg[25] = ERL_DRV_TUPLE; msg[26] = (ErlDrvTermData) 8; msg += 27; } break; case 2: /* Deep stack */ { int i; for (i = 0; i < 400; i += 2) { msg[i] = ERL_DRV_INT; msg[i+1] = (ErlDrvTermData) (i / 2); } msg[i] = ERL_DRV_NIL; msg[i+1] = ERL_DRV_LIST; msg[i+2] = (ErlDrvTermData) 201; msg += i+3; } break; case 3: /* Binaries */ { ErlDrvBinary* bin; int i; bin = bins[bin_ix++] = driver_alloc_binary(256); for (i = 0; i < 256; i++) { bin->orig_bytes[i] = i; } msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) bin; msg[2] = (ErlDrvTermData) 256; msg[3] = (ErlDrvTermData) 0; msg[4] = ERL_DRV_BINARY; msg[5] = (ErlDrvTermData) bin; msg[6] = (ErlDrvTermData) 256-23-17; msg[7] = (ErlDrvTermData) 23; msg[8] = ERL_DRV_TUPLE; msg[9] = (ErlDrvTermData) 2; msg += 10; } break; case 4: /* Pids */ msg[0] = ERL_DRV_PID; msg[1] = driver_connected(erlang_port); msg[2] = ERL_DRV_PID; msg[3] = driver_caller(erlang_port); msg[4] = ERL_DRV_TUPLE; msg[5] = (ErlDrvTermData) 2; msg += 6; break; case 5: msg += make_ext_term_list(msg, 0); break; case 6: msg[0] = ERL_DRV_INT; msg[1] = ~((ErlDrvTermData) 0); msg[2] = ERL_DRV_UINT; msg[3] = ~((ErlDrvTermData) 0); msg[4] = ERL_DRV_TUPLE; msg[5] = (ErlDrvTermData) 2; msg += 6; break; case 7: { int len = 0; memset(buf7, 17, sizeof(buf7)); /* empty heap binary */ msg[len++] = ERL_DRV_BUF2BINARY; msg[len++] = (ErlDrvTermData) NULL; /* NULL is ok if size == 0 */ msg[len++] = (ErlDrvTermData) 0; /* empty heap binary again */ msg[len++] = ERL_DRV_BUF2BINARY; msg[len++] = (ErlDrvTermData) buf7; /* ptr is ok if size == 0 */ msg[len++] = (ErlDrvTermData) 0; /* heap binary */ msg[len++] = ERL_DRV_BUF2BINARY; msg[len++] = (ErlDrvTermData) buf7; msg[len++] = (ErlDrvTermData) 17; /* off heap binary */ msg[len++] = ERL_DRV_BUF2BINARY; msg[len++] = (ErlDrvTermData) buf7; msg[len++] = (ErlDrvTermData) sizeof(buf7); msg[len++] = ERL_DRV_TUPLE; msg[len++] = (ErlDrvTermData) 4; msg += len; break; } case 8: msg[0] = ERL_DRV_NIL; msg += 1; break; case 9: msg[0] = ERL_DRV_ATOM; msg[1] = (ErlDrvTermData) driver_mk_atom(""); msg += 2; break; case 10: msg[0] = ERL_DRV_ATOM; msg[1] = (ErlDrvTermData) driver_mk_atom("an_atom"); msg += 2; break; case 11: msg[0] = ERL_DRV_INT; msg[1] = (ErlDrvTermData) -4711; msg += 2; break; case 12: msg[0] = ERL_DRV_UINT; msg[1] = (ErlDrvTermData) 4711; msg += 2; break; case 13: msg[0] = ERL_DRV_PORT; msg[1] = driver_mk_port(erlang_port); msg += 2; break; case 14: { ErlDrvBinary *dbin = bins[bin_ix++] = driver_alloc_binary(0); msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) dbin; msg[2] = (ErlDrvTermData) 0; msg[3] = (ErlDrvTermData) 0; msg += 4; break; } case 15: { static const char buf[] = "hejsan"; ErlDrvBinary *dbin = bins[bin_ix++] = driver_alloc_binary(sizeof(buf)-1); if (dbin) memcpy((void *) dbin->orig_bytes, (void *) buf, sizeof(buf)-1); msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) dbin; msg[2] = (ErlDrvTermData) (dbin ? sizeof(buf)-1 : 0); msg[3] = (ErlDrvTermData) 0; msg += 4; break; } case 16: msg[0] = ERL_DRV_BUF2BINARY; msg[1] = (ErlDrvTermData) NULL; msg[2] = (ErlDrvTermData) 0; msg += 3; break; case 17: { static const char buf[] = ""; msg[0] = ERL_DRV_BUF2BINARY; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; msg += 3; break; } case 18: { static const char buf[] = "hoppsan"; msg[0] = ERL_DRV_BUF2BINARY; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; msg += 3; break; } case 19: msg[0] = ERL_DRV_STRING; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) 0; msg += 3; break; case 20: { static const char buf[] = ""; msg[0] = ERL_DRV_STRING; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; msg += 3; break; } case 21: { static const char buf[] = "hippsan"; msg[0] = ERL_DRV_STRING; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; msg += 3; break; } case 22: msg[0] = ERL_DRV_TUPLE; msg[1] = (ErlDrvTermData) 0; msg += 2; break; case 23: msg[0] = ERL_DRV_NIL; msg[1] = ERL_DRV_LIST; msg[2] = (ErlDrvTermData) 1; msg += 3; break; case 24: msg[0] = ERL_DRV_PID; msg[1] = driver_connected(erlang_port); msg += 2; break; case 25: msg[0] = ERL_DRV_NIL; msg[1] = ERL_DRV_STRING_CONS; msg[2] = (ErlDrvTermData) ""; msg[3] = (ErlDrvTermData) 0; msg += 4; break; case 26: { static double my_float = 0.0; msg[0] = ERL_DRV_FLOAT; msg[1] = (ErlDrvTermData) &my_float; msg += 2; break; } case 27: { static char buf[] = {131, 106}; /* [] */ msg[0] = ERL_DRV_EXT2TERM; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf); msg += 3; break; } case 28: { ErlDrvUInt64* x = &u64[u64_ix++]; *x = ~((ErlDrvUInt64) 0); msg[0] = ERL_DRV_UINT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 29: { ErlDrvUInt64* x = &u64[u64_ix++]; *x = ((ErlDrvUInt64) 4711) << 32; msg[0] = ERL_DRV_UINT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 30: { ErlDrvUInt64* x = &u64[u64_ix++]; *x = 4711; msg[0] = ERL_DRV_UINT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 31: { ErlDrvUInt64* x = &u64[u64_ix++]; *x = 0; msg[0] = ERL_DRV_UINT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 32: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = ((((ErlDrvUInt64) 0x7fffffff) << 32) | ((ErlDrvUInt64) 0xffffffff)); msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 33: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = (ErlDrvSInt64) (((ErlDrvUInt64) 4711) << 32); msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 34: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = 4711; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 35: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = 0; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 36: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = -1; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 37: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = -4711; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 38: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = ((ErlDrvSInt64) ((ErlDrvUInt64) 4711) << 32)*-1; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 39: { ErlDrvSInt64* x = &s64[s64_ix++]; *x = ((ErlDrvSInt64) 1) << 63; msg[0] = ERL_DRV_INT64; msg[1] = (ErlDrvTermData) x; msg += 2; break; } case 40: { msg[0] = ERL_DRV_MAP; msg[1] = (ErlDrvTermData) 0; msg += 2; break; } case 41: /* Most term types inside a map */ case 42: { double f = 3.1416; if (buf[i] == 41) { *msg++ = ERL_DRV_ATOM; *msg++ = driver_mk_atom("blurf"); } *msg++ = ERL_DRV_INT; *msg++ = (ErlDrvTermData)42; *msg++ = ERL_DRV_NIL; *msg++ = ERL_DRV_INT; *msg++ = (ErlDrvTermData)-42; *msg++ = ERL_DRV_TUPLE; *msg++ = (ErlDrvTermData)0; *msg++ = ERL_DRV_PORT; *msg++ = driver_mk_port(erlang_port); *msg++ = ERL_DRV_STRING_CONS; *msg++ = (ErlDrvTermData)"abc"; *msg++ = (ErlDrvTermData)3; *msg++ = ERL_DRV_LIST; *msg++ = (ErlDrvTermData)3; *msg++ = ERL_DRV_STRING; *msg++ = (ErlDrvTermData)"kalle"; *msg++ = (ErlDrvTermData)5; *msg++ = ERL_DRV_FLOAT; *msg++ = (ErlDrvTermData)&f; *msg++ = ERL_DRV_PID; *msg++ = driver_connected(erlang_port); *msg++ = ERL_DRV_MAP; *msg++ = (ErlDrvTermData)0; if (buf[i] == 42) { *msg++ = ERL_DRV_ATOM; *msg++ = driver_mk_atom("blurf"); } *msg++ = ERL_DRV_MAP; *msg++ = (ErlDrvTermData)4; break; } case 127: /* Error cases */ { long refc; ErlDrvBinary* bin = bins[bin_ix++] = driver_alloc_binary(256); FAIL_TERM(msg, 0); msg[0] = ERL_DRV_LIST; msg[1] = (ErlDrvTermData) 0; FAIL_TERM(msg, 2); /* Not an atom */ msg[0] = ERL_DRV_ATOM; msg[1] = (ErlDrvTermData) driver_connected(erlang_port); FAIL_TERM(msg, 2); msg[0] = ERL_DRV_ATOM; msg[1] = driver_term_nil; FAIL_TERM(msg, 2); /* Not a pid */ msg[0] = ERL_DRV_PID; msg[1] = (ErlDrvTermData) driver_mk_atom("blurf"); FAIL_TERM(msg, 2); msg[0] = ERL_DRV_PID; msg[1] = driver_term_nil; FAIL_TERM(msg, 2); /* Not a port */ msg[0] = ERL_DRV_PORT; msg[1] = (ErlDrvTermData) driver_mk_atom("blurf"); FAIL_TERM(msg, 2); msg[0] = ERL_DRV_PORT; msg[1] = driver_term_nil; FAIL_TERM(msg, 2); /* Missing parameter on stack */ msg[0] = ERL_DRV_STRING_CONS; msg[1] = (ErlDrvTermData) "abc"; msg[2] = (ErlDrvTermData) 3; FAIL_TERM(msg, 3); /* * The first binary reference is correct, the second is incorrect. * There should not be any "binary leak". */ msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) bin; msg[2] = (ErlDrvTermData) 256; msg[3] = (ErlDrvTermData) 0; msg[4] = ERL_DRV_BINARY; msg[5] = (ErlDrvTermData) bin; msg[6] = (ErlDrvTermData) 257; msg[7] = (ErlDrvTermData) 0; msg[8] = ERL_DRV_TUPLE; msg[9] = (ErlDrvTermData) 2; FAIL_TERM(msg, 10); msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) bin; msg[2] = (ErlDrvTermData) 256; msg[3] = (ErlDrvTermData) 0; msg[4] = ERL_DRV_BINARY; msg[5] = (ErlDrvTermData) bin; msg[6] = (ErlDrvTermData) 256; msg[7] = (ErlDrvTermData) 50; msg[8] = ERL_DRV_TUPLE; msg[9] = (ErlDrvTermData) 2; FAIL_TERM(msg, 10); /* * We have succefully built two binaries. We expect the ref count * to be 1 (SMP) or 3 (non-SMP). */ refc = driver_binary_get_refc(bin); if (refc > 3) { char sbuf[128]; sprintf(sbuf, "bad_refc:%ld", refc); driver_failure_atom(erlang_port, sbuf); } driver_free_binary(bin); FAIL_TERM(msg, make_ext_term_list(msg, 1)); /* * Check that we fail for missing args. * * We setup valid terms but pass a too small size. We * want valid terms since we want to verify that the * failure really is due to the small size. */ msg[0] = ERL_DRV_ATOM; msg[1] = (ErlDrvTermData) driver_mk_atom("an_atom"); FAIL_TERM(msg, 1); msg[0] = ERL_DRV_INT; msg[1] = (ErlDrvTermData) -4711; FAIL_TERM(msg, 1); msg[0] = ERL_DRV_UINT; msg[1] = (ErlDrvTermData) 4711; FAIL_TERM(msg, 1); msg[0] = ERL_DRV_PORT; msg[1] = driver_mk_port(erlang_port); FAIL_TERM(msg, 1); { char buf[] = "hejsan"; ErlDrvBinary *dbin = driver_alloc_binary(sizeof(buf)-1); if (!dbin) driver_failure_posix(erlang_port, ENOMEM); else { memcpy((void *) dbin->orig_bytes, (void *) buf, sizeof(buf)-1); msg[0] = ERL_DRV_BINARY; msg[1] = (ErlDrvTermData) dbin; msg[2] = (ErlDrvTermData) sizeof(buf)-1; msg[3] = (ErlDrvTermData) 0; FAIL_TERM(msg, 1); FAIL_TERM(msg, 2); FAIL_TERM(msg, 3); driver_free_binary(dbin); } } { char buf[] = "hoppsan"; msg[0] = ERL_DRV_BUF2BINARY; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; FAIL_TERM(msg, 1); FAIL_TERM(msg, 2); } { char buf[] = "hippsan"; msg[0] = ERL_DRV_STRING; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf)-1; FAIL_TERM(msg, 1); FAIL_TERM(msg, 2); } msg[0] = ERL_DRV_TUPLE; msg[1] = (ErlDrvTermData) 0; FAIL_TERM(msg, 1); msg[0] = ERL_DRV_NIL; msg[1] = ERL_DRV_LIST; msg[2] = (ErlDrvTermData) 1; FAIL_TERM(msg, 2); msg[0] = ERL_DRV_PID; msg[1] = driver_connected(erlang_port); FAIL_TERM(msg, 1); msg[0] = ERL_DRV_NIL; msg[1] = ERL_DRV_STRING_CONS; msg[2] = (ErlDrvTermData) ""; msg[3] = (ErlDrvTermData) 0; FAIL_TERM(msg, 2); FAIL_TERM(msg, 3); { double my_float = 0.0; msg[0] = ERL_DRV_FLOAT; msg[1] = (ErlDrvTermData) &my_float; FAIL_TERM(msg, 1); } { char buf[] = {131, 106}; /* [] */ msg[0] = ERL_DRV_EXT2TERM; msg[1] = (ErlDrvTermData) buf; msg[2] = (ErlDrvTermData) sizeof(buf); FAIL_TERM(msg, 1); FAIL_TERM(msg, 2); } msg[0] = ERL_DRV_MAP; msg[1] = (ErlDrvTermData) 0; FAIL_TERM(msg, 1); /* map with duplicate key */ msg[0] = ERL_DRV_ATOM; msg[1] = driver_mk_atom("key"); msg[2] = ERL_DRV_NIL; msg[3] = ERL_DRV_ATOM; msg[4] = driver_mk_atom("key"); msg[5] = ERL_DRV_INT; msg[6] = (ErlDrvTermData) -4711; msg[7] = ERL_DRV_MAP; msg[8] = 2; FAIL_TERM(msg, 9); /* Signal end of test case */ msg[0] = ERL_DRV_NIL; erl_drv_output_term(driver_mk_port(erlang_port), msg, 1); return; } break; default: driver_failure_atom(erlang_port, "bad_request"); break; } if (count > 1) { *msg++ = ERL_DRV_NIL; *msg++ = ERL_DRV_LIST; *msg++ = count + 1; } output_term(spec, msg-spec); if ((bin_ix|s64_ix|u64_ix) > 15) abort(); while (bin_ix) { driver_free_binary(bins[--bin_ix]); } }
static void tun_output(ErlDrvData data, char* buf, int len) { struct tun_state *state = (struct tun_state *)data; if (write(state->fd, buf, len) < 0) driver_failure_posix(state->port, errno); }