Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial checkin of unixdom_drv (based on EDTK-generated code).

  • Loading branch information...
commit dc26e1d31defadb3a4586fa39ac934d0ad11414b 0 parents
slfritchie authored
28 LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2003, Scott Lystig Fritchie, <slfritchie@snookles.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ Neither the name of Mr. Fritchie nor the names of any other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2  Makefile
@@ -0,0 +1,2 @@
+include ../../support/subdir.mk
+
58 README
@@ -0,0 +1,58 @@
+
+This is a very incomplete reimplementation of the UNIX domain socket driver
+that I wrote and added to the www.erlang.org "User Contributions"
+collection.
+
+There are two things that this driver can do that the original UNIX
+domain socket driver cannot:
+
+ 1. It has a "getfd" call so that Erlang can discover the
+ underlying UNIX file descriptor for the socket.
+
+ 2. Has the ability to use BSD-style file descriptor passing
+ to pass file descriptors between Erlang nodes running
+ on the same machine.
+
+The file descriptor passing has been tested under Linux and FreeBSD. It
+should work under Solaris (minor tweaking perhaps necessary?) and any other
+platform that supports BSD-style file descriptor passing over UNIX domain
+sockets.
+
+To test the file descriptor passing, to the following:
+
+ On machine-A:
+
+ % make ... GNU make is required, sorry!
+ % cd test
+ % make
+ % make test ... optional, but shows some instructions
+ % erl -pz ../ebin
+ 1> file:delete("/tmp/sock").
+ 2> unixdom_test:tcp_listen(5555, "/tmp/sock").
+
+ On machine-A, but in another login session:
+
+ % cd test
+ % erl -pz ../ebin
+ 1> unixdom_test:receive_fd("/tmp/sock").
+ [... some time later ...]
+ TCP socket fd 8 received, looping now
+
+ On machine-B:
+
+ % telnet machine-A 5555
+ Trying 10.1.1.1...
+ Connected to machine-A
+ Escape character is '^]'.
+ Hello, there! Please type some stuff:
+
+If someone would like to fix this driver up, they are more than welcome to!
+I have only done enough to implement the file descriptor passing. My
+intention was to create a minimal implementation this time, relying on
+using gen_tcp:fdopen() or prim_inet:fdopen() to create a full-featured
+socket port, rather than trying to re-invent the wheel (including all of
+the gadgets, like packet encoding types, that the inets driver currently
+supports!).
+
+-Scott
+23 February 2004
29 c_src/Makefile
@@ -0,0 +1,29 @@
+include ../../../support/include.mk
+
+CFLAGS += -I$(ERL_C_INCLUDE_DIR) -I.
+
+UNIXDOM_DRV_SO = ../priv/unixdom_drv.so
+
+SHLIB_OBJS = unixdom_drv.o my-unixdom.o erl_driver_tk.o
+
+all: $(UNIXDOM_DRV_SO)
+
+$(UNIXDOM_DRV_SO): $(SHLIB_OBJS)
+ ld -G -o $@ $^
+ # Symlink is only to help when using "jerl" script.
+ -ln -s $(UNIXDOM_DRV_SO) ../ebin
+
+unixdom_drv.o: unixdom_drv.c
+ $(CC) $(CFLAGS) -o $@ -c -fpic $(ERL_INCLUDE) $<
+
+my-unixdom.o: my-unixdom.c
+ $(CC) $(CFLAGS) -o $@ -c -fpic $(ERL_INCLUDE) $<
+
+erl_driver_tk.o: erl_driver_tk.c
+ $(CC) $(CFLAGS) -o $@ -c -fpic $(ERL_INCLUDE) $<
+
+clean:
+ -rm $(UNIXDOM_DRV_SO) ../ebin/*.so
+
+.INTERMEDIATE: $(SHLIB_OBJS)
+
141 c_src/erl_driver_tk.c
@@ -0,0 +1,141 @@
+/*
+** Copyright (c) 2003, Scott Lystig Fritchie. All rights reserved.
+** See the file "../LICENSE" for license details.
+*/
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <erl_driver.h>
+#include <erl_driver_tk.h>
+
+int edtk_debug_flag = 1;
+
+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;
+ }
+}
+
+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;
+ }
+}
+
+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);
+ }
+}
+
+/* QQQ Make this a macro, use scratch static ErlDrvBinary? */
+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;
+ }
+}
+
+int
+edtk_debug(char *f, ...)
+{
+ va_list ap;
+ int res;
+
+ va_start(ap, f);
+ if (edtk_debug_flag) {
+ res = vfprintf(DEBUGIO, f, ap);
+ fprintf(DEBUGIO, "\r\n");
+ }
+ va_end(ap);
+ return res;
+}
+
+void
+edtk_debug_errcall(const char *errpfx, char *msg)
+{
+ if (edtk_debug_flag)
+ fprintf(stderr, "\t\t\t\t\t%s: %s: %s\r\n", __FUNCTION__, errpfx, msg);
+}
+
+void
+edtk_free_data(void *data)
+{
+ if (data != NULL)
+ sys_free(data);
+}
+
+/*
+** Arg want_contiguous: if true, return error if the 'n' we're forwarding
+** past are in a single contiguous buffer.
+**
+** Return value:
+** -1 = error
+** 0 = Success, there is no more data to be read
+** 1 = Success
+*/
+
+int
+edtk_ev_forward_N(ErlIOVec *ev, int n, int *pp, int *qp, int want_contiguous)
+{
+ int forward_pos = *pp + n;
+
+ if ((*qp) >= ev->vsize) {
+ return -1;
+ }
+ if (forward_pos < ev->iov[*qp].iov_len) {
+ *pp += n;
+ return 1;
+ } else if (forward_pos == ev->iov[*qp].iov_len) {
+ (*qp)++;
+ *pp = 0;
+ if ((*qp) < ev->vsize) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ n -= (ev->iov[*qp].iov_len - *pp);
+ (*qp)++;
+ (*pp) = 0;
+ if ((*qp) < ev->vsize && ! want_contiguous) {
+ return edtk_ev_forward_N(ev, n, pp, qp, want_contiguous);
+ } else {
+ return -1;
+ }
+ }
+}
+
157 c_src/erl_driver_tk.h
@@ -0,0 +1,157 @@
+/*
+** Copyright (c) 2003, Scott Lystig Fritchie. All rights reserved.
+** See the file "../LICENSE" for license details.
+*/
+
+#include <erl_driver.h>
+
+/*
+** ErlIOVec manipulation functions, stolen from efile_drv.c
+*/
+
+/* char *EV_CHAR_P(ErlIOVec *ev, int p, int q) */
+#define EV_CHAR_P(ev, p, q) \
+ (((char *)(ev)->iov[(q)].iov_base) + (p))
+
+/* char *EV_UCHAR_P(ErlIOVec *ev, int p, int q) */
+#define EV_UCHAR_P(ev, p, q) \
+ (((unsigned char *)(ev)->iov[(q)].iov_base) + (p))
+
+/* int EV_GET_CHAR(ErlIOVec *ev, char *p, int *pp, int *qp) */
+#define EV_GET_CHAR(ev, p, pp, qp) \
+ (*(pp)+1 <= (ev)->iov[*(qp)].iov_len \
+ ? (*(p) = *EV_CHAR_P(ev, *(pp), *(qp)), \
+ *(pp) = (*(pp)+1 < (ev)->iov[*(qp)].iov_len \
+ ? *(pp)+1 \
+ : ((*(qp))++, 0)), \
+ !0) \
+ : 0)
+
+#define EV_GET_UINT8(ev, p, pp, qp) EV_GET_CHAR(ev, p, pp, qp)
+
+/* int EV_GET_UINT16(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
+#define EV_GET_UINT16(ev, p, pp, qp) \
+ (*(pp)+2 <= (ev)->iov[*(qp)].iov_len \
+ ? (*((unsigned short *) p) = (*EV_UCHAR_P(ev, *(pp), *(qp)) << 8)\
+ | *EV_UCHAR_P(ev, *(pp)+1, *(qp)), \
+ *(pp) = (*(pp)+2 < (ev)->iov[*(qp)].iov_len \
+ ? *(pp)+2 \
+ : ((*(qp))++, 0)), \
+ !0) \
+ : 0)
+
+/* int EV_GET_UINT32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) */
+#define EV_GET_UINT32(ev, p, pp, qp) \
+ (*(pp)+4 <= (ev)->iov[*(qp)].iov_len \
+ ? (*((unsigned long *) p) = (*EV_UCHAR_P(ev, *(pp), *(qp)) << 24) \
+ | (*EV_UCHAR_P(ev, *(pp)+1, *(qp)) << 16) \
+ | (*EV_UCHAR_P(ev, *(pp)+2, *(qp)) << 8)\
+ | *EV_UCHAR_P(ev, *(pp)+3, *(qp)), \
+ *(pp) = (*(pp)+4 < (ev)->iov[*(qp)].iov_len \
+ ? *(pp)+4 \
+ : ((*(qp))++, 0)), \
+ !0) \
+ : 0)
+
+/* char * EV_GETPOS(ErlIOVec *ev, int p, int q) */
+#define EV_GETPOS(ev, p, q) \
+ ((q) < (ev)->vsize \
+ ? ((ev)->iov[(q)].iov_base + p) \
+ : NULL)
+
+/*********************************************************************
+ * Term manipulation functions, stolen from inet_drv.c
+ */
+
+#define LOAD_NIL(vec, i) \
+ (((vec)[(i)] = ERL_DRV_NIL), \
+ (i+1))
+
+#define LOAD_ATOM(vec, i, atom) \
+ (((vec)[(i)] = ERL_DRV_ATOM), \
+ ((vec)[(i)+1] = (atom)), \
+ (i+2))
+
+#define LOAD_INT(vec, i, val) \
+ (((vec)[(i)] = ERL_DRV_INT), \
+ ((vec)[(i)+1] = (ErlDrvTermData)(val)), \
+ (i+2))
+
+#define LOAD_PORT(vec, i, port) \
+ (((vec)[(i)] = ERL_DRV_PORT), \
+ ((vec)[(i)+1] = (port)), \
+ (i+2))
+
+#define LOAD_PID(vec, i, pid) \
+ (((vec)[(i)] = ERL_DRV_PID), \
+ ((vec)[(i)+1] = (pid)), \
+ (i+2))
+
+#define LOAD_BINARY(vec, i, bin, offs, len) \
+ (((vec)[(i)] = ERL_DRV_BINARY), \
+ ((vec)[(i)+1] = (ErlDrvTermData)(bin)), \
+ ((vec)[(i)+2] = (len)), \
+ ((vec)[(i)+3] = (offs)), \
+ (i+4))
+
+#define LOAD_STRING(vec, i, str, len) \
+ (((vec)[(i)] = ERL_DRV_STRING), \
+ ((vec)[(i)+1] = (ErlDrvTermData)(str)), \
+ ((vec)[(i)+2] = (len)), \
+ (i+3))
+
+#define LOAD_STRING_CONS(vec, i, str, len) \
+ (((vec)[(i)] = ERL_DRV_STRING_CONS), \
+ ((vec)[(i)+1] = (ErlDrvTermData)(str)), \
+ ((vec)[(i)+2] = (len)), \
+ (i+3))
+
+#define LOAD_TUPLE(vec, i, size) \
+ (((vec)[(i)] = ERL_DRV_TUPLE), \
+ ((vec)[(i)+1] = (size)), \
+ (i+2))
+
+#define LOAD_LIST(vec, i, size) \
+ (((vec)[(i)] = ERL_DRV_LIST), \
+ ((vec)[(i)+1] = (size)), \
+ (i+2))
+
+#define get_int32(s) ((((unsigned char*) (s))[0] << 24) | \
+ (((unsigned char*) (s))[1] << 16) | \
+ (((unsigned char*) (s))[2] << 8) | \
+ (((unsigned char*) (s))[3]))
+
+#define DEBUGIO stderr
+
+#define PIPE_DRIVER_TERM_NIL 0
+#define PIPE_DRIVER_TERM_ATOM 1
+#define PIPE_DRIVER_TERM_PORT 2
+#define PIPE_DRIVER_TERM_INT 3
+#define PIPE_DRIVER_TERM_TUPLE 4
+#define PIPE_DRIVER_TERM_BINARY 5
+#define PIPE_DRIVER_TERM_STRING 6
+#define PIPE_DRIVER_TERM_LIST 7
+
+/* Forward declarations */
+struct descriptor;
+typedef struct descriptor descriptor_t;
+
+extern int edtk_debug_flag; /* For verbose debugging trace messages */
+
+/* Wrapper functions for driver_{alloc,realloc,free}_binary */
+void *edtk_driver_alloc_wrapper(size_t);
+void *edtk_driver_realloc_wrapper(void *, size_t);
+void edtk_driver_free_wrapper(void *);
+ErlDrvBinary *edtk_alloced_ptr2ErlDrvBinary(void *);
+
+/* QQQ Where are these from? */
+void *sys_alloc(size_t);
+void *sys_realloc(void *, size_t);
+void sys_free(void *);
+
+int edtk_debug(char *f, ...);
+void edtk_debug_errcall(const char *, char *);
+void edtk_free_data(void *data);
+
+int edtk_ev_forward_N(ErlIOVec *ev, int n, int *pp, int *qp, int);
+
170 c_src/my-unixdom.c
@@ -0,0 +1,170 @@
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <my-unixdom.h>
+
+void
+null(void)
+{
+ printf("Hello, world! This is null()\r\n");
+}
+
+int
+my_open(char *path, int is_server)
+{
+ struct stat sb;
+ struct sockaddr_un sin;
+ int s;
+ int res;
+
+ /* Race condition exists, but is better than nothing */
+ if (is_server && stat(path, &sb) == 0) {
+ errno = EEXIST;
+ return -1;
+ }
+ if ((s = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ return -1;
+ }
+
+ sin.sun_family = AF_UNIX;
+ strcpy(sin.sun_path, path);
+#if defined(SUN_LEN) && (! (defined(solaris) || defined(linux)))
+ sin.sun_len = SUN_LEN(&sin);
+#endif /* SUN_LEN */
+
+ if (is_server) {
+ res = bind(s, (struct sockaddr *) &sin, sizeof(sin));
+ } else {
+ res = connect(s, (struct sockaddr *) &sin, sizeof(sin));
+ }
+ if (res < 0) {
+ return -1;
+ } else {
+ return s;
+ }
+}
+
+int
+my_getfd(int fd)
+{
+ return fd;
+}
+
+int
+my_sendfd(int fd, int wfd)
+{
+ if (writefd(fd, wfd) >= 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int
+my_receivefd(int fd)
+{
+ int passed_fd = -42;
+
+ if (readfd(fd, &passed_fd) < 0) {
+ return -1;
+ } else {
+ return passed_fd;
+ }
+}
+
+/*
+** My interpretation of Steven's file descriptor passing code.
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <signal.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <netinet/in.h> /* htonl() et al */
+#ifndef TCP_NODELAY
+#include <netinet/tcp.h>
+#endif /* TCP_NODELAY */
+#include <sys/uio.h>
+
+ssize_t
+writevfd(int fd, const struct iovec *iov, int iovcnt, int wfd)
+{
+ struct msghdr mh;
+ char cmhbuf[sizeof(struct cmsghdr) + sizeof(int)];
+ struct cmsghdr *const cmh = (struct cmsghdr *) cmhbuf;
+ int *const fdp = (int *) (cmhbuf + sizeof(struct cmsghdr));
+
+ *fdp = wfd;
+ memset(&mh, 0, sizeof(mh));
+ mh.msg_iov = (struct iovec *) iov;
+ mh.msg_iovlen = iovcnt;
+
+ mh.msg_control = (char *) cmh;
+ mh.msg_controllen = cmh->cmsg_len = sizeof(cmhbuf) /* + sizeof(int) */;
+ cmh->cmsg_level = SOL_SOCKET;
+ cmh->cmsg_type = SCM_RIGHTS;
+
+ return sendmsg(fd, &mh, 0);
+}
+
+ssize_t
+writefd(int fd, int wfd)
+{
+ void *buf = "b"; /* Gotta send at least 1 byte along with msg */
+ size_t len = 1;
+ struct iovec iov[1];
+
+ iov->iov_base = (void *) buf;
+ iov->iov_len = len;
+ return writevfd(fd, iov, 1, wfd);
+}
+
+
+ssize_t
+readvfd(int fd, const struct iovec *iov, int iovcnt, int *rfdp)
+{
+ struct msghdr mh;
+ char cmhbuf[sizeof(struct cmsghdr) + sizeof(int)];
+ struct cmsghdr *const cmh = (struct cmsghdr *) cmhbuf;
+ int *const fdp = (int *) (cmhbuf + sizeof(struct cmsghdr));
+ int n;
+
+ *fdp = -1;
+ memset(&mh, 0, sizeof(mh));
+ mh.msg_iov = (struct iovec *) iov;
+ mh.msg_iovlen = iovcnt;
+
+ mh.msg_control = (char *) cmh;
+ mh.msg_controllen = cmh->cmsg_len = sizeof(cmhbuf) /* + sizeof(int) */;
+ cmh->cmsg_level = SOL_SOCKET;
+ cmh->cmsg_type = SCM_RIGHTS;
+
+ n = recvmsg(fd, &mh, 0);
+ *rfdp = *fdp;
+ return n;
+}
+
+ssize_t
+readfd(int fd, int *rfdp)
+{
+ char readbuf[128];
+ size_t len = sizeof(readbuf);
+ struct iovec iov[1];
+
+ iov->iov_base = readbuf;
+ iov->iov_len = len;
+ return readvfd(fd, iov, 1, rfdp);
+}
11 c_src/my-unixdom.h
@@ -0,0 +1,11 @@
+
+void null(void);
+int my_open(char *, int);
+int my_getfd(int);
+int my_sendfd(int fd, int wfd);
+int my_receivefd(int fd);
+
+ssize_t writevfd(int fd, const struct iovec *iov, int iovcnt, int wfd);
+ssize_t writefd(int fd, int wfd);
+ssize_t readvfd(int fd, const struct iovec *iov, int iovcnt, int *rfdp);
+ssize_t readfd(int fd, int *rfdp);
894 c_src/unixdom_drv.c
@@ -0,0 +1,894 @@
+/*
+** File : unixdom_drv.c
+** Summary : EDTK implementation of UNIX domain socket driver (incomplete!)
+**
+** NOTICE: This file was generated by the tools of the Erlang Driver
+** toolkit. Do not edit this file by hand unless you know
+** what you're doing!
+**
+** Copyright (c) 2004, Scott Lystig Fritchie. All rights reserved.
+** See the file "LICENSE" at the top of the source distribution for
+** full license terms.
+*/
+
+/*
+** XXX Before I forget yet again to write this down...
+**
+** ... the valmap ID assignment has a weakness that should be fixed
+** sometime in the future. The weakness is that reusing the array
+** indexes could result in Erlang being able to access a later
+** incarnation of a valmap table entry by simply remembering a
+** previous valmap {valmap_blah, Integer} and resending it to the
+** driver, hoping to get (un)lucky.
+**
+** The solution would be to choose a valmap index from a larger, very
+** unlikely to repeat set, and use a mapping data structure less naive
+** than a dumb array.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <assert.h>
+#ifdef DRIVER_USING_PTHREADS
+#include <pthread.h>
+#else /* DRIVER_USING_PTHREADS */
+#define pthread_self() 0
+#endif /* DRIVER_USING_PTHREADS */
+
+/* TODO: Add additional system & local header file #includes */
+
+#include <erl_driver.h>
+#include <erl_driver_tk.h>
+
+/* <verbatim place="top_cpp_stuff"> */
+
+#include <stdio.h>
+#include <time.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h> /* htonl() et al. */
+
+#include <my-unixdom.h>
+
+/* </verbatim --place="top_cpp_stuff"--> */
+
+/* Last, but not least.... */
+#include <unixdom_drv.h>
+
+
+static ErlDrvTermData am_ok;
+static ErlDrvTermData am_error;
+static ErlDrvTermData am_badarg;
+static ErlDrvTermData am_enomem;
+static ErlDrvTermData am_unknown;
+static ErlDrvTermData am_valmap_fd;
+
+static int default_async_calls = 0;
+
+/* This variable may be set only by pipe-main! */
+int pipe_driver_p = 0;
+
+/*
+** TODO: Define any other Erlang terms this driver may return.
+*/
+
+/* Function prototypes */
+
+ErlDrvEntry *driver_init(void *); /* Do not change name! */
+static int s1_init(void);
+static ErlDrvData s1_start(ErlDrvPort port, char *command);
+static void s1_stop(ErlDrvData drv_data);
+static void s1_output(ErlDrvData drv_data, char *buf, int len);
+static int s1_control(ErlDrvData drv_data, unsigned int command, char *buf, int len, char **rbuf, int rlen);
+static void s1_outputv(ErlDrvData drv_data, ErlIOVec *ev);
+static void s1_ready_async(ErlDrvData drv_data, ErlDrvThreadData thread_data);
+
+void *sys_alloc(size_t);
+void *sys_realloc(void *, size_t);
+void sys_free(void *);
+
+static void invoke_s1_null(void *data);
+static void invoke_s1_open(void *data);
+static void invoke_s1_getfd(void *data);
+static void invoke_s1_sendfd(void *data);
+static void invoke_s1_receivefd(void *data);
+static void invoke_s1_close(void *data);
+static void invoke_s1_write(void *data);
+static void invoke_s1_read(void *data);
+
+static int reply_xtra_1_ptr_ssize_t(descriptor_t *, callstate_t *);
+
+static int find_unused_fd_index(descriptor_t *, unsigned long *);
+static void cleanup_valmap_fd_index(descriptor_t *, int, int);
+static void cleanup_valmap_fd_all(descriptor_t *, int);
+
+static int reply_ok(descriptor_t *desc);
+static int reply_ok_num(descriptor_t *desc, unsigned long num);
+static int reply_ok_binary(descriptor_t *desc, char *p, int, int);
+static int reply_ok_valmap(descriptor_t *, ErlDrvTermData, unsigned long);
+static int reply_error(descriptor_t *desc, int errnum);
+static int reply_error_atom(descriptor_t *, ErlDrvTermData);
+#if 0
+static int reply_tag_ok(descriptor_t *desc, unsigned short tag);
+static int reply_tag_error(descriptor_t *desc, unsigned short tag, int errnum);
+#endif /* 0 */
+
+static ErlDrvEntry s1_driver_entry = {
+ s1_init, /* init */
+ s1_start, /* start */
+ s1_stop, /* stop */
+ s1_output, /* output */
+ NULL, /* ready_input */
+ NULL, /* ready_output */
+ "unixdom_drv", /* driver_name */
+ NULL, /* finish */
+ NULL, /* handle */
+ s1_control, /* control */
+ NULL, /* timeout */
+ s1_outputv, /* outputv */
+ s1_ready_async, /* ready_async */
+ NULL, /* flush */
+ NULL /* call */
+};
+
+/*
+** All dynamically-loadable driver libraries must contain a driver_init().
+*/
+
+ErlDrvEntry *
+driver_init(void *handle)
+{
+ edtk_debug_flag = 0;
+ s1_driver_entry.handle = handle;
+ return &s1_driver_entry;
+}
+
+static int
+s1_init(void)
+{
+ am_ok = driver_mk_atom("ok");
+ am_error = driver_mk_atom("error");
+ am_badarg = driver_mk_atom("badarg");
+ am_enomem = driver_mk_atom("enomem");
+ am_unknown = driver_mk_atom("unknown");
+ am_valmap_fd = driver_mk_atom("valmap_fd");
+
+ /* TODO: Take care of other first-time initialization tasks */
+
+ return 0;
+}
+
+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 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 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__);
+}
+
+
+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;
+}
+
+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 (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;
+ 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;
+ case S1_WRITE:
+ c->invoke = invoke_s1_write;
+ 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;
+ }
+ EV_GET_UINT32(ev, &binlen, &p, &q);
+ c->i.__stash[0] = binlen;
+ c->i.ptr = (char *) EV_GETPOS(ev, p, q);
+ if (edtk_ev_forward_N(ev, binlen, &p, &q, 1) < 0) {
+ goto error;
+ }
+ break;
+ case S1_READ:
+ c->invoke = invoke_s1_read;
+ 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;
+ }
+ EV_GET_UINT32(ev, &c->i.size, &p, &q);
+ /* <hack place="post-deserialize" type="verbatim"> */
+
+ edtk_debug("XXX c->i.size = %d\r\n", c->i.size);
+ if ((c->i.ptr = (char *) edtk_driver_alloc_wrapper(c->i.size)) == NULL) {
+ goto error;
+ }
+
+ /* </hack --place="post-deserialize" type="verbatim"--> */
+ break;
+ default:
+ edtk_debug("%s: invalid command %d", __FUNCTION__, cmd);
+ goto error;
+ 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);
+}
+
+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;
+
+ p = p;
+ bytes = bytes;
+ offset = offset;
+ i = i;
+ index = index;
+ 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;
+ case S1_WRITE:
+ reply_ok_num(desc, c->o.ret_ssize_t);
+ break;
+ case S1_READ:
+ reply_xtra_1_ptr_ssize_t(desc, c);
+ 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;
+
+ c = c;
+ edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self());
+ null(
+ );
+ edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self());
+}
+
+static void
+invoke_s1_open(void *data)
+{
+ callstate_t *c = (callstate_t *) data;
+
+ c = c;
+ 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 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());
+}
+
+static void
+invoke_s1_sendfd(void *data)
+{
+ callstate_t *c = (callstate_t *) data;
+
+ c = c;
+ edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self());
+ c->o.ret_int_t = my_sendfd(
+ c->i.unixdom_fd,
+ c->i.fd_to_be_sent
+ );
+ 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
+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
+invoke_s1_close(void *data)
+{
+ callstate_t *c = (callstate_t *) data;
+
+ c = c;
+ edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self());
+ c->o.ret_int = close(
+ c->i.fd
+ );
+ 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 void
+invoke_s1_write(void *data)
+{
+ callstate_t *c = (callstate_t *) data;
+
+ c = c;
+ edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self());
+ c->o.ret_ssize_t = write(
+ c->i.fd,
+ c->i.ptr,
+ c->i.__stash[0]
+ );
+ edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self());
+}
+
+static void
+invoke_s1_read(void *data)
+{
+ callstate_t *c = (callstate_t *) data;
+
+ c = c;
+ edtk_debug("%s: threadid = %lx", __FUNCTION__, pthread_self());
+ c->o.ret_ssize_t = read(
+ c->i.fd,
+ c->i.ptr,
+ c->i.size
+ );
+ edtk_debug("%s: threadid = %lx done", __FUNCTION__, pthread_self());
+}
+
+
+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_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 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 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 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 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;
+}
+
+#if 0 /* XXX These are unused right now */
+static int
+reply_tag_ok(descriptor_t *desc, unsigned short tag)
+{
+ ErlDrvTermData msg[24];
+ int i = 0;
+ int res;
+
+ i = LOAD_PORT(msg, i, driver_mk_port(desc->port));
+ i = LOAD_INT(msg, i, tag);
+ i = LOAD_ATOM(msg, i, am_ok);
+ i = LOAD_TUPLE(msg, i, 3);
+ 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_tag_error(descriptor_t *desc, unsigned short tag, 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_INT(msg, i, tag);
+ i = LOAD_INT(msg, i, errnum);
+ i = LOAD_ATOM(msg, i, am_error);
+ i = LOAD_TUPLE(msg, i, 4);
+ edtk_debug("reply_error: i = %d", i);
+ res = driver_output_term(desc->port, msg, i);
+ edtk_debug("reply_error: res = %d", res);
+ return res;
+}
+#endif /* 0 */
+
+
+static int
+reply_xtra_1_ptr_ssize_t(descriptor_t *desc, callstate_t *c)
+{
+ ErlDrvTermData msg[MAX_RETURN_TERMS];
+ int msgcount = 0;
+ int res;
+ int members = 0;
+ char *tmp = NULL;
+ int i;
+ ErlDrvBinary *tmpbin = NULL;
+
+ tmp = tmp; tmpbin = tmpbin;
+ desc->num_tofree = 0;
+
+ msgcount = LOAD_PORT(msg, msgcount, driver_mk_port(desc->port));
+ members = 2; /* members will be 2 very shortly: Port & ok|error */
+ if (c->o.__expect) {
+ msgcount = LOAD_ATOM(msg, msgcount, am_ok);
+ {
+ int members = 0;
+ tmpbin = edtk_alloced_ptr2ErlDrvBinary(c->i.ptr);
+ msgcount = LOAD_BINARY(msg, msgcount, tmpbin, 0, c->o.ret_ssize_t);
+ /* driver_output_term() incrs refc, and we're done, so decr refc LATER */
+ desc->tofree[desc->num_tofree++] = tmpbin;
+ members++;
+ msgcount = LOAD_TUPLE(msg, msgcount, members);
+ }
+ } else {
+ msgcount = LOAD_ATOM(msg, msgcount, am_error);
+ {
+ int members = 0;
+ msgcount = LOAD_TUPLE(msg, msgcount, members);
+ }
+ }
+ msgcount = LOAD_TUPLE(msg, msgcount, 3);
+ edtk_debug("reply_xtra_1_ptr_ssize_t: i = %d", msgcount);
+ res = driver_output_term(desc->port, msg, msgcount);
+ edtk_debug("reply_xtra_1_ptr_ssize_t: res = %d", res);
+ if (res < 0) {
+ fprintf(stderr, "\r\n\r\nreply_xtra_1_ptr_ssize_t: driver_output_term() failed! This should never happen!\r\n\r\n");
+ }
+
+ for (i = 0; i < desc->num_tofree; i++) {
+ driver_free_binary(desc->tofree[i]);
+ }
+ return res;
+}
+
+
+static int
+find_unused_fd_index(descriptor_t *desc, unsigned long *index_p)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (desc->valmap_fd[i] == -1) {
+ *index_p = i;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+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;
+}
+
+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);
+ }
+}
+
+
108 c_src/unixdom_drv.h
@@ -0,0 +1,108 @@
+/*
+** Copyright (c) 2004, Scott Lystig Fritchie. All rights reserved.
+** See the file "LICENSE" at the top of the source distribution for
+** full license terms.
+**
+** NOTICE: This file was generated by the tools of the Erlang Driver
+** toolkit. Do not edit this file by hand unless you know
+** what you're doing!
+**
+*/
+
+#ifndef __UNIXDOM_DRV_H
+#define __UNIXDOM_DRV_H
+
+#ifdef DRIVER_USING_PTHREADS
+#include <pthread.h>
+#endif /* DRIVER_USING_PTHREADS */
+
+/*
+** Driver<->emulator communication codes (xref with top of unixdom_drv.hrl)
+*/
+
+#define S1_DEBUG 0
+#define S1_NULL 1
+#define S1_OPEN 2
+#define S1_GETFD 3
+#define S1_SENDFD 4
+#define S1_RECEIVEFD 5
+#define S1_CLOSE 6
+#define S1_WRITE 7
+#define S1_READ 8
+
+/*
+** Constants
+*/
+
+
+#define MAX_BINVS 24 /* XXX dumb constant! */
+
+#define VALMAP_INUSE 0x01
+#define VALMAP_DELAYED_CLEANUP 0x02
+
+#define MAX_RETURN_TERMS 64
+
+/*
+** descriptor_t = general state for the port
+**
+*/
+
+struct descriptor {
+ ErlDrvPort port;
+ unsigned short nextxid; /* Call txn ID for very async calls */
+ int valmap_fd[32];
+ ErlDrvBinary *tofree[MAX_RETURN_TERMS];
+ int num_tofree;
+};
+
+/*
+** callstate_t: asychronous call state
+**
+** TODO: Fill in the struct members of "i" and "o" below.
+*/
+typedef struct callstate {
+ struct callstate *next;
+ int cmd; /* call/command # */
+ unsigned int *key; /* Not really used */
+ void (*invoke)(void *);
+ void (*free)(void *);
+ unsigned short xid; /* Only used by as_control()-
+ initiated funcs */
+ /*
+ ** Input & output args use a struct simply for convenient
+ ** naming & grouping. There isn't any attempt to get smart about
+ ** using a union or anything like that to avoid bloating the size
+ ** of these structs with the combined args of a zillion different
+ ** functions. XXX For future use: look into the use of unions
+ ** and/or different flavors of callstate_t to avoid structure bloat.
+ */
+ struct {
+ int __valmap_fd_index;
+ unsigned long __stash[3];
+ char * filename;
+ int flags;
+ int fd;
+ int unixdom_fd;
+ int fd_to_be_sent;
+ char * ptr;
+ size_t size;
+ } i;
+ struct {
+ int __expect;
+ int __expect_errval;
+ int ret_int;
+ int ret_int_t;
+ ssize_t ret_ssize_t;
+ } o;
+} callstate_t;
+
+/*
+** End of autogenerated code
+** script = ../../edtk/c_h_template.gsl
+** filename = unixdom.xml
+** gslgen version = 2.000 Beta 1
+** date = 2004/02/23
+** time = 2:24:55
+*/
+
+#endif /* __UNIXDOM_DRV_H */
0  ebin/.empty
No changes.
109 priv/unixdom.xml
@@ -0,0 +1,109 @@
+<erldriver name="unixdom_drv" abbrev="s1"
+ default_async_calls="0" default_debug_verbose="0">
+
+<summary>EDTK implementation of UNIX domain socket driver (incomplete!)</summary>
+
+<cpy>Copyright (c) 2004, Scott Lystig Fritchie. All rights reserved.</cpy>
+<cpy>See the file "LICENSE" at the top of the source distribution for</cpy>
+<cpy>full license terms.</cpy>
+
+<stash size="3"/>
+
+<verbatim place="top_cpp_stuff">
+#include &lt;stdio.h>
+#include &lt;time.h>
+#include &lt;fcntl.h>
+#include &lt;errno.h>
+#include &lt;string.h>
+#include &lt;sys/types.h>
+#include &lt;sys/stat.h>
+#include &lt;sys/socket.h>
+#include &lt;sys/un.h>
+#include &lt;netinet/in.h> /* htonl() et al. */
+
+#include &lt;my-unixdom.h>
+</verbatim>
+
+<include name="unixdom_drv.h" style="angle"/>
+
+<func name="null">
+ <return ctype="void"/>
+</func>
+
+<func name="open" cname="my_open">
+ <arg name="filename" ctype="char *" ser_type="binary" nulterm="1"/>
+ <arg name="flags" ctype="int"/>
+ <return ctype="int" name="ret_int" valmap_name="fd" valmap_type="start"
+ expect=">= 0" expect_errval="errno"/>
+</func>
+
+<!--
+ The getfd func is necessary because we want to use the valmap
+ feature to be able to clean up file descriptors if our owning Erlang
+ process crashes ... but valmaps also (intentionally) hide the value
+ of the values they store. We want to expose the file descriptor
+ value, so use my_getfd().
+-->
+
+<func name="getfd" cname="my_getfd">
+ <arg name="fd" ctype="int" valmap_name="fd"/>
+ <return ctype="int" name="ret_int"/>
+</func>
+
+<func name="sendfd" cname="my_sendfd">
+ <arg name="unixdom_fd" ctype="int"/>
+ <arg name="fd_to_be_sent" ctype="int"/>
+ <return ctype="int" name="ret_int_t" etype="integer"
+ expect="== 0" expect_errval="errno"/>
+</func>
+
+<func name="receivefd" cname="my_receivefd">
+ <arg name="unixdom_fd" ctype="int"/>
+ <return ctype="int" name="ret_int_t" etype="integer"
+ expect=">= 0" expect_errval="errno"/>
+</func>
+
+<func name="close">
+ <arg name="fd" ctype="int" valmap_name="fd"/>
+ <!-- QQQ valmap return processing is a bit weird. We specify
+ valmap_name on an input arg _and_ we specify it on the return
+ item, along with the 'valmap_type="stop"' attrib.
+ -->
+ <return ctype="int" name="ret_int" valmap_name="fd" valmap_type="stop"
+ expect="== 0" expect_errval="errno"/>
+</func>
+
+<func name="write">
+ <arg name="fd" ctype="int" valmap_name="fd"/>
+ <arg name="ptr" ctype="char *" ser_type="binary" binlen2stash="0"/>
+ <arg name="size" ctype="size_t" noerlcall="1" usestash="0"/>
+ <return ctype="ssize_t" name="ret_ssize_t" etype="integer"/>
+</func>
+
+<func name="read">
+ <arg name="fd" ctype="int" valmap_name="fd"/>
+ <arg name="ptr" ctype="char *" noerlcall="1"/>
+ <arg name="size" ctype="size_t" ser_type="integer"/>
+ <return ctype="ssize_t" name="ret_ssize_t" etype="integer"
+ xreturn="1_ptr_ssize_t"/>
+ <hack place="post-deserialize" type="verbatim">
+ edtk_debug("XXX c->i.size = %d\r\n", c->i.size);
+ if ((c->i.ptr = (char *) edtk_driver_alloc_wrapper(c->i.size)) == NULL) {
+ goto error;
+ }
+ </hack>
+</func>
+
+<valmap name="fd" ctype="int" maxsize="32" initial_val="-1"
+ cleanup_func="close"/>
+
+<xtra_return name="1_ptr_ssize_t">
+ <xtra_ok>
+ <xtra_val etype="binary" valtype="erl_drv_bin" val="c->i.ptr"
+ offset="0" length="c->o.ret_ssize_t"/>
+ </xtra_ok>
+</xtra_return>
+
+
+</erldriver>
+
14 src/Makefile
@@ -0,0 +1,14 @@
+include ../../../support/include.mk
+
+# Some modules are automatically generated, so we won't use the
+# include makefile's auto-detected list.
+MODULES := unixdom_drv
+OBJECTS := $(MODULES:%=../ebin/%.$(EMULATOR))
+
+all: $(OBJECTS)
+
+clean:
+ rm -f $(OBJECTS)
+ rm -f ../ebin/unixdom_drv.so
+ rm -f ../priv/unixdom_drv.so
+
342 src/unixdom_drv.erl
@@ -0,0 +1,342 @@
+%%%----------------------------------------------------------------------
+%%% File : unixdom_drv.erl
+%%% Summary : EDTK implementation of UNIX domain socket driver (incomplete!)
+%%%
+%%%
+%%% NOTICE: This file was generated by the tools of the Erlang Driver
+%%% toolkit. Do not edit this file by hand unless you know
+%%% what you're doing!
+%%%
+%%% Copyright (c) 2004, Scott Lystig Fritchie. All rights reserved.
+%%% See the file "LICENSE" at the top of the source distribution for
+%%% full license terms.
+%%%
+%%%----------------------------------------------------------------------
+
+-module(unixdom_drv).
+-include("unixdom_drv.hrl").
+
+%% Xref with erl_driver_tk.h's PIPE_DRIVER_TERM_* values
+-define(T_NIL, 0).
+-define(T_ATOM, 1).
+-define(T_PORT, 2).
+-define(T_INT, 3).
+-define(T_TUPLE, 4).
+-define(T_BINARY, 5).
+-define(T_STRING, 6).
+-define(T_LIST, 7).
+
+%% External exports
+-export([start/0, start_pipe/0]).
+-export([shutdown/1]).
+-export([debug/2]).
+-export([
+ null/1,
+ open/3,
+ getfd/2,
+ sendfd/3,
+ receivefd/2,
+ close/2,
+ write/3,
+ read/3
+ ]).
+
+start() ->
+ {ok, Path} = load_path(?DRV_NAME ++ ".so"),
+ erl_ddll:start(),
+ ok = erl_ddll:load_driver(Path, ?DRV_NAME),
+ case open_port({spawn, ?DRV_NAME}, []) of
+ P when port(P) ->
+ {ok, P};
+ Err ->
+ Err
+ end.
+
+start_pipe() ->
+ {ok, PipeMain} = load_path("pipe-main"),
+ {ok, ShLib} = load_path("./unixdom_drv.so"),
+ Cmd = PipeMain ++ "/pipe-main " ++ ShLib ++ "/unixdom_drv.so",
+ case open_port({spawn, Cmd}, [exit_status, binary, use_stdio, {packet, 4}]) of
+ P when port(P) ->
+ {ok, P};
+ Err ->
+ Err
+ end.
+
+shutdown(Port) when port(Port) ->
+ catch erlang:port_close(Port),
+ %% I was under the impression you'd always get a message sent to
+ %% you in this case, so this receive is to keep your mailbox from
+ %% getting cluttered. Hrm, well, sometimes the message does
+ %% not arrive at all!
+ receive
+ {'EXIT', Port, normal} -> {ok, normal};
+ {'EXIT', Port, Err} -> {error, Err}
+ after 0 -> {ok, normall} % XXX is 0 too small?
+
+ end.
+
+debug(Port, Flags) when port(Port), integer(Flags) ->
+ case catch erlang:port_command(Port, <<?S1_DEBUG, Flags:32>>) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX too drastic?
+ end.
+
+null(Port
+ ) when port(Port) -> % TODO: Add additional constraints here
+ IOList_____ = <<?S1_NULL>>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+open(Port,
+ Filename,
+ Flags
+ ) when port(Port) -> % TODO: Add additional constraints here
+ {FilenameBinOrList, FilenameLen} = serialize_contiguously(Filename, 1),
+ IOList_____ = [ <<?S1_OPEN,
+ FilenameLen:32/integer>>, % I/O list length
+ FilenameBinOrList,
+ <<
+ Flags:32/integer
+ >> ],
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+getfd(Port,
+ Fd
+ ) when port(Port) -> % TODO: Add additional constraints here
+ {valmap_fd, FdIndex} = Fd,
+ IOList_____ = <<?S1_GETFD,
+ FdIndex:32/integer
+ >>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+sendfd(Port,
+ Unixdom_Fd,
+ Fd_To_Be_Sent
+ ) when port(Port) -> % TODO: Add additional constraints here
+ IOList_____ = <<?S1_SENDFD,
+ Unixdom_Fd:32/integer,
+ Fd_To_Be_Sent:32/integer
+ >>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+receivefd(Port,
+ Unixdom_Fd
+ ) when port(Port) -> % TODO: Add additional constraints here
+ IOList_____ = <<?S1_RECEIVEFD,
+ Unixdom_Fd:32/integer
+ >>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+close(Port,
+ Fd
+ ) when port(Port) -> % TODO: Add additional constraints here
+ {valmap_fd, FdIndex} = Fd,
+ IOList_____ = <<?S1_CLOSE,
+ FdIndex:32/integer
+ >>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+write(Port,
+ Fd,
+ Ptr
+ ) when port(Port) -> % TODO: Add additional constraints here
+ {valmap_fd, FdIndex} = Fd,
+ {PtrBinOrList, PtrLen} = serialize_contiguously(Ptr, 0),
+ IOList_____ = [ <<?S1_WRITE,
+ FdIndex:32/integer,
+ PtrLen:32/integer>>, % I/O list length
+ PtrBinOrList,
+ <<
+ >> ],
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+read(Port,
+ Fd,
+ Size
+ ) when port(Port) -> % TODO: Add additional constraints here
+ {valmap_fd, FdIndex} = Fd,
+ IOList_____ = <<?S1_READ,
+ FdIndex:32/integer,
+ Size:32/integer
+ >>,
+ case catch erlang:port_command(Port, IOList_____) of
+ true -> get_port_reply(Port);
+ Err -> throw(Err) % XXX Is this too drastic?
+ end.
+
+
+%%%
+%%% Internal functions.
+%%%
+
+load_path(File) ->
+ case lists:filter(fun(D) ->
+ case file:read_file_info(D ++ "/" ++ File) of
+ {ok, _} -> true;
+ _ -> false
+ end
+ end, code:get_path()) of
+ [Dir|_] ->
+ {ok, Dir};
+ [] ->
+ io:format("Error: ~s not found in code path\n", [File]),
+ {error, enoent}
+ end.
+
+%%%
+%%% Note that an 'xtra_return' that only returns one item in its
+%%% tuple will return {Port, ok, {Thingie}}, so we'll return
+%%% {ok, {Thingie}}, which is *sooooooo* maddening because I keep
+%%% forgetting the extra tuple wrapper. So, if there's only one
+%%% thingie in the return tuple, strip it off: {ok, Thingie}
+%%%
+
+get_port_reply(Port) when port(Port) ->
+ receive
+ {Port, ok} = T -> proc_reply(T);
+ {Port, ok, {M}} = T -> proc_reply(T);
+ {Port, ok, M} = T -> proc_reply(T);
+ {Port, error, {Reason}} = T -> proc_reply(T);
+ {Port, error, Reason} = T -> proc_reply(T);
+ %% Pipe driver messages
+ {Port, {data, Bytes}} -> proc_reply(pipedrv_deser(Port, Bytes));
+ {'EXIT', Port, Reason} -> throw({port_error, Reason}); % XXX too drastic?
+ {Port, Reason} -> throw({port_error, Reason}) % XXX too drastic?
+ end.
+
+%% This function exists to provide consistency of replies
+%% given by linked-in and pipe drivers. The "receive" statement
+%% in get_port_reply/1 is specific because we want it to be
+%% very selective about what it will grab out of the mailbox.
+proc_reply({Port, ok}) when port(Port) ->
+ ok;
+proc_reply({Port, ok, {M}}) when port(Port) ->
+ {ok, M};
+proc_reply({Port, ok, M}) when port(Port) ->
+ {ok, M};
+proc_reply({Port, error, {Reason}}) when port(Port) ->
+ {error, Reason};
+proc_reply({Port, error, Reason}) when port(Port) ->
+ {error, Reason}.
+
+
+%%% io_list_len() is an extremely useful function. BEAM has got this
+%%% implemented quite efficiently in C. It would be *fabulous* to be able
+%%% to use it from Erlang via a BIF.
+
+io_list_len(B) when binary(B) -> {B, size(B)};
+io_list_len(L) when list(L) -> io_list_len(L, 0).
+io_list_len([H|T], N) ->
+ if
+ H >= 0, H =< 255 -> io_list_len(T, N+1);
+ list(H) -> io_list_len(T, io_list_len(H,N));
+ binary(H) -> io_list_len(T, size(H) + N);
+ true -> throw({error, partial_len, N})
+ end;
+io_list_len(H, N) when binary(H) ->
+ size(H) + N;
+io_list_len([], N) ->
+ N.
+
+%%% We need to make the binary thing we're passing in contiguous
+%%% because the C function we're calling is expecting a single
+%%% contiguous buffer. If IOList is ["Hello, ", <<"World">>, "!"],
+%%% that binary in the middle element will end up with the argument
+%%% spanning three parts of an ErlIOVec. If that happens, then we'd
+%%% have to have the driver do the dirty work of putting the argument
+%%% into a single contiguous buffer.
+%%%
+%%% Frankly, we're lazy, and this code is short and won't be much
+%%% slower than doing it in C.
+
+%%% 2nd arg: if 1, NUL-terminate the IOList
+
+serialize_contiguously(B, 0) when binary(B) ->
+ {B, size(B)};
+serialize_contiguously([B], 0) when binary(B) ->
+ {B, size(B)};
+serialize_contiguously(IOList, 1) ->
+ serialize_contiguously([IOList, 0], 0);
+serialize_contiguously(IOList, 0) ->
+ B = list_to_binary(IOList),
+ {B, size(B)}.
+
+
+%% pipedrv_deser/2 -- Deserialize the term that the pipe driver is
+%% is returning to Erlang. The pipe driver doesn't know it's a pipe
+%% driver, it thinks it's a linked-in driver, so it tries to return
+%% an arbitrary Erlang term to us. The pipe-main program is sneaky:
+%% it has a driver_output_term() function that serializes the term
+%% that the driver built. With the help of a list-as-stack, we
+%% deserialize that term.
+
+pipedrv_deser(Port, B) ->
+ pipedrv_deser(Port, B, []).
+
+pipedrv_deser(Port, <<>>, []) ->
+ throw(icky_i_think);
+pipedrv_deser(Port, <<>>, [T]) ->
+ T;
+pipedrv_deser(Port, <<?T_NIL:8, Rest/binary>>, Stack) ->
+ pipedrv_deser(Port, Rest, [foo___foo_nil___|Stack]);
+pipedrv_deser(Port, <<?T_ATOM:8, Len:8, Rest/binary>>, Stack) ->
+ <<A:Len/binary, Rest2/binary>> = Rest,
+ pipedrv_deser(Port, Rest2, [list_to_atom(binary_to_list(A))|Stack]);
+pipedrv_deser(Port, <<?T_PORT:8, P:32/unsigned, Rest/binary>>, Stack) ->
+ %% The pipe driver tried sending us a port, but it cannot know what
+ %% port ID was assigned to this port, so we'll assume it is Port.
+ pipedrv_deser(Port, Rest, [Port|Stack]);
+pipedrv_deser(Port, <<?T_INT:8, I:32/signed, Rest/binary>>, Stack) ->
+ pipedrv_deser(Port, Rest, [I|Stack]);
+pipedrv_deser(Port, <<?T_TUPLE:8, N:8, Rest/binary>>, Stack) ->
+ {L, NewStack} = popN(N, Stack),
+ pipedrv_deser(Port, Rest, [list_to_tuple(L)|NewStack]);
+pipedrv_deser(Port, <<?T_LIST:8, N:32, Rest/binary>>, Stack) ->
+ {L, NewStack} = popN(N, Stack),
+ pipedrv_deser(Port, Rest, [L|NewStack]);
+pipedrv_deser(Port, <<?T_BINARY:8, Len:32/signed, Rest/binary>>, Stack) ->
+ <<Bin:Len/binary, Rest2/binary>> = Rest,
+ pipedrv_deser(Port, Rest2, [Bin|Stack]);
+pipedrv_deser(Port, <<?T_STRING:8, Len:32/signed, Rest/binary>>, Stack) ->
+ <<Bin:Len/binary, Rest2/binary>> = Rest,
+ pipedrv_deser(Port, Rest2, [binary_to_list(Bin)|Stack]);
+pipedrv_deser(Port, X, Y) ->
+ throw({bah, X, Y}).
+
+popN(N, Stack) ->
+ popN(N, Stack, []).
+popN(0, Stack, Acc) ->
+ {Acc, Stack};
+popN(N, [foo___foo_nil___|T], Acc) ->
+ %% This is the nonsense we put on the stack to represent NIL. Ignore it.
+ popN(N - 1, T, Acc);
+popN(N, [H|T], Acc) ->
+ popN(N - 1, T, [H|Acc]).
+
+
+%%%
+%%% Begin code included via <custom_erl> tags
+%%%
+
+
53 src/unixdom_drv.hrl
@@ -0,0 +1,53 @@
+%%%----------------------------------------------------------------------
+%%% File : unixdom_drv.hrl
+%%% Summary : EDTK implementation of UNIX domain socket driver (incomplete!)
+%%%
+%%%
+%%% NOTICE: This file was generated by the tools of the Erlang Driver
+%%% toolkit. Do not edit this file by hand unless you know
+%%% what you're doing!
+%%%
+%%% Copyright (c) 2004, Scott Lystig Fritchie. All rights reserved.
+%%% See the file "LICENSE" at the top of the source distribution for
+%%% full license terms.
+%%%
+%%%----------------------------------------------------------------------
+
+-define(DRV_NAME, "unixdom_drv").
+
+-define(MAX_UINT32, 16#FFFFFFFF).
+
+-define(LEN_NUL_TERM, ?MAX_UINT32).
+
+%%%
+%%% Driver<->emulator communication codes (xref with top of unixdom_drv.h)
+%%%
+
+-define(S1_DEBUG, 0).
+-define(S1_NULL, 1).
+-define(S1_OPEN, 2).
+-define(S1_GETFD, 3).
+-define(S1_SENDFD, 4).
+-define(S1_RECEIVEFD, 5).
+-define(S1_CLOSE, 6).
+-define(S1_WRITE, 7).
+-define(S1_READ, 8).
+
+%%%
+%%% Constants
+%%%
+
+
+%%%
+%%% Verbatim stuff
+%%%
+
+
+%%%
+%%% End of autogenerated code
+%%% script = ../../edtk/hrl_template.gsl
+%%% filename = unixdom.xml
+%%% gslgen version = 2.000 Beta 1
+%%% date = 2004/02/23
+%%% time = 2:24:54
+%%%
17 test/Makefile
@@ -0,0 +1,17 @@
+include ../../../support/include.mk
+
+# Some modules are automatically generated, so we won't use the
+# include makefile's auto-detected list.
+MODULES := unixdom_test
+OBJECTS := $(MODULES:%=../ebin/%.$(EMULATOR))
+
+ERLC_FLAGS += -I../src
+
+all: $(OBJECTS)
+
+clean:
+ rm -f $(OBJECTS)
+
+test: all
+ erl +A5 -pz ../ebin -pz ../priv -s unixdom_test regression -s erlang halt
+
72 test/unixdom_test.erl
@@ -0,0 +1,72 @@
+
+-module(unixdom_test).
+-define(DRV, unixdom_drv).
+
+%%%
+%%% Change this macro to suit your local platform
+%%%
+-define(MY_OS, "FreeBSD").
+%-define(MY_OS, "Linux").
+%-define(MY_OS, "Solaris").
+
+-export([regression/0, tcp_listen/2, receive_fd/1]).
+-export([repeat/4]). %XXX
+
+repeat(0, _, _, _) ->
+ ok;
+repeat(N, M, F, A) ->
+ apply(M, F, A),
+ repeat(N - 1, M, F, A).
+
+regression() ->
+ SockPath = "/tmp/sock",
+ io:format("\n\nThere is no regression test yet.\n"),
+ io:format("On Erlang node A, run file:delete(\"~s\").\n", [SockPath]),
+ io:format("Then run ~s:tcp_listen(5555, \"~s\").\n", [?MODULE, SockPath]),
+ io:format("\n"),
+ io:format("On Erlang node B (on the same machine as A!), run\n"),
+ io:format("~s:receive_fd(\"~s\").\n", [?MODULE, SockPath]),
+ io:format("\n"),
+ io:format("Then run 'telnet node-A-hostname 5555' and\n"),
+ io:format("then type some stuff!\n\n"),
+
+ io:format("All regression tests PASSED.\n"),
+ ok.
+
+tcp_listen(TcpPort, SockPath) ->
+ {ok, Port} = unixdom_drv:start(),
+ {ok, MasterUSock} = unixdom_drv:open(Port, "/tmp/sock", 1),
+ {ok, Val} = unixdom_drv:getfd(Port, MasterUSock),
+ {ok, MasterSock} = gen_tcp:fdopen(Val, []),
+ prim_inet:setopts(MasterSock, [{packet, raw}]),
+ prim_inet:listen(MasterSock),
+ {ok, Usock} = gen_tcp:accept(MasterSock),
+ {ok, Usockfd} = prim_inet:getfd(Usock),
+ %
+ {ok, Tcpmsock} = gen_tcp:listen(TcpPort, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
+ {ok, Tcpsock} = gen_tcp:accept(Tcpmsock),
+ {ok, Tcpsockfd} = prim_inet:getfd(Tcpsock),
+ %
+ unixdom_drv:sendfd(Port, Usockfd, Tcpsockfd),
+ ?DRV:shutdown(Port).
+
+receive_fd(SockPath) ->
+ {ok, Port} = unixdom_drv:start(),
+ {ok, ClntSock} = unixdom_drv:open(Port, "/tmp/sock", 0),
+ {ok, ClntSockFd} = unixdom_drv:getfd(Port, ClntSock),
+ {ok, Tcpsockfd} = unixdom_drv:receivefd(Port, ClntSockFd),
+ io:format("TCP socket fd ~w received, looping now\n", [Tcpsockfd]),
+ ?DRV:shutdown(Port),
+ {ok, Tcpsock} = gen_tcp:fdopen(Tcpsockfd, []),
+ gen_tcp:send(Tcpsock, "Hello, there! Please type some stuff:\r\n"),
+ readloop(Tcpsock).
+
+readloop(Tcpsock) ->
+ case gen_tcp:recv(Tcpsock, 1) of
+ {ok, B} ->
+ io:format("~s", [B]),
+ readloop(Tcpsock);
+ {error, _} ->
+ ok
+ end.
+
Please sign in to comment.
Something went wrong with that request. Please try again.