summaryrefslogtreecommitdiff
path: root/erts/emulator/nifs/unix/unix_socket_syncio.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/nifs/unix/unix_socket_syncio.c')
-rw-r--r--erts/emulator/nifs/unix/unix_socket_syncio.c7390
1 files changed, 7390 insertions, 0 deletions
diff --git a/erts/emulator/nifs/unix/unix_socket_syncio.c b/erts/emulator/nifs/unix/unix_socket_syncio.c
new file mode 100644
index 0000000000..644400aeb5
--- /dev/null
+++ b/erts/emulator/nifs/unix/unix_socket_syncio.c
@@ -0,0 +1,7390 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2022-2023. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ *
+ * ----------------------------------------------------------------------
+ * Purpose : UNIX version of synchronous I/O backend.
+ * ----------------------------------------------------------------------
+ *
+ * essio = ESock Synchronous I/O
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef ESOCK_ENABLE
+
+#ifdef HAVE_SENDFILE
+#if defined(__linux__) || (defined(__sun) && defined(__SVR4))
+ #include <sys/sendfile.h>
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+ /* Need to define __BSD_VISIBLE in order to expose prototype
+ * of sendfile in sys/socket.h
+ */
+ #define __BSD_VISIBLE 1
+#endif
+#endif
+
+#ifndef WANT_NONBLOCKING
+#define WANT_NONBLOCKING
+#endif
+#include "sys.h"
+
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#include <net/if.h>
+
+#include "prim_socket_int.h"
+#include "socket_util.h"
+#include "socket_io.h"
+#include "socket_syncio.h"
+#include "socket_tarray.h"
+#include "prim_file_nif_dyncall.h"
+
+
+/* ======================================================================== *
+ * Socket wrappers *
+ * ======================================================================== *
+ */
+
+#ifdef HAS_ACCEPT4
+// We have to figure out what the flags are...
+#define sock_accept(s, addr, len) \
+ accept4((s), (addr), (len), (SOCK_CLOEXEC))
+#else
+#define sock_accept(s, addr, len) accept((s), (addr), (len))
+#endif
+#define sock_bind(s, addr, len) bind((s), (addr), (len))
+#define sock_close(s) close((s))
+// #define sock_close_event(e) /* do nothing */
+#define sock_connect(s, addr, len) connect((s), (addr), (len))
+#define sock_errno() errno
+// #define sock_listen(s, b) listen((s), (b))
+// #define sock_name(s, addr, len) getsockname((s), (addr), (len))
+#define sock_open(domain, type, proto) socket((domain), (type), (proto))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
+#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
+#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
+ recvfrom((s),(buf),(blen),(flag),(addr),(alen))
+#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag))
+#define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag))
+#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag))
+#define sock_sendto(s,buf,blen,flag,addr,alen) \
+ sendto((s),(buf),(blen),(flag),(addr),(alen))
+#define sock_shutdown(s, how) shutdown((s), (how))
+
+
+/* =================================================================== *
+ * *
+ * Various esaio macros *
+ * *
+ * =================================================================== */
+
+/* Global socket debug */
+#define SGDBG( proto ) ESOCK_DBG_PRINTF( ctrl.dbg , proto )
+
+
+/* =================================================================== *
+ * *
+ * Local types *
+ * *
+ * =================================================================== */
+
+typedef struct {
+ /* Misc stuff */
+ BOOLEAN_T dbg;
+ BOOLEAN_T sockDbg;
+} ESSIOControl;
+
+
+
+/* ======================================================================== *
+ * Function Forwards *
+ * ======================================================================== *
+ */
+static BOOLEAN_T open_todup(ErlNifEnv* env,
+ ERL_NIF_TERM eopts);
+static BOOLEAN_T open_which_domain(SOCKET sock, int* domain);
+static BOOLEAN_T open_which_type(SOCKET sock, int* type);
+static BOOLEAN_T open_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* domain);
+static BOOLEAN_T open_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* type);
+static BOOLEAN_T open_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* protocol);
+
+#ifdef HAVE_SETNS
+static BOOLEAN_T open_get_netns(ErlNifEnv* env,
+ ERL_NIF_TERM opts,
+ char** netns);
+static BOOLEAN_T change_network_namespace(BOOLEAN_T dbg,
+ char* netns, int* cns, int* err);
+static BOOLEAN_T restore_network_namespace(BOOLEAN_T dbg,
+ int ns, SOCKET sock, int* err);
+#endif
+
+static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err);
+
+static ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+static ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+static ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP);
+
+static ERL_NIF_TERM essio_accept_listening_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid caller,
+ int save_errno);
+static ERL_NIF_TERM essio_accept_listening_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ErlNifPid caller);
+static ERL_NIF_TERM essio_accept_accepting_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM ref);
+static
+ERL_NIF_TERM essio_accept_accepting_current_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock);
+static
+ERL_NIF_TERM essio_accept_accepting_current_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef,
+ int save_errno);
+static ERL_NIF_TERM essio_accept_accepting_other(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ErlNifPid caller);
+static ERL_NIF_TERM essio_accept_busy_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid* pidP);
+static BOOLEAN_T essio_accept_accepted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ErlNifPid pid,
+ ERL_NIF_TERM* result);
+
+static BOOLEAN_T send_check_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ERL_NIF_TERM* checkResult);
+static ERL_NIF_TERM send_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t send_result,
+ ssize_t dataSize,
+ BOOLEAN_T dataInTail,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef);
+static ERL_NIF_TERM send_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t written,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+static void send_error_waiting_writers(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason);
+static ERL_NIF_TERM send_check_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t written,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef);
+
+static BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* cmsgHdrBufP,
+ size_t cmsgHdrBufLen,
+ size_t* cmsgHdrBufUsed);
+static BOOLEAN_T decode_cmsghdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* bufP,
+ size_t rem,
+ size_t* used);
+static BOOLEAN_T decode_cmsghdr_value(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eValue,
+ char* dataP,
+ size_t dataLen,
+ size_t* dataUsedP);
+static BOOLEAN_T decode_cmsghdr_data(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eData,
+ char* dataP,
+ size_t dataLen,
+ size_t* dataUsedP);
+
+static void encode_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM* eMsg);
+static void encode_cmsgs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifBinary* cmsgBinP,
+ struct msghdr* msgHdrP,
+ ERL_NIF_TERM* eCMsg);
+
+#if defined(HAVE_SENDFILE)
+static int essio_sendfile(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ off_t offset,
+ size_t* countP,
+ int* errP);
+static ERL_NIF_TERM essio_sendfile_errno(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ int err);
+static ERL_NIF_TERM essio_sendfile_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason);
+static ERL_NIF_TERM essio_sendfile_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ size_t count);
+static ERL_NIF_TERM essio_sendfile_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ size_t count);
+#endif
+
+static ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ int saveErrno,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ int saveErrno,
+ ErlNifBinary* bufP,
+ ESockAddress* fromAddrP,
+ SOCKLEN_T fromAddrLen,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static BOOLEAN_T recv_check_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ERL_NIF_TERM* checkResult);
+static ERL_NIF_TERM recv_check_full(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ErlNifBinary* buf1P,
+ ErlNifBinary* buf2P,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static void recv_init_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM recvRef);
+static void recv_update_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static void recv_error_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason);
+
+static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ int saveErrno,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef);
+static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM sockRef);
+
+
+static ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env,
+ ESockDescriptor* descP);
+#if defined(SIOCGIFNAME)
+static ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eidx);
+#endif
+
+/* esock_ioctl_gifindex */
+#if defined(SIOCGIFINDEX)
+#define IOCTL_GIFINDEX_FUNC_DEF IOCTL_GET_FUNC_DEF(gifindex)
+#else
+#define IOCTL_GIFINDEX_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifflags */
+#if defined(SIOCGIFFLAGS)
+#define IOCTL_GIFFLAGS_FUNC_DEF IOCTL_GET_FUNC_DEF(gifflags)
+#else
+#define IOCTL_GIFFLAGS_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifaddr */
+#if defined(SIOCGIFADDR)
+#define IOCTL_GIFADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifaddr)
+#else
+#define IOCTL_GIFADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifdstaddr */
+#if defined(SIOCGIFDSTADDR)
+#define IOCTL_GIFDSTADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifdstaddr)
+#else
+#define IOCTL_GIFDSTADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifbrdaddr */
+#if defined(SIOCGIFBRDADDR)
+#define IOCTL_GIFBRDADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifbrdaddr)
+#else
+#define IOCTL_GIFBRDADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifnetmask */
+#if defined(SIOCGIFNETMASK)
+#define IOCTL_GIFNETMASK_FUNC_DEF IOCTL_GET_FUNC_DEF(gifnetmask)
+#else
+#define IOCTL_GIFNETMASK_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifmtu */
+#if defined(SIOCGIFMTU)
+#define IOCTL_GIFMTU_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmtu)
+#else
+#define IOCTL_GIFMTU_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifhwaddr */
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+#define IOCTL_GIFHWADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifhwaddr)
+#else
+#define IOCTL_GIFHWADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_gifmap */
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+#define IOCTL_GIFMAP_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmap)
+#else
+#define IOCTL_GIFMAP_FUNC_DEF
+#endif
+
+/* esock_ioctl_giftxqlen */
+#if defined(SIOCGIFTXQLEN)
+#define IOCTL_GIFTXQLEN_FUNC_DEF IOCTL_GET_FUNC_DEF(giftxqlen)
+#else
+#define IOCTL_GIFTXQLEN_FUNC_DEF
+#endif
+
+#define IOCTL_GET_FUNCS_DEF \
+ IOCTL_GIFINDEX_FUNC_DEF; \
+ IOCTL_GIFFLAGS_FUNC_DEF; \
+ IOCTL_GIFADDR_FUNC_DEF; \
+ IOCTL_GIFDSTADDR_FUNC_DEF; \
+ IOCTL_GIFBRDADDR_FUNC_DEF; \
+ IOCTL_GIFNETMASK_FUNC_DEF; \
+ IOCTL_GIFMTU_FUNC_DEF; \
+ IOCTL_GIFHWADDR_FUNC_DEF; \
+ IOCTL_GIFMAP_FUNC_DEF; \
+ IOCTL_GIFTXQLEN_FUNC_DEF;
+#define IOCTL_GET_FUNC_DEF(F) \
+ static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename)
+IOCTL_GET_FUNCS_DEF
+#undef IOCTL_GET_FUNC_DEF
+
+/* esock_ioctl_sifflags */
+#if defined(SIOCSIFFLAGS)
+#define IOCTL_SIFFLAGS_FUNC_DEF IOCTL_SET_FUNC_DEF(sifflags)
+#else
+#define IOCTL_SIFFLAGS_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifaddr */
+#if defined(SIOCSIFADDR)
+#define IOCTL_SIFADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifaddr)
+#else
+#define IOCTL_SIFADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifdstaddr */
+#if defined(SIOCSIFDSTADDR)
+#define IOCTL_SIFDSTADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifdstaddr)
+#else
+#define IOCTL_SIFDSTADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifbrdaddr */
+#if defined(SIOCSIFBRDADDR)
+#define IOCTL_SIFBRDADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifbrdaddr)
+#else
+#define IOCTL_SIFBRDADDR_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifnetmask */
+#if defined(SIOCSIFNETMASK)
+#define IOCTL_SIFNETMASK_FUNC_DEF IOCTL_SET_FUNC_DEF(sifnetmask)
+#else
+#define IOCTL_SIFNETMASK_FUNC_DEF
+#endif
+
+/* esock_ioctl_sifmtu */
+#if defined(SIOCSIFMTU)
+#define IOCTL_SIFMTU_FUNC_DEF IOCTL_SET_FUNC_DEF(sifmtu)
+#else
+#define IOCTL_SIFMTU_FUNC_DEF
+#endif
+
+/* esock_ioctl_siftxqlen */
+#if defined(SIOCSIFTXQLEN)
+#define IOCTL_SIFTXQLEN_FUNC_DEF IOCTL_SET_FUNC_DEF(siftxqlen)
+#else
+#define IOCTL_SIFTXQLEN_FUNC_DEF
+#endif
+
+#define IOCTL_SET_FUNCS_DEF \
+ IOCTL_SIFFLAGS_FUNC_DEF; \
+ IOCTL_SIFADDR_FUNC_DEF; \
+ IOCTL_SIFDSTADDR_FUNC_DEF; \
+ IOCTL_SIFBRDADDR_FUNC_DEF; \
+ IOCTL_SIFNETMASK_FUNC_DEF; \
+ IOCTL_SIFMTU_FUNC_DEF; \
+ IOCTL_SIFTXQLEN_FUNC_DEF;
+#define IOCTL_SET_FUNC_DEF(F) \
+ static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename, \
+ ERL_NIF_TERM evalue)
+IOCTL_SET_FUNCS_DEF
+#undef IOCTL_SET_FUNC_DEF
+
+
+static ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifconf* ifcP);
+static ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifreq* ifrP);
+static ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
+ char* name);
+static ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env,
+ struct sockaddr* sa);
+static ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
+ ERL_NIF_TERM name,
+ ERL_NIF_TERM key2,
+ ERL_NIF_TERM val2);
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+static ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifmap* mapP);
+#endif
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+static ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP);
+#endif
+static ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP);
+static ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ short flags);
+#if defined(SIOCSIFFLAGS)
+static BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eflags,
+ short* flags);
+#endif
+static BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eaddr,
+ ESockAddress* addr);
+#if defined(SIOCSIFMTU)
+static BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM emtu,
+ int* mtu);
+#endif
+#if defined(SIOCSIFTXQLEN)
+static BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM etxqlen,
+ int* txqlen);
+#endif
+#if defined(SIOCSIFTXQLEN)
+static BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eivalue,
+ int* ivalue);
+#endif
+static ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int ivalue);
+
+
+/*
+static void essio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP);
+*/
+static void essio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void essio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+static void essio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP);
+
+static BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP);
+
+
+/* =================================================================== *
+ * *
+ * Local (global) variables *
+ * *
+ * =================================================================== */
+
+static ESSIOControl ctrl = {0};
+
+
+
+/* ======================================================================== *
+ * ESSIO Functions *
+ * ======================================================================== *
+ */
+
+/*
+ * For "standard" (unix) synchronous I/O, in our case
+ * this is just a dummy function.
+ */
+extern
+int essio_init(unsigned int numThreads,
+ const ESockData* dataP)
+{
+ VOID(numThreads);
+
+ ctrl.dbg = dataP->dbg;
+ ctrl.sockDbg = dataP->sockDbg;
+
+ return ESOCK_IO_OK;
+}
+
+
+/*
+ * For "standard" (unix) synchronous I/O, this is just a dummy function.
+ * Also, will we ever call this?
+ */
+extern
+void essio_finish(void)
+{
+ return;
+}
+
+
+
+/* *******************************************************************
+ * essio_info - Return info "about" this I/O backend.
+ */
+
+extern
+ERL_NIF_TERM essio_info(ErlNifEnv* env)
+{
+ ERL_NIF_TERM info;
+ ERL_NIF_TERM keys[] = {esock_atom_name};
+ ERL_NIF_TERM vals[] = {MKA(env, "unix_essio")};
+ unsigned int numKeys = NUM(keys);
+ unsigned int numVals = NUM(vals);
+
+ ESOCK_ASSERT( numKeys == numVals );
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) );
+
+ return info;
+}
+
+
+
+/* ========================================================================
+ * essio_open - create an endpoint (from an existing fd) for communication
+ *
+ * Assumes the input has been validated.
+ *
+ * Normally we want debugging on (individual) sockets to be controlled
+ * by the sockets own debug flag. But since we don't even have a socket
+ * yet, we must use the global debug flag.
+ */
+extern
+ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env,
+ int fd,
+ ERL_NIF_TERM eopts,
+ const ESockData* dataP)
+{
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg);
+ BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg);
+ ESockDescriptor* descP;
+ ERL_NIF_TERM sockRef;
+ int domain, type, protocol;
+ int save_errno = 0;
+ BOOLEAN_T closeOnClose;
+ SOCKET sock;
+ ErlNifPid self;
+
+ /* Keep track of the creator
+ * This should not be a problem, but just in case
+ * the *open* function is used with the wrong kind
+ * of environment...
+ */
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "essio_open2 -> entry with"
+ "\r\n fd: %d"
+ "\r\n eopts: %T"
+ "\r\n", fd, eopts) );
+
+ /*
+ * Before we do anything else, we try to retrieve domain, type and protocol
+ * This information is either present in the eopts map or if not we need
+ * to "get" it from the system (getsockopt).
+ * Note that its not possible to get all of these on all platforms,
+ * and in those cases the user *must* provide us with them (eopts).
+ *
+ * We try the system first (since its more reliable) and if that fails
+ * we check the eopts map. If neither one works, we *give up*!
+ */
+
+ if (! open_which_domain(fd, &domain)) {
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> failed get domain from system\r\n") );
+
+ if (! open_get_domain(env, eopts, &domain)) {
+ return esock_make_invalid(env, esock_atom_domain);
+ }
+ }
+
+ if (! open_which_type(fd, &type)) {
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> failed get type from system\r\n") );
+
+ if (! open_get_type(env, eopts, &type))
+ return esock_make_invalid(env, esock_atom_type);
+ }
+
+ if (! esock_open_which_protocol(fd, &protocol)) {
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> failed get protocol from system\r\n") );
+
+ if (! open_get_protocol(env, eopts, &protocol)) {
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> "
+ "failed get protocol => try protocol 0\r\n") );
+ protocol = 0;
+ }
+ }
+
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> "
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n", domain, type, protocol) );
+
+
+ if (open_todup(env, eopts)) {
+ /* We shall dup the socket */
+ if (ESOCK_IS_ERROR(sock = dup(fd))) {
+ save_errno = sock_errno();
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO",
+ "essio_open2 -> dup failed: %d\r\n",
+ save_errno) );
+
+ return esock_make_error_errno(env, save_errno);
+ }
+ closeOnClose = TRUE;
+ } else {
+ sock = fd;
+ closeOnClose = FALSE;
+ }
+
+
+ SET_NONBLOCKING(sock);
+
+ /* Create and initiate the socket "descriptor" */
+ descP = esock_alloc_descriptor(sock);
+ descP->ctrlPid = self;
+ descP->domain = domain;
+ descP->type = type;
+ descP->protocol = protocol;
+ descP->closeOnClose = closeOnClose;
+ descP->origFD = fd;
+
+ /* Check if we are already connected, if so change state */
+ {
+ ESockAddress remote;
+ SOCKLEN_T addrLen = sizeof(remote);
+ sys_memzero((char *) &remote, addrLen);
+ if (sock_peer(descP->sock,
+ (struct sockaddr*) &remote,
+ &addrLen) == 0) {
+ SSDBG2( dbg, ("UNIX-ESSIO", "essio_open2 -> connected\r\n") );
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+ } else {
+ SSDBG2( dbg, ("UNIX-ESSIO", "essio_open2 -> not connected\r\n") );
+ }
+ }
+
+ /* And create the 'socket' resource */
+ sockRef = enif_make_resource(env, descP);
+ enif_release_resource(descP);
+
+ ESOCK_ASSERT( MONP("essio_open2 -> ctrl",
+ env, descP,
+ &descP->ctrlPid,
+ &descP->ctrlMon) == 0 );
+
+ descP->dbg = dbg;
+ descP->useReg = useReg;
+ esock_inc_socket(domain, type, protocol);
+
+ /* And finally (maybe) update the registry.
+ * Shall we keep track of the fact that this socket is created elsewhere?
+ */
+ if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "essio_open2 -> done: %T\r\n", sockRef) );
+
+ return esock_make_ok2(env, sockRef);
+}
+
+
+static
+BOOLEAN_T open_which_domain(SOCKET sock, int* domain)
+{
+#if defined(SO_DOMAIN)
+ if (esock_getopt_int(sock, SOL_SOCKET, SO_DOMAIN, domain))
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+/* The eopts contains an integer 'domain' key.
+ */
+static
+BOOLEAN_T open_get_domain(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* domain)
+{
+ ERL_NIF_TERM edomain;
+
+ if (!GET_MAP_VAL(env, eopts,
+ esock_atom_domain, &edomain))
+ return FALSE;
+
+ if (esock_decode_domain(env, edomain, domain) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static
+BOOLEAN_T open_which_type(SOCKET sock, int* type)
+{
+#if defined(SO_TYPE)
+ if (esock_getopt_int(sock, SOL_SOCKET, SO_TYPE, type))
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+/* The eopts contains an integer 'type' key.
+ */
+static
+BOOLEAN_T open_get_type(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* type)
+{
+ ERL_NIF_TERM etype;
+
+ if (! GET_MAP_VAL(env, eopts, esock_atom_type, &etype))
+ return FALSE;
+
+ if (! esock_decode_type(env, etype, type))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* The eopts contains an integer 'type' key.
+ */
+static
+BOOLEAN_T open_get_protocol(ErlNifEnv* env,
+ ERL_NIF_TERM eopts,
+ int* protocol)
+{
+ return esock_extract_int_from_map(env, eopts,
+ esock_atom_protocol, protocol);
+}
+
+
+/* The eopts contains a boolean 'dup' key. Defaults to TRUE.
+ */
+static
+BOOLEAN_T open_todup(ErlNifEnv* env, ERL_NIF_TERM eopts)
+{
+ return esock_get_bool_from_map(env, eopts, esock_atom_dup, TRUE);
+}
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_open_plain(ErlNifEnv* env,
+ int domain,
+ int type,
+ int protocol,
+ ERL_NIF_TERM eopts,
+ const ESockData* dataP)
+{
+ BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg);
+ BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg);
+ ESockDescriptor* descP;
+ ERL_NIF_TERM sockRef;
+ int proto = protocol;
+ SOCKET sock;
+ char* netns;
+#ifdef HAVE_SETNS
+ int save_errno;
+ int current_ns = 0;
+#endif
+ ErlNifPid self;
+
+ /* Keep track of the creator
+ * This should not be a problem, but just in case
+ * the *open* function is used with the wrong kind
+ * of environment...
+ */
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "essio_open4 -> entry with"
+ "\r\n domain: %d"
+ "\r\n type: %d"
+ "\r\n protocol: %d"
+ "\r\n eopts: %T"
+ "\r\n", domain, type, protocol, eopts) );
+
+
+#ifdef HAVE_SETNS
+ if (open_get_netns(env, eopts, &netns)) {
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "essio_open4 -> namespace: %s\r\n", netns) );
+ }
+#else
+ netns = NULL;
+#endif
+
+
+#ifdef HAVE_SETNS
+ if ((netns != NULL) &&
+ (! change_network_namespace(dbg,
+ netns, &current_ns, &save_errno))) {
+ FREE(netns);
+ return esock_make_error_errno(env, save_errno);
+ }
+#endif
+
+ if (ESOCK_IS_ERROR(sock = sock_open(domain, type, proto))) {
+ if (netns != NULL) FREE(netns);
+ return esock_make_error_errno(env, sock_errno());
+ }
+
+ SSDBG2( dbg, ("UNIX-ESSIO", "essio_open4 -> open success: %d\r\n", sock) );
+
+
+ /* NOTE that if the protocol = 0 (default) and the domain is not
+ * local (AF_LOCAL) we need to explicitly get the protocol here!
+ */
+
+ if (proto == 0)
+ (void) esock_open_which_protocol(sock, &proto);
+
+#ifdef HAVE_SETNS
+ if (netns != NULL) {
+ FREE(netns);
+ if (! restore_network_namespace(dbg,
+ current_ns, sock, &save_errno))
+ return esock_make_error_errno(env, save_errno);
+ }
+#endif
+
+ SET_NONBLOCKING(sock);
+
+
+ /* Create and initiate the socket "descriptor" */
+ descP = esock_alloc_descriptor(sock);
+ descP->ctrlPid = self;
+ descP->domain = domain;
+ descP->type = type;
+ descP->protocol = proto;
+
+ sockRef = enif_make_resource(env, descP);
+ enif_release_resource(descP);
+
+ ESOCK_ASSERT( MONP("esock_open -> ctrl",
+ env, descP,
+ &descP->ctrlPid,
+ &descP->ctrlMon) == 0 );
+
+ descP->dbg = dbg;
+ descP->useReg = useReg;
+ esock_inc_socket(domain, type, proto);
+
+ /* And finally (maybe) update the registry */
+ if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
+
+ return esock_make_ok2(env, sockRef);
+}
+
+
+#ifdef HAVE_SETNS
+/* open_get_netns - extract the netns field from the opts map
+ */
+static
+BOOLEAN_T open_get_netns(ErlNifEnv* env, ERL_NIF_TERM opts, char** netns)
+{
+ ERL_NIF_TERM val;
+ ErlNifBinary bin;
+ char* buf;
+
+ /* The currently only supported extra option is: netns */
+ if (!GET_MAP_VAL(env, opts, esock_atom_netns, &val)) {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+
+ /* The value should be a binary file name */
+ if (! enif_inspect_binary(env, val, &bin)) {
+ *netns = NULL; // Just in case...
+ return FALSE;
+ }
+
+ ESOCK_ASSERT( (buf = MALLOC(bin.size+1)) != NULL );
+
+ sys_memcpy(buf, bin.data, bin.size);
+ buf[bin.size] = '\0';
+ *netns = buf;
+
+ return TRUE;
+}
+
+
+/* We should really have another API, so that we can return errno... */
+
+/* *** change network namespace ***
+ * Retrieve the current namespace and set the new.
+ * Return result and previous namespace if successful.
+ */
+static
+BOOLEAN_T change_network_namespace(BOOLEAN_T dbg,
+ char* netns, int* cns, int* err)
+{
+ int save_errno;
+ int current_ns = 0;
+ int new_ns = 0;
+
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "change_network_namespace -> entry with"
+ "\r\n new ns: %s"
+ "\r\n", netns) );
+
+ current_ns = open("/proc/self/ns/net", O_RDONLY);
+ if (ESOCK_IS_ERROR(current_ns)) {
+ *err = sock_errno();
+ return FALSE;
+ }
+ new_ns = open(netns, O_RDONLY);
+ if (ESOCK_IS_ERROR(new_ns)) {
+ save_errno = sock_errno();
+ (void) close(current_ns);
+ *err = save_errno;
+ return FALSE;
+ }
+ if (setns(new_ns, CLONE_NEWNET) != 0) {
+ save_errno = sock_errno();
+ (void) close(new_ns);
+ (void) close(current_ns);
+ *err = save_errno;
+ return FALSE;
+ } else {
+ (void) close(new_ns);
+ *cns = current_ns;
+ return TRUE;
+ }
+}
+
+
+/* *** restore network namespace ***
+ * Restore the previous namespace (see above).
+ */
+static
+BOOLEAN_T restore_network_namespace(BOOLEAN_T dbg,
+ int ns, SOCKET sock, int* err)
+{
+ SSDBG2( dbg,
+ ("UNIX-ESSIO", "restore_network_namespace -> entry with"
+ "\r\n ns: %d"
+ "\r\n", ns) );
+
+ if (setns(ns, CLONE_NEWNET) != 0) {
+ /* XXX Failed to restore network namespace.
+ * What to do? Tidy up and return an error...
+ * Note that the thread now might still be in the namespace.
+ * Can this even happen? Should the emulator be aborted?
+ */
+ int save_errno = sock_errno();
+ (void) close(sock);
+ (void) close(ns);
+ *err = save_errno;
+ return FALSE;
+ } else {
+ (void) close(ns);
+ return TRUE;
+ }
+}
+
+#endif
+
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_bind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESockAddress* sockAddrP,
+ SOCKLEN_T addrLen)
+{
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ if (sock_bind(descP->sock, &sockAddrP->sa, addrLen) < 0) {
+ return esock_make_error_errno(env, sock_errno());
+ }
+
+ descP->readState |= ESOCK_STATE_BOUND;
+
+ return esock_atom_ok;
+}
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM connRef,
+ ESockAddress* addrP,
+ SOCKLEN_T addrLen)
+{
+ int save_errno;
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ /*
+ * Verify that we are in the proper state
+ */
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->currentWriterP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ if (descP->connectorP != NULL) {
+ /* Connect in progress */
+
+ if (COMPARE_PIDS(&self, &descP->connector.pid) != 0) {
+ /* Other process has connect in progress */
+ if (addrP != NULL) {
+ return esock_make_error(env, esock_atom_already);
+ } else {
+ /* This is a bad call sequence
+ * - connect without an address is only allowed
+ * for the connecting process
+ */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+ }
+
+ /* Finalize after received select message */
+
+ esock_requestor_release("essio_connect finalize -> connected",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+
+ if (! verify_is_connected(descP, &save_errno)) {
+ return esock_make_error_errno(env, save_errno);
+ }
+
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+
+ return esock_atom_ok;
+ }
+
+ /* No connect in progress */
+
+ if (addrP == NULL)
+ /* This is a bad call sequence
+ * - connect without an address is only allowed when
+ * a connect is in progress, after getting the select message
+ */
+ return esock_raise_invalid(env, esock_atom_state);
+
+ /* Initial connect call, with address */
+
+ if (sock_connect(descP->sock, (struct sockaddr*) addrP, addrLen) == 0) {
+ /* Success already! */
+ SSDBG( descP, ("UNIX-ESSIO", "essio_connect {%d} -> connected\r\n",
+ descP->sock) );
+
+ descP->writeState |= ESOCK_STATE_CONNECTED;
+
+ return esock_atom_ok;
+ }
+
+ /* Connect returned error */
+ save_errno = sock_errno();
+
+ switch (save_errno) {
+
+ case EINPROGRESS: /* Unix & OSE!! */
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_connect {%d} -> would block => select\r\n",
+ descP->sock) );
+ {
+ int sres;
+
+ if ((sres =
+ esock_select_write(env, descP->sock, descP, NULL,
+ sockRef, connRef)) < 0)
+ return
+ enif_raise_exception(env,
+ MKT2(env, esock_atom_select_write,
+ MKI(env, sres)));
+ /* Initiate connector */
+ descP->connector.pid = self;
+ ESOCK_ASSERT( MONP("essio_connect -> conn",
+ env, descP,
+ &self, &descP->connector.mon) == 0 );
+ descP->connector.env = esock_alloc_env("connector");
+ descP->connector.ref = CP_TERM(descP->connector.env, connRef);
+ descP->connectorP = &descP->connector;
+ descP->writeState |=
+ (ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
+
+ return esock_atom_select;
+ }
+ break;
+
+ default:
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_connect {%d} -> error: %d\r\n",
+ descP->sock, save_errno) );
+
+ return esock_make_error_errno(env, save_errno);
+
+ } // switch(save_errno)
+}
+
+
+/* *** verify_is_connected ***
+ * Check if a connection has been established.
+ */
+static
+BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err)
+{
+ /*
+ * *** This is strange ***
+ *
+ * This *should* work on Windows NT too, but doesn't.
+ * An bug in Winsock 2.0 for Windows NT?
+ *
+ * See "Unix Netwok Programming", "The Sockets Networking API",
+ * W.R.Stevens, Volume 1, third edition, 16.4 Nonblocking 'connect',
+ * before Interrupted 'connect' (p 412) for a discussion about
+ * Unix portability and non blocking connect.
+ */
+
+ int error = 0;
+
+#ifdef SO_ERROR
+ if (! esock_getopt_int(descP->sock, SOL_SOCKET, SO_ERROR, &error)) {
+ // Solaris does it this way according to W.R.Stevens
+ error = sock_errno();
+ }
+#elif 1
+ char buf[0];
+ if (ESOCK_IS_ERROR(read(descP->sock, buf, sizeof(buf)))) {
+ error = sock_errno();
+ }
+#else
+ /* This variant probably returns wrong error value
+ * ENOTCONN instead of the actual connect error
+ */
+ ESockAddress remote;
+ SOCKLEN_T addrLen = sizeof(remote);
+ sys_memzero((char *) &remote, addrLen);
+ if (sock_peer(descP->sock,
+ (struct sockaddr*) &remote, &addrLen)) < 0) {
+ error = sock_errno();
+ }
+#endif
+
+ if (error != 0) {
+ *err = error;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+
+/* *** essio_listen *** */
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
+{
+ ErlNifPid caller;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->currentReaderP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ if (descP->currentAcceptorP == NULL) {
+ SOCKET accSock;
+
+ /* We have no active acceptor (and therefore no acceptors in queue)
+ */
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_accept {%d} -> try accept\r\n",
+ descP->sock) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_tries, &descP->accTries, 1);
+
+ accSock = sock_accept(descP->sock, NULL, NULL);
+
+ if (ESOCK_IS_ERROR(accSock)) {
+ int save_errno;
+
+ save_errno = sock_errno();
+
+ return essio_accept_listening_error(env, descP, sockRef,
+ accRef, caller, save_errno);
+ } else {
+ /* We got an incoming connection */
+ return essio_accept_listening_accept(env, descP, sockRef,
+ accSock, caller);
+ }
+ } else {
+
+ /* We have an active acceptor and possibly acceptors waiting in queue.
+ * If the pid of the calling process is not the pid of the
+ * "current process", push the requester onto the (acceptor) queue.
+ */
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_accept_accepting -> check: "
+ "is caller current acceptor:"
+ "\r\n Caller: %T"
+ "\r\n Current: %T"
+ "\r\n Current Mon: %T"
+ "\r\n",
+ caller,
+ descP->currentAcceptor.pid,
+ ESOCK_MON2TERM(env, &descP->currentAcceptor.mon)) );
+
+ if (COMPARE_PIDS(&descP->currentAcceptor.pid, &caller) == 0) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting {%d} -> current acceptor"
+ "\r\n", descP->sock) );
+
+ return essio_accept_accepting_current(env, descP, sockRef, accRef);
+
+ } else {
+
+ /* Not the "current acceptor", so (maybe) push onto queue */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting {%d} -> *not* current acceptor\r\n",
+ descP->sock) );
+
+ return essio_accept_accepting_other(env, descP, accRef, caller);
+ }
+ }
+}
+
+
+/* *** essio_accept_listening_error ***
+ *
+ * The accept call resultet in an error - handle it.
+ * There are only two cases:
+ * 1) BLOCK => Attempt a "retry"
+ * 2) Other => Return the value (converted to an atom)
+ */
+static
+ERL_NIF_TERM essio_accept_listening_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid caller,
+ int save_errno)
+{
+ ERL_NIF_TERM res;
+
+ if (save_errno == ERRNO_BLOCK ||
+ save_errno == EAGAIN) {
+
+ /* *** Try again later *** */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_listening_error {%d} -> would block - retry\r\n",
+ descP->sock) );
+
+ descP->currentAcceptor.pid = caller;
+ ESOCK_ASSERT( MONP("essio_accept_listening -> current acceptor",
+ env, descP,
+ &descP->currentAcceptor.pid,
+ &descP->currentAcceptor.mon) == 0 );
+ ESOCK_ASSERT( descP->currentAcceptor.env == NULL );
+ descP->currentAcceptor.env = esock_alloc_env("current acceptor");
+ descP->currentAcceptor.ref =
+ CP_TERM(descP->currentAcceptor.env, accRef);
+ descP->currentAcceptorP = &descP->currentAcceptor;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_listening_error {%d} -> retry for: "
+ "\r\n Current Pid: %T"
+ "\r\n Current Mon: %T"
+ "\r\n",
+ descP->sock,
+ descP->currentAcceptor.pid,
+ ESOCK_MON2TERM(env, &descP->currentAcceptor.mon)) );
+
+ res = essio_accept_busy_retry(env, descP, sockRef, accRef, NULL);
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_listening {%d} -> errno: %d\r\n",
+ descP->sock, save_errno) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_fails, &descP->accFails, 1);
+
+ res = esock_make_error_errno(env, save_errno);
+ }
+
+ return res;
+}
+
+
+/* *** essio_accept_listening_accept ***
+ *
+ * The accept call was successful (accepted) - handle the new connection.
+ */
+static
+ERL_NIF_TERM essio_accept_listening_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ErlNifPid caller)
+{
+ ERL_NIF_TERM res;
+
+ essio_accept_accepted(env, descP, sockRef, accSock, caller, &res);
+
+ return res;
+}
+
+
+/* *** essio_accept_accepting_current ***
+ * Handles when the current acceptor makes another attempt.
+ */
+static
+ERL_NIF_TERM essio_accept_accepting_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
+{
+ SOCKET accSock;
+ int save_errno;
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current {%d} -> try accept\r\n",
+ descP->sock) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_tries, &descP->accTries, 1);
+
+ accSock = sock_accept(descP->sock, NULL, NULL);
+
+ if (ESOCK_IS_ERROR(accSock)) {
+
+ save_errno = sock_errno();
+
+ res = essio_accept_accepting_current_error(env, descP, sockRef,
+ accRef, save_errno);
+ } else {
+
+ res = essio_accept_accepting_current_accept(env, descP, sockRef,
+ accSock);
+ }
+
+ return res;
+}
+
+
+/* *** essio_accept_accepting_current_accept ***
+ *
+ * Handles when the current acceptor succeeded in its accept call -
+ * handle the new connection.
+ */
+static
+ERL_NIF_TERM essio_accept_accepting_current_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current_accept {%d}"
+ "\r\n", descP->sock) );
+
+ if (essio_accept_accepted(env, descP, sockRef, accSock,
+ descP->currentAcceptor.pid, &res)) {
+
+ ESOCK_ASSERT( DEMONP("essio_accept_accepting_current_accept -> "
+ "current acceptor",
+ env, descP, &descP->currentAcceptor.mon) == 0);
+
+ MON_INIT(&descP->currentAcceptor.mon);
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current_accept {%d} ->"
+ " no more acceptors"
+ "\r\n", descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ }
+
+ return res;
+}
+
+
+/* *** essio_accept_accepting_current_error ***
+ * The accept call of current acceptor resultet in an error - handle it.
+ * There are only two cases:
+ * 1) BLOCK => Attempt a "retry"
+ * 2) Other => Return the value (converted to an atom)
+ */
+static
+ERL_NIF_TERM essio_accept_accepting_current_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef,
+ int save_errno)
+{
+ ERL_NIF_TERM res, reason;
+
+ if (save_errno == ERRNO_BLOCK ||
+ save_errno == EAGAIN) {
+
+ /*
+ * Just try again, no real error, just a ghost trigger from poll,
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current_error {%d} -> "
+ "would block: try again\r\n", descP->sock) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_waits, &descP->accWaits, 1);
+
+ res = essio_accept_busy_retry(env, descP, sockRef, opRef,
+ &descP->currentAcceptor.pid);
+
+ } else {
+ ESockRequestor req;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current_error {%d} -> "
+ "error: %d\r\n", descP->sock, save_errno) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_fails, &descP->accFails, 1);
+
+ esock_requestor_release("essio_accept_accepting_current_error",
+ env, descP, &descP->currentAcceptor);
+
+ reason = MKA(env, erl_errno_id(save_errno));
+ res = esock_make_error(env, reason);
+
+ req.env = NULL;
+ while (esock_acceptor_pop(env, descP, &req)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_accepting_current_error {%d} -> abort %T\r\n",
+ descP->sock, req.pid) );
+
+ esock_send_abort_msg(env, descP, sockRef, &req, reason);
+
+ (void) DEMONP("essio_accept_accepting_current_error -> "
+ "pop'ed writer",
+ env, descP, &req.mon);
+ }
+ descP->currentAcceptorP = NULL;
+ }
+
+ return res;
+}
+
+
+/* *** essio_accept_accepting_other ***
+ * Handles when the another acceptor makes an attempt, which
+ * results (maybe) in the request being pushed onto the
+ * acceptor queue.
+ */
+static
+ERL_NIF_TERM essio_accept_accepting_other(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ErlNifPid caller)
+{
+ if (! esock_acceptor_search4pid(env, descP, &caller)) {
+ esock_acceptor_push(env, descP, caller, ref, NULL);
+ return esock_atom_select;
+ } else {
+ /* Acceptor already in queue */
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+}
+
+
+/* *** essio_accept_busy_retry ***
+ *
+ * Perform a retry select. If successful, set nextState.
+ */
+static
+ERL_NIF_TERM essio_accept_busy_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid* pidP)
+{
+ int sres;
+ ERL_NIF_TERM res;
+
+ if ((sres = esock_select_read(env, descP->sock, descP, pidP,
+ sockRef, accRef)) < 0) {
+
+ ESOCK_ASSERT( DEMONP("essio_accept_busy_retry - select failed",
+ env, descP, &descP->currentAcceptor.mon) == 0);
+
+ MON_INIT(&descP->currentAcceptor.mon);
+
+ /* It is very unlikely that a next acceptor will be able
+ * to do anything successful, but we will clean the queue
+ */
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_accept_busy_retry {%d} -> no more acceptors\r\n",
+ descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ res =
+ enif_raise_exception(env,
+ MKT2(env, esock_atom_select_read,
+ MKI(env, sres)));
+ } else {
+ descP->readState |=
+ (ESOCK_STATE_ACCEPTING | ESOCK_STATE_SELECTED);
+ res = esock_atom_select;
+ }
+
+ return res;
+}
+
+
+/* *** essio_accept_accepted ***
+ *
+ * Generic function handling a successful accept.
+ */
+static
+BOOLEAN_T essio_accept_accepted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ErlNifPid pid,
+ ERL_NIF_TERM* result)
+{
+ ESockDescriptor* accDescP;
+ ERL_NIF_TERM accRef;
+
+ /*
+ * We got one
+ */
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_acc_success, &descP->accSuccess, 1);
+
+ accDescP = esock_alloc_descriptor(accSock);
+ accDescP->domain = descP->domain;
+ accDescP->type = descP->type;
+ accDescP->protocol = descP->protocol;
+
+ MLOCK(descP->writeMtx);
+
+ accDescP->rBufSz = descP->rBufSz; // Inherit buffer size
+ accDescP->rNum = descP->rNum; // Inherit buffer uses
+ accDescP->rNumCnt = 0;
+ accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size
+ accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size
+ accDescP->iow = descP->iow; // Inherit iow
+ accDescP->dbg = descP->dbg; // Inherit debug flag
+ accDescP->useReg = descP->useReg; // Inherit useReg flag
+ esock_inc_socket(accDescP->domain, accDescP->type, accDescP->protocol);
+
+ accRef = enif_make_resource(env, accDescP);
+ enif_release_resource(accDescP);
+
+ accDescP->ctrlPid = pid;
+ /* pid has actually been compared equal to self()
+ * in this code path just a little while ago
+ */
+ ESOCK_ASSERT( MONP("essio_accept_accepted -> ctrl",
+ env, accDescP,
+ &accDescP->ctrlPid,
+ &accDescP->ctrlMon) == 0 );
+
+ SET_NONBLOCKING(accDescP->sock);
+
+ accDescP->writeState |= ESOCK_STATE_CONNECTED;
+
+ MUNLOCK(descP->writeMtx);
+
+ /* And finally (maybe) update the registry */
+ if (descP->useReg) esock_send_reg_add_msg(env, descP, accRef);
+
+ *result = esock_make_ok2(env, accRef);
+
+ return TRUE;
+}
+
+
+
+/* ========================================================================
+ * Do the actual send.
+ * Do some initial writer checks, do the actual send and then
+ * analyze the result. If we are done, another writer may be
+ * scheduled (if there is one in the writer queue).
+ */
+extern
+ERL_NIF_TERM essio_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* sndDataP,
+ int flags)
+{
+ ssize_t send_result;
+ ERL_NIF_TERM writerCheck;
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ send_result = (ssize_t) sndDataP->size;
+ if ((size_t) send_result != sndDataP->size)
+ return esock_make_error_invalid(env, esock_atom_data_size);
+
+ /* Ensure that we either have no current writer or we are it,
+ * or enqueue this process if there is a current writer */
+ if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
+ SSDBG( descP, ("UNIX-ESSIO", "esock_send {%d} -> writer check failed: "
+ "\r\n %T\r\n", descP->sock, writerCheck) );
+ return writerCheck;
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ send_result = sock_send(descP->sock, sndDataP->data, sndDataP->size, flags);
+
+ return send_check_result(env, descP,
+ send_result, sndDataP->size, FALSE,
+ sockRef, sendRef);
+
+}
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_sendto(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* dataP,
+ int flags,
+ ESockAddress* toAddrP,
+ SOCKLEN_T toAddrLen)
+{
+ ssize_t result;
+ ERL_NIF_TERM writerCheck;
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ result = (ssize_t) dataP->size;
+ if ((size_t) result != dataP->size)
+ return esock_make_error_invalid(env, esock_atom_data_size);
+
+ /* Ensure that we either have no current writer or we are it,
+ * or enqueue this process if there is a current writer */
+ if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendto {%d} -> writer check failed: "
+ "\r\n %T\r\n", descP->sock, writerCheck) );
+ return writerCheck;
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ if (toAddrP != NULL) {
+ result = sock_sendto(descP->sock,
+ dataP->data, dataP->size, flags,
+ &toAddrP->sa, toAddrLen);
+ } else {
+ result = sock_sendto(descP->sock,
+ dataP->data, dataP->size, flags,
+ NULL, 0);
+ }
+
+ return send_check_result(env, descP, result, dataP->size, FALSE,
+ sockRef, sendRef);
+}
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_sendmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ERL_NIF_TERM eMsg,
+ int flags,
+ ERL_NIF_TERM eIOV,
+ const ESockData* dataP)
+{
+ ERL_NIF_TERM res, eAddr, eCtrl;
+ ESockAddress addr;
+ struct msghdr msgHdr;
+ ErlNifIOVec *iovec = NULL;
+ char* ctrlBuf;
+ size_t ctrlBufLen, ctrlBufUsed;
+ ssize_t dataSize, sendmsg_result;
+ ERL_NIF_TERM writerCheck, tail;
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that we either have no current writer or we are it,
+ * or enqueue this process if there is a current writer */
+ if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_sendmsg {%d} -> writer check failed: "
+ "\r\n %T\r\n", descP->sock, writerCheck) );
+ return writerCheck;
+ }
+
+ /* Initiate the .name and .namelen fields depending on if
+ * we have an address or not
+ */
+ if (! GET_MAP_VAL(env, eMsg, esock_atom_addr, &eAddr)) {
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> no address\r\n", descP->sock) );
+
+ msgHdr.msg_name = NULL;
+ msgHdr.msg_namelen = 0;
+ } else {
+ msgHdr.msg_name = (void*) &addr;
+ msgHdr.msg_namelen = sizeof(addr);
+ sys_memzero((char *) msgHdr.msg_name, msgHdr.msg_namelen);
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_sendmsg {%d} ->"
+ "\r\n address: %T"
+ "\r\n", descP->sock, eAddr) );
+
+ if (! esock_decode_sockaddr(env, eAddr,
+ msgHdr.msg_name,
+ &msgHdr.msg_namelen)) {
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> invalid address\r\n",
+ descP->sock) );
+ return esock_make_invalid(env, esock_atom_addr);
+ }
+ }
+
+ /* Extract the *mandatory* 'iov', which must be an erlang:iovec(),
+ * from which we take at most IOV_MAX binaries
+ */
+ if ((! enif_inspect_iovec(NULL, dataP->iov_max, eIOV, &tail, &iovec))) {
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> not an iov\r\n",
+ descP->sock) );
+
+ return esock_make_invalid(env, esock_atom_iov);
+ }
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_sendmsg {%d} ->"
+ "\r\n iovcnt: %lu"
+ "\r\n tail: %s"
+ "\r\n", descP->sock,
+ (unsigned long) iovec->iovcnt,
+ B2S(! enif_is_empty_list(env, tail))) );
+
+ /* We now have an allocated iovec */
+
+ eCtrl = esock_atom_undefined;
+ ctrlBufLen = 0;
+ ctrlBuf = NULL;
+
+ if (iovec->iovcnt > dataP->iov_max) {
+ if (descP->type == SOCK_STREAM) {
+ iovec->iovcnt = dataP->iov_max;
+ } else {
+ /* We can not send the whole packet in one sendmsg() call */
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> iovcnt > iov_max\r\n",
+ descP->sock) );
+ res = esock_make_invalid(env, esock_atom_iov);
+ goto done_free_iovec;
+ }
+ }
+
+ dataSize = 0;
+ {
+ ERL_NIF_TERM h, t;
+ ErlNifBinary bin;
+ size_t i;
+
+ /* Find out if there is remaining data in the tail.
+ * Skip empty binaries otherwise break.
+ * If 'tail' after loop exit is the empty list
+ * there was no more data. Otherwise there is more
+ * data or the 'iov' is invalid.
+ */
+ for (;;) {
+ if (enif_get_list_cell(env, tail, &h, &t) &&
+ enif_inspect_binary(env, h, &bin) &&
+ (bin.size == 0)) {
+ tail = t;
+ continue;
+ } else
+ break;
+ }
+
+ if ((! enif_is_empty_list(env, tail)) &&
+ (descP->type != SOCK_STREAM)) {
+ /* We can not send the whole packet in one sendmsg() call */
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> invalid tail\r\n",
+ descP->sock) );
+ res = esock_make_invalid(env, esock_atom_iov);
+ goto done_free_iovec;
+ }
+
+ /* Calculate the data size */
+
+ for (i = 0; i < iovec->iovcnt; i++) {
+ size_t len = iovec->iov[i].iov_len;
+ dataSize += len;
+ if (dataSize < len) {
+ /* Overflow */
+ SSDBG( descP, ("UNIX-ESSIO", "essio_sendmsg {%d} -> Overflow"
+ "\r\n i: %lu"
+ "\r\n len: %lu"
+ "\r\n dataSize: %ld"
+ "\r\n", descP->sock, (unsigned long) i,
+ (unsigned long) len, (long) dataSize) );
+ res = esock_make_invalid(env, esock_atom_iov);
+ goto done_free_iovec;
+ }
+ }
+ }
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> iovec size verified"
+ "\r\n iov length: %lu"
+ "\r\n data size: %u"
+ "\r\n",
+ descP->sock,
+ (unsigned long) iovec->iovcnt, (long) dataSize) );
+
+ msgHdr.msg_iovlen = iovec->iovcnt;
+ msgHdr.msg_iov = iovec->iov;
+
+ /* Extract the *optional* 'ctrl' */
+ if (GET_MAP_VAL(env, eMsg, esock_atom_ctrl, &eCtrl)) {
+ ctrlBufLen = descP->wCtrlSz;
+ ctrlBuf = (char*) MALLOC(ctrlBufLen);
+ ESOCK_ASSERT( ctrlBuf != NULL );
+ }
+ SSDBG( descP, ("UNIX-ESSIO", "essio_sendmsg {%d} -> optional ctrl: "
+ "\r\n ctrlBuf: %p"
+ "\r\n ctrlBufLen: %lu"
+ "\r\n eCtrl: %T"
+ "\r\n", descP->sock,
+ ctrlBuf, (unsigned long) ctrlBufLen, eCtrl) );
+
+ /* Decode the ctrl and initiate that part of the msghdr.
+ */
+ if (ctrlBuf != NULL) {
+ if (! decode_cmsghdrs(env, descP,
+ eCtrl,
+ ctrlBuf, ctrlBufLen, &ctrlBufUsed)) {
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendmsg {%d} -> invalid ctrl\r\n",
+ descP->sock) );
+ res = esock_make_invalid(env, esock_atom_ctrl);
+ goto done_free_iovec;
+ }
+ } else {
+ ctrlBufUsed = 0;
+ }
+ msgHdr.msg_control = ctrlBuf;
+ msgHdr.msg_controllen = ctrlBufUsed;
+
+ /* The msg_flags field is not used when sending,
+ * but zero it just in case */
+ msgHdr.msg_flags = 0;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_tries, &descP->writeTries, 1);
+
+ /* And now, try to send the message */
+ sendmsg_result = sock_sendmsg(descP->sock, &msgHdr, flags);
+
+ res = send_check_result(env, descP, sendmsg_result, dataSize,
+ (! enif_is_empty_list(env, tail)),
+ sockRef, sendRef);
+
+ done_free_iovec:
+ FREE_IOVEC( iovec );
+ if (ctrlBuf != NULL) FREE(ctrlBuf);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_sendmsg {%d} -> done"
+ "\r\n %T"
+ "\r\n", descP->sock, res) );
+
+ return res;
+
+}
+
+
+/* ========================================================================
+ * Start a sendfile() operation
+ */
+extern
+ERL_NIF_TERM essio_sendfile_start(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ off_t offset,
+ size_t count,
+ ERL_NIF_TERM fRef)
+{
+#if defined(HAVE_SENDFILE)
+ ERL_NIF_TERM writerCheck;
+ ssize_t res;
+ int err;
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile_start {%d} -> entry with"
+ "\r\n sockRef: %T"
+ "\r\n sendRef: %T"
+ "\r\n fRef: %T"
+ "\r\n offset: %lu"
+ "\r\n count: %lu"
+ "\r\n",
+ descP->sock, sockRef, sendRef,
+ fRef, (unsigned long) offset, (unsigned long) count) );
+
+ if (! IS_OPEN(descP->writeState)) {
+ return esock_make_error_closed(env);
+ }
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->connectorP != NULL) {
+ return esock_make_error_invalid(env, esock_atom_state);
+ }
+
+ /* Ensure that we either have no current writer or we are it,
+ * or enqueue this process if there is a current writer
+ */
+ if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile_start {%d} -> writer check failed: "
+ "\r\n %T\r\n", descP->sock, writerCheck) );
+
+ /* Returns 'select' if current process got enqueued,
+ * or exception invalid state if current process already
+ * was enqueued
+ */
+ return writerCheck;
+ }
+
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Get a dup:ed file handle from prim_file_nif
+ * through a NIF dyncall
+ */
+ {
+ struct prim_file_nif_dyncall_dup dc_dup;
+
+ dc_dup.op = prim_file_nif_dyncall_dup;
+ dc_dup.result = EINVAL; // should not be needed
+
+ /* Request the handle */
+ if (enif_dynamic_resource_call(env,
+ esock_atom_prim_file,
+ esock_atom_efile,
+ fRef,
+ &dc_dup)
+ != 0) {
+ return
+ essio_sendfile_error(env, descP, sockRef,
+ MKT2(env,
+ esock_atom_invalid,
+ esock_atom_efile));
+ }
+ if (dc_dup.result != 0) {
+ return
+ essio_sendfile_errno(env, descP, sockRef, dc_dup.result);
+ }
+ descP->sendfileHandle = dc_dup.handle;
+ }
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile_start(%T) {%d} -> sendRef: %T"
+ "\r\n sendfileHandle: %d"
+ "\r\n",
+ sockRef, descP->sock, sendRef,
+ descP->sendfileHandle) );
+
+ if (descP->sendfileCountersP == NULL) {
+ descP->sendfileCountersP = MALLOC(sizeof(ESockSendfileCounters));
+ *descP->sendfileCountersP = initESockSendfileCounters;
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile_tries,
+ &descP->sendfileCountersP->tries, 1);
+ descP->sendfileCountersP->maxCnt = 0;
+
+ res = essio_sendfile(env, descP, sockRef, offset, &count, &err);
+
+ if (res < 0) { // Terminal error
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return essio_sendfile_errno(env, descP, sockRef, err);
+
+ } else if (res > 0) { // Retry by select
+
+ if (descP->currentWriterP == NULL) {
+ int mon_res;
+
+ /* Register writer as current */
+ ESOCK_ASSERT( enif_self(env, &descP->currentWriter.pid) != NULL );
+ mon_res =
+ MONP("sendfile-start -> current writer",
+ env, descP,
+ &descP->currentWriter.pid,
+ &descP->currentWriter.mon);
+ ESOCK_ASSERT( mon_res >= 0 );
+
+ if (mon_res > 0) {
+ /* Caller died already, can happen for dirty NIFs */
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return essio_sendfile_error(env, descP, sockRef,
+ MKT2(env,
+ esock_atom_invalid,
+ esock_atom_not_owner));
+ }
+ ESOCK_ASSERT( descP->currentWriter.env == NULL );
+ descP->currentWriter.env = esock_alloc_env("current-writer");
+ descP->currentWriter.ref =
+ CP_TERM(descP->currentWriter.env, sendRef);
+ descP->currentWriterP = &descP->currentWriter;
+ }
+ // else current writer is already registered by esock_requestor_pop()
+
+ return essio_sendfile_select(env, descP, sockRef, sendRef, count);
+
+ } else { // res == 0: Done
+ return essio_sendfile_ok(env, descP, sockRef, count);
+ }
+#else
+ VOID(env);
+ VOID(descP);
+ VOID(sockRef);
+ VOID(sendRef);
+ VOID(offset);
+ VOID(count);
+ VOID(fRef);
+ return enif_raise_exception(env, MKA(env, "notsup"));
+#endif
+}
+
+
+/* ========================================================================
+ * Continue an ongoing sendfile operation
+ */
+
+extern
+ERL_NIF_TERM essio_sendfile_cont(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ off_t offset,
+ size_t count)
+{
+#if defined(HAVE_SENDFILE)
+ ErlNifPid caller;
+ ssize_t res;
+ int err;
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile_cont {%d} -> entry"
+ "\r\n sockRef: %T"
+ "\r\n sendRef: %T"
+ "\r\n", descP->sock, sockRef, sendRef) );
+
+ if (! IS_OPEN(descP->writeState))
+ return esock_make_error_closed(env);
+
+ /* Connect and Write uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->connectorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Verify that this process has a sendfile operation in progress */
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+ if ((descP->currentWriterP == NULL) ||
+ (descP->sendfileHandle == INVALID_HANDLE) ||
+ (COMPARE_PIDS(&descP->currentWriter.pid, &caller) != 0)) {
+ //
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ res = essio_sendfile(env, descP, sockRef, offset, &count, &err);
+
+ if (res < 0) { // Terminal error
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return essio_sendfile_errno(env, descP, sockRef, err);
+
+ } else if (res > 0) { // Retry by select
+
+ /* Overwrite current writer registration */
+ enif_clear_env(descP->currentWriter.env);
+ descP->currentWriter.ref =
+ CP_TERM(descP->currentWriter.env, sendRef);
+
+ return essio_sendfile_select(env, descP, sockRef, sendRef, count);
+
+ } else { // res == 0: Done
+ return essio_sendfile_ok(env, descP, sockRef, count);
+ }
+#else
+ VOID(env);
+ VOID(descP);
+ VOID(sockRef);
+ VOID(sendRef);
+ VOID(offset);
+ VOID(count);
+ return enif_raise_exception(env, MKA(env, "notsup"));
+#endif
+}
+
+
+/* ========================================================================
+ * Deferred close of the dup:ed file descriptor
+ */
+
+extern
+ERL_NIF_TERM essio_sendfile_deferred_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+#if defined(HAVE_SENDFILE)
+ if (descP->sendfileHandle == INVALID_HANDLE)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return esock_atom_ok;
+#else
+ VOID(env);
+ VOID(descP);
+ return enif_raise_exception(env, MKA(env, "notsup"));
+#endif
+}
+
+
+
+/* ========================================================================
+ * The (read) buffer handling should be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throwing it away...
+ */
+extern
+ERL_NIF_TERM essio_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags)
+{
+ ssize_t read;
+ ErlNifBinary buf;
+ ERL_NIF_TERM readerCheck;
+ int save_errno;
+ size_t bufSz = (len != 0 ? len : descP->rBufSz);
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_recv {%d} -> entry with"
+ "\r\n count,size: (%ld:%u:%lu)"
+ "\r\n", descP->sock,
+ (long) len, descP->rNumCnt, (unsigned long) bufSz) );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->currentAcceptorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that we either have no current reader or that we are it,
+ * or enqueue this process if there is a current reader */
+ if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_recv {%d} -> reader check failed: "
+ "\r\n %T"
+ "\r\n", descP->sock, readerCheck) );
+ return readerCheck;
+ }
+
+ /* Allocate a buffer:
+ * Either as much as we want to read or (if zero (0)) use the "default"
+ * size (what has been configured).
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &buf) );
+
+ // If it fails (read = -1), we need errno...
+ SSDBG( descP, ("UNIX-ESSIO", "essio_recv {%d} -> try read (%lu)\r\n",
+ descP->sock, (unsigned long) buf.size) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ read = sock_recv(descP->sock, buf.data, buf.size, flags);
+ if (ESOCK_IS_ERROR(read)) {
+ save_errno = sock_errno();
+ } else {
+ save_errno = 0; // The value does not actually matter in this case
+ }
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_recv {%d} -> read: %ld (%d)\r\n",
+ descP->sock, (long) read, save_errno) );
+
+ return recv_check_result(env, descP, read, len, save_errno,
+ &buf, sockRef, recvRef);
+}
+
+
+/* *** recv_check_result ***
+ *
+ * Process the result of a call to recv.
+ */
+static
+ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ int saveErrno,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recv_check_result(%T) {%d} -> entry with"
+ "\r\n read: %ld"
+ "\r\n toRead: %ld"
+ "\r\n saveErrno: %d"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock,
+ (long) read, (long) toRead, saveErrno, recvRef) );
+
+
+ /* <KOLLA>
+ *
+ * We need to handle read = 0 for other type(s) (DGRAM) when
+ * its actually valid to read 0 bytes.
+ *
+ * </KOLLA>
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+ ERL_NIF_TERM reason = esock_atom_closed;
+ res = esock_make_error(env, reason);
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ /*
+ * When a stream socket peer has performed an orderly shutdown,
+ * the return value will be 0 (the traditional "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes!
+ *
+ * We must also notify any waiting readers!
+ */
+
+ recv_error_current_reader(env, descP, sockRef, reason);
+
+ FREE_BIN(bufP);
+
+ } else {
+
+ /* There is a special case: If the provided 'to read' value is
+ * zero (0) (only for type =/= stream).
+ * That means that we read as much as we can, using the default
+ * read buffer size.
+ */
+
+ if (bufP->size == read) {
+
+ /* +++ We filled the buffer +++ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_result(%T) {%d} -> [%lu] filled the buffer\r\n",
+ sockRef, descP->sock, (unsigned long) bufP->size) );
+
+ res = recv_check_full(env, descP, read, toRead, bufP,
+ sockRef, recvRef);
+
+ } else if (read < 0) {
+
+ /* +++ Error handling +++ */
+
+ res = recv_check_fail(env, descP, saveErrno, bufP, NULL,
+ sockRef, recvRef);
+
+ } else {
+
+ /* +++ We did not fill the buffer +++ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_result(%T) {%d} -> [%lu] "
+ "did not fill the buffer (%ld)\r\n",
+ sockRef, descP->sock, (unsigned long) bufP->size,
+ (long) read) );
+
+ res = recv_check_partial(env, descP, read, toRead, bufP,
+ sockRef, recvRef);
+ }
+ }
+
+ return res;
+}
+
+
+
+/* ========================================================================
+ * The (read) buffer handling *must* be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throwing it away...
+ */
+extern
+ERL_NIF_TERM essio_recvfrom(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t len,
+ int flags)
+{
+ ESockAddress fromAddr;
+ SOCKLEN_T addrLen;
+ ssize_t read;
+ int save_errno;
+ ErlNifBinary buf;
+ ERL_NIF_TERM readerCheck;
+ size_t bufSz = (len != 0 ? len : descP->rBufSz);
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_recvfrom {%d} -> entry with"
+ "\r\n bufSz: %d"
+ "\r\n", descP->sock, bufSz) );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->currentAcceptorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that we either have no current reader or that we are it,
+ * or enqueue this process if there is a current reader */
+ if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_recv {%d} -> reader check failed: "
+ "\r\n %T\r\n", descP->sock, readerCheck) );
+ return readerCheck;
+ }
+
+ /* Allocate a buffer:
+ * Either as much as we want to read or (if zero (0)) use the "default"
+ * size (what has been configured).
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &buf) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ addrLen = sizeof(fromAddr);
+ sys_memzero((char*) &fromAddr, addrLen);
+
+ read = sock_recvfrom(descP->sock, buf.data, buf.size, flags,
+ &fromAddr.sa, &addrLen);
+ if (ESOCK_IS_ERROR(read))
+ save_errno = sock_errno();
+ else
+ save_errno = 0; // The value does not actually matter in this case
+
+ return recvfrom_check_result(env, descP, read, save_errno,
+ &buf, &fromAddr, addrLen,
+ sockRef, recvRef);
+}
+
+
+/* The recvfrom function delivers one (1) message. If our buffer
+ * is too small, the message will be truncated. So, regardless
+ * if we filled the buffer or not, we have got what we are going
+ * to get regarding this message.
+ */
+
+static
+ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ int saveErrno,
+ ErlNifBinary* bufP,
+ ESockAddress* fromAddrP,
+ SOCKLEN_T fromAddrLen,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM data, res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recvfrom_check_result(%T) {%d} -> entry with"
+ "\r\n read: %ld"
+ "\r\n saveErrno: %d"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock,
+ (long) read, saveErrno, recvRef) );
+
+ /* <KOLLA>
+ *
+ * We need to handle read = 0 for non_stream socket type(s) when
+ * its actually valid to read 0 bytes.
+ *
+ * </KOLLA>
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+
+ /*
+ * When a stream socket peer has performed an orderly shutdown,
+ * the return value will be 0 (the traditional "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes!
+ */
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ FREE_BIN(bufP);
+
+ return esock_make_error_closed(env);
+ }
+
+ if (read < 0) {
+
+ /* +++ Error handling +++ */
+
+ res = recv_check_fail(env, descP, saveErrno, bufP, NULL,
+ sockRef, recvRef);
+
+ } else {
+
+ /* +++ We successfully got a message - time to encode the address +++ */
+
+ ERL_NIF_TERM eSockAddr;
+
+ esock_encode_sockaddr(env,
+ fromAddrP, fromAddrLen,
+ &eSockAddr);
+
+ if (read == bufP->size) {
+
+ data = MKBIN(env, bufP);
+
+ } else {
+
+ /* +++ We got a chunk of data but +++
+ * +++ since we did not fill the +++
+ * +++ buffer, we must split it +++
+ * +++ into a sub-binary. +++
+ */
+
+ data = MKBIN(env, bufP);
+ data = MKSBIN(env, data, 0, read);
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef, esock_atom_read_pkg,
+ &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef, esock_atom_read_byte,
+ &descP->readByteCnt, read);
+
+ recv_update_current_reader(env, descP, sockRef);
+
+ res = esock_make_ok2(env, MKT2(env, eSockAddr, data));
+
+ }
+
+ return res;
+
+}
+
+
+
+/* ========================================================================
+ * The (read) buffer handling *must* be optimized!
+ * But for now we make it easy for ourselves by
+ * allocating a binary (of the specified or default
+ * size) and then throwing it away...
+ */
+extern
+ERL_NIF_TERM essio_recvmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ ssize_t bufLen,
+ ssize_t ctrlLen,
+ int flags)
+{
+ SOCKLEN_T addrLen;
+ ssize_t read;
+ int save_errno;
+ size_t bufSz = (bufLen != 0 ? bufLen : descP->rBufSz);
+ size_t ctrlSz = (ctrlLen != 0 ? ctrlLen : descP->rCtrlSz);
+ struct msghdr msgHdr;
+ SysIOVec iov[1]; // Shall we always use 1?
+ ErlNifBinary data[1]; // Shall we always use 1?
+ ErlNifBinary ctrl;
+ ERL_NIF_TERM readerCheck;
+ ESockAddress addr;
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_recvmsg {%d} -> entry with"
+ "\r\n bufSz: %lu (%ld)"
+ "\r\n ctrlSz: %ld (%ld)"
+ "\r\n", descP->sock,
+ (unsigned long) bufSz, (long) bufLen,
+ (unsigned long) ctrlSz, (long) ctrlLen) );
+
+ if (! IS_OPEN(descP->readState))
+ return esock_make_error_closed(env);
+
+ /* Accept and Read uses the same select flag
+ * so they can not be simultaneous
+ */
+ if (descP->currentAcceptorP != NULL)
+ return esock_make_error_invalid(env, esock_atom_state);
+
+ /* Ensure that we either have no current reader or that we are it,
+ * or enqueue this process if there is a current reader */
+ if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_recvmsg {%d} -> reader check failed: "
+ "\r\n %T\r\n", descP->sock, readerCheck) );
+ return readerCheck;
+ }
+
+ /* Allocate the (msg) data buffer:
+ */
+ ESOCK_ASSERT( ALLOC_BIN(bufSz, &data[0]) );
+
+ /* Allocate the ctrl (buffer):
+ */
+ ESOCK_ASSERT( ALLOC_BIN(ctrlSz, &ctrl) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_tries, &descP->readTries, 1);
+
+ addrLen = sizeof(addr);
+ sys_memzero((char*) &addr, addrLen);
+ sys_memzero((char*) &msgHdr, sizeof(msgHdr));
+
+ iov[0].iov_base = data[0].data;
+ iov[0].iov_len = data[0].size;
+
+ msgHdr.msg_name = &addr;
+ msgHdr.msg_namelen = addrLen;
+ msgHdr.msg_iov = iov;
+ msgHdr.msg_iovlen = 1; // Should use a constant or calculate...
+ msgHdr.msg_control = ctrl.data;
+ msgHdr.msg_controllen = ctrl.size;
+
+ read = sock_recvmsg(descP->sock, &msgHdr, flags);
+ if (ESOCK_IS_ERROR(read))
+ save_errno = sock_errno();
+ else
+ save_errno = 0; // The value does not actually matter in this case
+
+ return recvmsg_check_result(env, descP, read, save_errno,
+ &msgHdr,
+ data, // Needed for iov encode
+ &ctrl, // Needed for ctrl header encode
+ sockRef, recvRef);
+}
+
+
+/* *** recvmsg_check_result ***
+ *
+ * The recvmsg function delivers one (1) message. If our buffer
+ * is to small, the message will be truncated. So, regardless
+ * if we filled the buffer or not, we have got what we are going
+ * to get regarding this message.
+ */
+static
+ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ int saveErrno,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recvmsg_check_result(%T) {%d} -> entry with"
+ "\r\n read: %ld"
+ "\r\n saveErrno: %d"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock,
+ (long) read, saveErrno, recvRef) );
+
+
+ /* <KOLLA>
+ *
+ * We need to handle read = 0 for non_stream socket type(s) when
+ * its actually valid to read 0 bytes.
+ *
+ * </KOLLA>
+ */
+
+ if ((read == 0) && (descP->type == SOCK_STREAM)) {
+
+ /*
+ * When a stream socket peer has performed an orderly shutdown,
+ * the return value will be 0 (the traditional "end-of-file" return).
+ *
+ * *We* do never actually try to read 0 bytes!
+ */
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_fails, &descP->readFails, 1);
+
+ FREE_BIN(dataBufP); FREE_BIN(ctrlBufP);
+
+ return esock_make_error_closed(env);
+ }
+
+
+ if (read < 0) {
+
+ /* +++ Error handling +++ */
+
+ res = recv_check_fail(env, descP, saveErrno, dataBufP, ctrlBufP,
+ sockRef, recvRef);
+
+ } else {
+
+ /* +++ We successfully got a message - time to encode it +++ */
+
+ res = recvmsg_check_msg(env, descP, read, msgHdrP,
+ dataBufP, ctrlBufP, sockRef);
+
+ }
+
+ return res;
+
+}
+
+
+/* *** recvmsg_check_msg ***
+ *
+ * We successfully read one message. Time to process.
+ */
+static
+ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM eMsg;
+
+ /*
+ * <KOLLA>
+ *
+ * The return value of recvmsg is the *total* number of bytes
+ * that where successfully read. This data has been put into
+ * the *IO vector*.
+ *
+ * </KOLLA>
+ */
+
+ encode_msg(env, descP,
+ read, msgHdrP, dataBufP, ctrlBufP,
+ &eMsg);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recvmsg_check_result(%T) {%d} -> ok\r\n",
+ sockRef, descP->sock) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef, esock_atom_read_byte,
+ &descP->readByteCnt, read);
+
+ recv_update_current_reader(env, descP, sockRef);
+
+ return esock_make_ok2(env, eMsg);
+}
+
+
+
+/* ========================================================================
+ */
+extern
+ERL_NIF_TERM essio_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ if (! IS_OPEN(descP->readState)) {
+ /* A bit of cheeting; maybe not closed yet - do we need a queue? */
+ return esock_make_error_closed(env);
+ }
+
+ /* Store the PID of the caller,
+ * since we need to inform it when we
+ * (that is, the stop callback function)
+ * completes.
+ */
+ ESOCK_ASSERT( enif_self(env, &descP->closerPid) != NULL );
+
+ /* If the caller is not the owner; monitor the caller,
+ * since we should complete this operation even if the caller dies
+ * (for whatever reason).
+ */
+ if (COMPARE_PIDS(&descP->closerPid, &descP->ctrlPid) != 0) {
+
+ ESOCK_ASSERT( MONP("essio_close-check -> closer",
+ env, descP,
+ &descP->closerPid,
+ &descP->closerMon) == 0 );
+ }
+
+ /* Prepare for closing the socket */
+ descP->readState |= ESOCK_STATE_CLOSING;
+ descP->writeState |= ESOCK_STATE_CLOSING;
+ if (do_stop(env, descP)) {
+ // stop() has been scheduled - wait for it
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_close {%d} -> stop was scheduled\r\n",
+ descP->sock) );
+
+ // Create closeRef for the close msg that esock_stop() will send
+ descP->closeEnv = esock_alloc_env("esock_close_do - close-env");
+ descP->closeRef = MKREF(descP->closeEnv);
+
+ return esock_make_ok2(env, CP_TERM(env, descP->closeRef));
+ } else {
+ // The socket may be closed - tell caller to finalize
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_close {%d} -> stop was called\r\n",
+ descP->sock) );
+
+ return esock_atom_ok;
+ }
+}
+
+
+
+/* Prepare for close - return whether stop is scheduled or not
+ */
+static
+BOOLEAN_T do_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ BOOLEAN_T ret;
+ int sres;
+ ERL_NIF_TERM sockRef;
+
+ sockRef = enif_make_resource(env, descP);
+
+ if (IS_SELECTED(descP)) {
+ ESOCK_ASSERT( (sres = esock_select_stop(env,
+ (ErlNifEvent) descP->sock,
+ descP))
+ >= 0 );
+ if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) {
+ /* The socket is no longer known by the select machinery
+ * - it may be closed
+ */
+ ret = FALSE;
+ } else {
+ ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 );
+ /* esock_stop() is scheduled
+ * - socket may be removed by esock_stop() or later
+ */
+ ret = TRUE;
+ }
+ } else {
+ sres = 0;
+ /* The socket has never been used in the select machinery
+ * - it may be closed
+ */
+ ret = FALSE;
+ }
+
+ /* +++++++ Current and waiting Writers +++++++ */
+
+ if (descP->currentWriterP != NULL) {
+
+ /* We have a current Writer; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
+
+ /* The current Writer will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "writer",
+ descP, sockRef, &descP->currentWriter);
+ }
+
+ /* Inform the waiting Writers (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting writer(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "writer",
+ descP, sockRef, &descP->writersQ,
+ esock_atom_closed);
+
+ descP->currentWriterP = NULL;
+ }
+
+ /* +++++++ Connector +++++++
+ * Note that there should not be Writers and a Connector
+ * at the same time so the check for if the
+ * current Writer/Connecter was deselected is only correct
+ * under that assumption
+ */
+
+ if (descP->connectorP != NULL) {
+
+ /* We have a Connector; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
+
+ /* The Connector will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "connector",
+ descP, sockRef, &descP->connector);
+ }
+
+ descP->connectorP = NULL;
+ }
+
+ /* +++++++ Current and waiting Readers +++++++ */
+
+ if (descP->currentReaderP != NULL) {
+
+ /* We have a current Reader; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
+
+ /* The current Reader will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "reader",
+ descP, sockRef, &descP->currentReader);
+ }
+
+ /* Inform the Readers (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting reader(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "writer",
+ descP, sockRef, &descP->readersQ,
+ esock_atom_closed);
+
+ descP->currentReaderP = NULL;
+ }
+
+ /* +++++++ Current and waiting Acceptors +++++++
+ *
+ * Note that there should not be Readers and Acceptors
+ * at the same time so the check for if the
+ * current Reader/Acceptor was deselected is only correct
+ * under that assumption
+ */
+
+ if (descP->currentAcceptorP != NULL) {
+
+ /* We have a current Acceptor; was it deselected?
+ */
+
+ if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
+
+ /* The current Acceptor will not get a select message
+ * - send it an abort message
+ */
+
+ esock_stop_handle_current(env,
+ "acceptor",
+ descP, sockRef, &descP->currentAcceptor);
+ }
+
+ /* Inform the waiting Acceptor (in the same way) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "do_stop {%d} -> handle waiting acceptors(s)\r\n",
+ descP->sock) );
+
+ esock_inform_waiting_procs(env, "acceptor",
+ descP, sockRef, &descP->acceptorsQ,
+ esock_atom_closed);
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ return ret;
+}
+
+
+
+/* ========================================================================
+ * Perform the final step in the socket close.
+ */
+extern
+ERL_NIF_TERM essio_fin_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ int err;
+ ErlNifPid self;
+#ifdef HAVE_SENDFILE
+ HANDLE sendfileHandle;
+#endif
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (IS_CLOSED(descP->readState))
+ return esock_make_error_closed(env);
+
+ if (! IS_CLOSING(descP->readState)) {
+ // esock_close() has not been called
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ if (IS_SELECTED(descP) && (descP->closeEnv != NULL)) {
+ // esock_stop() is scheduled but has not been called
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ if (COMPARE_PIDS(&descP->closerPid, &self) != 0) {
+ // This process is not the closer
+ return esock_raise_invalid(env, esock_atom_state);
+ }
+
+ // Close the socket
+
+ /* Stop monitoring the closer.
+ * Demonitoring may fail since this is a dirty NIF
+ * - the caller may have died already.
+ */
+ enif_set_pid_undefined(&descP->closerPid);
+ if (descP->closerMon.isActive) {
+ (void) DEMONP("essio_fin_close -> closer",
+ env, descP, &descP->closerMon);
+ }
+
+ /* Stop monitoring the owner */
+ enif_set_pid_undefined(&descP->ctrlPid);
+ (void) DEMONP("essio_fin_close -> ctrl",
+ env, descP, &descP->ctrlMon);
+ /* Not impossible to still get a esock_down() call from a
+ * just triggered owner monitor down
+ */
+
+#ifdef HAVE_SENDFILE
+ sendfileHandle = descP->sendfileHandle;
+ descP->sendfileHandle = INVALID_HANDLE;
+#endif
+
+ /* This nif-function is executed in a dirty scheduler just so
+ * that it can "hang" (with minimum effect on the VM) while the
+ * kernel writes our buffers. IF we have set the linger option
+ * for this ({true, integer() > 0}). For this to work we must
+ * be blocking...
+ */
+ SET_BLOCKING(descP->sock);
+ err = esock_close_socket(env, descP, TRUE);
+
+#ifdef HAVE_SENDFILE
+ if (sendfileHandle != INVALID_HANDLE) {
+ (void) close(descP->sendfileHandle);
+ }
+#endif
+
+ if (err != 0) {
+ if (err == ERRNO_BLOCK) {
+ /* Not all data in the buffers where sent,
+ * make sure the caller gets this.
+ */
+ return esock_make_error(env, esock_atom_timeout);
+ } else {
+ return esock_make_error_errno(env, err);
+ }
+ }
+
+ return esock_atom_ok;
+}
+
+
+/* ========================================================================
+ * *** essio_shutdown should go here - if we need one ***
+ */
+
+
+/* ========================================================================
+ * *** essio_sockname should go here - if we need one ***
+ */
+
+
+/* ========================================================================
+ * *** essio_peername should go here - if we need one ***
+ */
+
+
+/* ========================================================================
+ * Cancel a connect request.
+ */
+
+extern
+ERL_NIF_TERM essio_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+ ErlNifPid self;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_connect {%d} -> entry with"
+ "\r\n writeState: 0x%X"
+ "\r\n opRef: %T"
+ "\r\n",
+ descP->sock, descP->writeState, opRef) );
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if ((descP->connectorP == NULL) ||
+ (COMPARE_PIDS(&self, &descP->connector.pid) != 0) ||
+ (COMPARE(opRef, descP->connector.ref) != 0)) {
+
+ res = esock_make_error(env, esock_atom_not_found);
+
+ } else {
+
+ res = esock_cancel_write_select(env, descP, opRef);
+ esock_requestor_release("esock_cancel_connect",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_connect {%d} -> done when"
+ "\r\n res: %T"
+ "\r\n",
+ descP->sock, descP->writeState,
+ opRef, res) );
+
+ return res;
+}
+
+
+
+/* ========================================================================
+ * Cancel accept request
+ *
+ * We have two different cases:
+ * *) Its the current acceptor
+ * Cancel the select!
+ * We need to activate one of the waiting acceptors.
+ * *) Its one of the acceptors ("waiting") in the queue
+ * Simply remove the acceptor from the queue.
+ *
+ */
+extern
+ERL_NIF_TERM essio_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept(%T), {%d,0x%X} ->"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->readState,
+ opRef,
+ ((descP->currentAcceptorP == NULL)
+ ? "without acceptor" : "with acceptor")) );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentAcceptorP == NULL) {
+
+ res = esock_atom_not_found;
+
+ } else {
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) {
+ if (COMPARE(opRef, descP->currentAcceptor.ref) == 0)
+ res = essio_cancel_accept_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_accept_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_accept(%T) -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, res) );
+
+ return res;
+}
+
+
+/* The current acceptor process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the acceptor queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_accept_current -> current acceptor",
+ env, descP, &descP->currentAcceptor.mon) == 0);
+ MON_INIT(&descP->currentAcceptor.mon);
+ res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_accept_current(%T) {%d} -> "
+ "no more acceptors\r\n",
+ sockRef, descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ return res;
+}
+
+
+/* These processes have not performed a select, so we can simply
+ * remove them from the acceptor queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (acceptor) queue */
+
+ if (esock_acceptor_unqueue(env, descP, &opRef, selfP)) {
+ return esock_atom_ok;
+ } else {
+ return esock_atom_not_found;
+ }
+}
+
+
+
+/* ========================================================================
+ * Cancel send request
+ *
+ * Cancel a send operation.
+ * Its either the current writer or one of the waiting writers.
+ */
+
+extern
+ERL_NIF_TERM essio_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_send(%T), {%d,0x%X} -> entry with"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->writeState,
+ opRef,
+ ((descP->currentWriterP == NULL)
+ ? "without writer" : "with writer")) );
+
+ if (! IS_OPEN(descP->writeState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentWriterP == NULL) {
+
+ res = esock_atom_not_found;
+
+ } else {
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) {
+ if (COMPARE(opRef, descP->currentWriter.ref) == 0)
+ res = essio_cancel_send_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_send_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_send(%T) {%d} -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ return res;
+}
+
+
+
+/* The current writer process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the writer queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_send_current -> current writer",
+ env, descP, &descP->currentWriter.mon) == 0);
+ res = esock_cancel_write_select(env, descP, descP->currentWriter.ref);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_send_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_send_current(%T) {%d} -> no more writers"
+ "\r\n", sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ return res;
+}
+
+
+
+/* These processes have not performed a select, so we can simply
+ * remove them from the writer queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (writer) queue */
+
+ if (esock_writer_unqueue(env, descP, &opRef, selfP)) {
+ return esock_atom_ok;
+ } else {
+ return esock_atom_not_found;
+ }
+}
+
+
+
+/* ========================================================================
+ * Cancel receive request
+ *
+ * Cancel a read operation.
+ * Its either the current reader or one of the waiting readers.
+ */
+extern
+ERL_NIF_TERM essio_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
+{
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_recv(%T), {%d,0x%X} -> entry with"
+ "\r\n opRef: %T"
+ "\r\n %s"
+ "\r\n",
+ sockRef, descP->sock, descP->readState,
+ opRef,
+ ((descP->currentReaderP == NULL)
+ ? "without reader" : "with reader")) );
+
+ if (! IS_OPEN(descP->readState)) {
+
+ res = esock_make_error_closed(env);
+
+ } else if (descP->currentReaderP == NULL) {
+
+ res = esock_atom_not_found;
+
+ } else {
+ ErlNifPid self;
+
+ ESOCK_ASSERT( enif_self(env, &self) != NULL );
+
+ if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) {
+ if (COMPARE(opRef, descP->currentReader.ref) == 0)
+ res = essio_cancel_recv_current(env, descP, sockRef);
+ else
+ res = esock_atom_not_found;
+ } else {
+ res = essio_cancel_recv_waiting(env, descP, opRef, &self);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_recv(%T) {%d} -> done with result:"
+ "\r\n %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+
+ return res;
+
+}
+
+
+/* The current reader process has an ongoing select we first must
+ * cancel. Then we must re-activate the "first" (the first
+ * in the reader queue).
+ */
+static
+ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM res;
+
+ ESOCK_ASSERT( DEMONP("essio_cancel_recv_current -> current reader",
+ env, descP, &descP->currentReader.mon) == 0);
+ res = esock_cancel_read_select(env, descP, descP->currentReader.ref);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_cancel_recv_current(%T) {%d} -> cancel res: %T"
+ "\r\n", sockRef, descP->sock, res) );
+
+ if (!esock_activate_next_reader(env, descP, sockRef)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_cancel_recv_current(%T) {%d} -> no more readers"
+ "\r\n", sockRef, descP->sock) );
+
+ descP->currentReaderP = NULL;
+ }
+
+ return res;
+}
+
+
+/* These processes have not performed a select, so we can simply
+ * remove them from the reader queue.
+ */
+static
+ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ const ErlNifPid* selfP)
+{
+ /* unqueue request from (reader) queue */
+
+ if (esock_reader_unqueue(env, descP, &opRef, selfP)) {
+ return esock_atom_ok;
+ } else {
+ return esock_atom_not_found;
+ }
+}
+
+
+
+/* ========================================================================
+ * IOCTL with two args (socket and request "key")
+ *
+ */
+extern
+ERL_NIF_TERM essio_ioctl2(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req)
+{
+ switch (req) {
+
+#if defined(SIOCGIFCONF)
+ case SIOCGIFCONF:
+ return essio_ioctl_gifconf(env, descP);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
+/* ========================================================================
+ * IOCTL with three args (socket, request "key" and one argument)
+ *
+ * The type and value of 'arg' depend on the request,
+ * which we have not yet "analyzed".
+ *
+ * Request arg arg type
+ * ------- ------- --------
+ * gifname ifindex integer
+ * gifindex name string
+ * gifflags name string
+ * gifaddr name string
+ * gifdstaddr name string
+ * gifbdraddr name string
+ * gifnetmask name string
+ * gifmtu name string
+ * gifhwaddr name string
+ * gifmap name string
+ * giftxqlen name string
+ */
+extern
+ERL_NIF_TERM essio_ioctl3(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM arg)
+{
+ /* This for *get* requests */
+
+ switch (req) {
+
+#if defined(SIOCGIFNAME)
+ case SIOCGIFNAME:
+ return essio_ioctl_gifname(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFINDEX)
+ case SIOCGIFINDEX:
+ return essio_ioctl_gifindex(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFFLAGS)
+ case SIOCGIFFLAGS:
+ return essio_ioctl_gifflags(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFADDR)
+ case SIOCGIFADDR:
+ return essio_ioctl_gifaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFDSTADDR)
+ case SIOCGIFDSTADDR:
+ return essio_ioctl_gifdstaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFBRDADDR)
+ case SIOCGIFBRDADDR:
+ return essio_ioctl_gifbrdaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFNETMASK)
+ case SIOCGIFNETMASK:
+ return essio_ioctl_gifnetmask(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFMTU)
+ case SIOCGIFMTU:
+ return essio_ioctl_gifmtu(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+ case SIOCGIFHWADDR:
+ return essio_ioctl_gifhwaddr(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+ case SIOCGIFMAP:
+ return essio_ioctl_gifmap(env, descP, arg);
+ break;
+#endif
+
+#if defined(SIOCGIFTXQLEN)
+ case SIOCGIFTXQLEN:
+ return essio_ioctl_giftxqlen(env, descP, arg);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
+/* ========================================================================
+ * IOCTL with four args (socket, request "key" and two arguments)
+ *
+ * The type and value of arg(s) depend on the request,
+ * which we have not yet "analyzed".
+ *
+ * Request arg1 arg1 type arg2 arg2 type
+ * ------- ------- --------- ------ ---------
+ * sifflags name string Flags #{IntFlag := boolean()}
+ * IntFlag is the native flag
+ * sifaddr name string Addr sockaddr()
+ * sifdstaddr name string DstAddr sockaddr()
+ * sifbrdaddr name string BrdAddr sockaddr()
+ * sifnetmask name string NetMask sockaddr()
+ * gifmtu name string MTU integer()
+ * sifhwaddr name string HwAddr sockaddr()
+ * giftxqlen name string Len integer()
+ */
+extern
+ERL_NIF_TERM essio_ioctl4(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ unsigned long req,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eval)
+{
+
+ switch (req) {
+
+#if defined(SIOCSIFFLAGS)
+ case SIOCSIFFLAGS:
+ return essio_ioctl_sifflags(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFADDR)
+ case SIOCSIFADDR:
+ return essio_ioctl_sifaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFDSTADDR)
+ case SIOCSIFDSTADDR:
+ return essio_ioctl_sifdstaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFBRDADDR)
+ case SIOCSIFBRDADDR:
+ return essio_ioctl_sifbrdaddr(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFNETMASK)
+ case SIOCSIFNETMASK:
+ return essio_ioctl_sifnetmask(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFMTU)
+ case SIOCSIFMTU:
+ return essio_ioctl_sifmtu(env, descP, ename, eval);
+ break;
+#endif
+
+#if defined(SIOCSIFTXQLEN)
+ case SIOCSIFTXQLEN:
+ return essio_ioctl_siftxqlen(env, descP, ename, eval);
+ break;
+#endif
+
+ default:
+ return esock_make_error(env, esock_atom_enotsup);
+ break;
+ }
+
+}
+
+
+
+/* ===========================================================================
+ * The implemented (ioctl) get requests falls into three grops:
+ *
+ * 1) gifconf - Takes no argument other then the request
+ * 2) gifname - Takes the interface index (integer) as an argument
+ * 3) other - All other (get) requests takes the interface name (string)
+ * as the argument.
+ *
+ * The functions defined using the macros below are all in the third (3)
+ * group.
+ *
+ */
+
+/* *** essio_ioctl_gifindex *** */
+#if defined(SIOCGIFINDEX)
+#if defined(ESOCK_USE_IFINDEX)
+#define IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_ifindex)
+#elif defined(ESOCK_USE_INDEX)
+#define IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_index)
+#else
+#define IOCTL_GIFINDEX_FUNC_DECL
+#endif
+#else
+#define IOCTL_GIFINDEX_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifflags *** */
+#if defined(SIOCGIFFLAGS)
+#define IOCTL_GIFFLAGS_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifflags, SIOCGIFFLAGS, flags, ifreq.ifr_flags)
+#else
+#define IOCTL_GIFFLAGS_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifaddr *** */
+#if defined(SIOCGIFADDR)
+#define IOCTL_GIFADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifaddr, SIOCGIFADDR, ifraddr, &ifreq.ifr_addr)
+#else
+#define IOCTL_GIFADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifdstaddr *** */
+#if defined(SIOCGIFDSTADDR)
+#define IOCTL_GIFDSTADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifdstaddr, SIOCGIFDSTADDR, ifraddr, &ifreq.ifr_dstaddr)
+#else
+#define IOCTL_GIFDSTADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifbrdaddr *** */
+#if defined(SIOCGIFBRDADDR)
+#define IOCTL_GIFBRDADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifbrdaddr, SIOCGIFBRDADDR, ifraddr, &ifreq.ifr_broadaddr)
+#else
+#define IOCTL_GIFBRDADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifnetmask *** */
+#if defined(SIOCGIFNETMASK)
+#ifdef __linux__
+#define IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_netmask)
+#else
+#define IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_addr)
+#endif
+#else
+#define IOCTL_GIFNETMASK_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifmtu *** */
+#if defined(SIOCGIFMTU)
+#define IOCTL_GIFMTU_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifmtu, SIOCGIFMTU, ivalue, ifreq.ifr_mtu)
+#else
+#define IOCTL_GIFMTU_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifhwaddr *** */
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+#define IOCTL_GIFHWADDR_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifhwaddr, SIOCGIFHWADDR, hwaddr, &ifreq.ifr_hwaddr)
+#else
+#define IOCTL_GIFHWADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_gifmap *** */
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+#define IOCTL_GIFMAP_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(gifmap, SIOCGIFMAP, ifrmap, &ifreq.ifr_map)
+#else
+#define IOCTL_GIFMAP_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_giftxqlen *** */
+#if defined(SIOCGIFTXQLEN)
+#define IOCTL_GIFTXQLEN_FUNC_DECL \
+ IOCTL_GET_REQUEST_DECL(giftxqlen, SIOCGIFTXQLEN, ivalue, ifreq.ifr_qlen)
+#else
+#define IOCTL_GIFTXQLEN_FUNC_DECL
+#endif
+
+#define IOCTL_GET_FUNCS \
+ IOCTL_GIFINDEX_FUNC_DECL \
+ IOCTL_GIFFLAGS_FUNC_DECL \
+ IOCTL_GIFADDR_FUNC_DECL \
+ IOCTL_GIFDSTADDR_FUNC_DECL \
+ IOCTL_GIFBRDADDR_FUNC_DECL \
+ IOCTL_GIFNETMASK_FUNC_DECL \
+ IOCTL_GIFMTU_FUNC_DECL \
+ IOCTL_GIFHWADDR_FUNC_DECL \
+ IOCTL_GIFMAP_FUNC_DECL \
+ IOCTL_GIFTXQLEN_FUNC_DECL
+
+#define IOCTL_GET_REQUEST_DECL(OR, R, EF, UV) \
+ static \
+ ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename) \
+ { \
+ ERL_NIF_TERM result; \
+ struct ifreq ifreq; \
+ char* ifn = NULL; \
+ int nlen; \
+ \
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \
+ "\r\n (e)Name: %T" \
+ "\r\n", descP->sock, ename) ); \
+ \
+ if (!esock_decode_string(env, ename, &ifn)) \
+ return enif_make_badarg(env); \
+ \
+ nlen = esock_strnlen(ifn, IFNAMSIZ); \
+ \
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
+ sys_memcpy(ifreq.ifr_name, ifn, \
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", \
+ "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \
+ descP->sock) ); \
+ \
+ if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
+ int saveErrno = sock_errno(); \
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \
+ "\r\n reason: %T (%d)" \
+ "\r\n", descP->sock, reason, saveErrno) ); \
+ \
+ result = esock_make_error(env, reason); \
+ \
+ } else { \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> encode value\r\n", \
+ descP->sock) ); \
+ result = encode_ioctl_##EF(env, descP, UV); \
+ } \
+ \
+ FREE(ifn); \
+ \
+ return result; \
+ \
+ }
+IOCTL_GET_FUNCS
+#undef IOCTL_GET_FUNCS
+
+
+/* ===========================================================================
+ * The "rest" of the implemented (ioctl) get requests
+ *
+ * These (get) requests could not be 'generated' by the macros above.
+ */
+
+static
+ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ struct ifconf ifc;
+ int ifc_len = 0;
+ int buflen = 100 * sizeof(struct ifreq);
+ char *buf = MALLOC(buflen);
+ ERL_NIF_TERM result;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> entry\r\n", descP->sock) );
+
+ for (;;) {
+ ifc.ifc_len = buflen;
+ ifc.ifc_buf = buf;
+ if (ioctl(descP->sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+ int saveErrno = sock_errno();
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> failure: "
+ "\r\n errno: %d (%s)"
+ "\r\n", descP->sock, saveErrno, erl_errno_id(saveErrno)) );
+
+ if (saveErrno != EINVAL || ifc_len) {
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+ FREE(buf);
+ return esock_make_error(env, reason);
+ }
+ } else {
+ if (ifc.ifc_len == ifc_len) break; /* buf large enough */
+ ifc_len = ifc.ifc_len;
+ }
+ buflen += 10 * sizeof(struct ifreq);
+ buf = (char *) REALLOC(buf, buflen);
+ }
+
+ result = encode_ioctl_ifconf(env, descP, &ifc);
+
+ FREE(ifc.ifc_buf);
+
+ return result;
+}
+
+
+#if defined(SIOCGIFNAME)
+static
+ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eidx)
+{
+ ERL_NIF_TERM result;
+ struct ifreq ifreq;
+ int index;
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> entry with"
+ "\r\n (e)Index: %T"
+ "\r\n", descP->sock, eidx) );
+
+ if (!GET_INT(env, eidx, &index))
+ return enif_make_badarg(env);
+
+ ifreq.ifr_ifindex = index;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_ioctl_gifname {%d} -> try ioctl\r\n", descP->sock) );
+
+ if (ioctl(descP->sock, SIOCGIFNAME, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> failure: "
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+
+ result = esock_make_error(env, reason);
+
+ } else {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> encode name\r\n",
+ descP->sock) );
+
+ result = esock_make_ok2(env, encode_ioctl_ifreq_name(env, ifreq.ifr_name));
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> done with"
+ "\r\n result: %T"
+ "\r\n",
+ descP->sock, result) );
+
+ return result;
+
+}
+#endif
+
+
+
+
+/* ===========================================================================
+ * The implemented (ioctl) set requests:
+ *
+ */
+
+/* *** essio_ioctl_sifaddr *** */
+#if defined(SIOCSIFADDR)
+#define IOCTL_SIFADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifaddr, SIOCSIFADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_addr))
+#else
+#define IOCTL_SIFADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifdstaddr *** */
+#if defined(SIOCSIFDSTADDR)
+#define IOCTL_SIFDSTADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifdstaddr, SIOCSIFDSTADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_dstaddr))
+#else
+#define IOCTL_SIFDSTADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifbrdaddr *** */
+#if defined(SIOCSIFBRDADDR)
+#define IOCTL_SIFBRDADDR_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifbrdaddr, SIOCSIFBRDADDR, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_broadaddr))
+#else
+#define IOCTL_SIFBRDADDR_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifnetmask *** */
+#if defined(SIOCSIFNETMASK)
+#ifdef __linux__
+#define IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_netmask))
+#else
+#define IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \
+ ((ESockAddress*) &ifreq.ifr_addr))
+#endif
+#else
+#define IOCTL_SIFNETMASK_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_sifmtu ***
+ * On some platforms, MTU is an unsigned int
+ */
+#if defined(SIOCSIFMTU)
+#define IOCTL_SIFMTU_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(sifmtu, SIOCSIFMTU, mtu, (int*) &ifreq.ifr_mtu)
+#else
+#define IOCTL_SIFMTU_FUNC_DECL
+#endif
+
+/* *** essio_ioctl_siftxqlen *** */
+#if defined(SIOCSIFTXQLEN)
+#define IOCTL_SIFTXQLEN_FUNC_DECL \
+ IOCTL_SET_REQUEST_DECL(siftxqlen, SIOCSIFTXQLEN, txqlen, &ifreq.ifr_qlen)
+#else
+#define IOCTL_SIFTXQLEN_FUNC_DECL
+#endif
+
+#define IOCTL_SET_FUNCS \
+ IOCTL_SIFADDR_FUNC_DECL \
+ IOCTL_SIFDSTADDR_FUNC_DECL \
+ IOCTL_SIFBRDADDR_FUNC_DECL \
+ IOCTL_SIFNETMASK_FUNC_DECL \
+ IOCTL_SIFMTU_FUNC_DECL \
+ IOCTL_SIFTXQLEN_FUNC_DECL
+
+#define IOCTL_SET_REQUEST_DECL(OR, R, DF, UVP) \
+ static \
+ ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM ename, \
+ ERL_NIF_TERM evalue) \
+ { \
+ ERL_NIF_TERM result; \
+ struct ifreq ifreq; \
+ char* ifn = NULL; \
+ int nlen; \
+ \
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \
+ "\r\n (e)Name: %T" \
+ "\r\n (e)Value: %T" \
+ "\r\n", descP->sock, ename, evalue) ); \
+ \
+ if (!esock_decode_string(env, ename, &ifn)) { \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode name" \
+ "\r\n", descP->sock) ); \
+ \
+ return enif_make_badarg(env); \
+ } \
+ \
+ if (! decode_ioctl_##DF(env, descP, evalue, UVP)) { \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode addr" \
+ "\r\n", descP->sock) ); \
+ \
+ return esock_make_invalid(env, esock_atom_##DF); \
+ } \
+ \
+ nlen = esock_strnlen(ifn, IFNAMSIZ); \
+ \
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \
+ sys_memcpy(ifreq.ifr_name, ifn, \
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \
+ descP->sock) ); \
+ \
+ if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \
+ int saveErrno = sock_errno(); \
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \
+ \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \
+ "\r\n reason: %T (%d)" \
+ "\r\n", descP->sock, reason, saveErrno) ); \
+ \
+ result = esock_make_error(env, reason); \
+ \
+ } else { \
+ SSDBG( descP, \
+ ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> " \
+ "addr successfully set\r\n", \
+ descP->sock) ); \
+ result = esock_atom_ok; \
+ } \
+ \
+ FREE(ifn); \
+ \
+ return result; \
+ \
+ }
+
+IOCTL_SET_FUNCS
+#undef IOCTL_SET_FUNCS
+
+
+/* ===========================================================================
+ * The "rest" of the implemented (ioctl) set requests
+ *
+ * These (set) requests could not be 'generated' by the macros above.
+ */
+
+#if defined(SIOCSIFFLAGS)
+static
+ERL_NIF_TERM essio_ioctl_sifflags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ename,
+ ERL_NIF_TERM eflags)
+{
+ ERL_NIF_TERM result;
+ struct ifreq ifreq;
+ char* ifn = NULL;
+ int nlen;
+
+ SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> entry with"
+ "\r\n (e)Name: %T"
+ "\r\n (e)Flags: %T"
+ "\r\n", descP->sock, ename, eflags) );
+
+ if (!esock_decode_string(env, ename, &ifn)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failed decode name"
+ "\r\n", descP->sock) );
+
+ return enif_make_badarg(env);
+ }
+
+ // Make sure the length of the string is valid!
+ nlen = esock_strnlen(ifn, IFNAMSIZ);
+
+ sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); // Just in case
+ sys_memcpy(ifreq.ifr_name, ifn,
+ (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (get) ioctl\r\n",
+ descP->sock) );
+
+ if (ioctl(descP->sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> "
+ "failure: failed reading *current* flags"
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+
+ result = esock_make_error(env, reason);
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_ioctl_sifflags {%d} -> (local) update flags\r\n",
+ descP->sock) );
+
+ if (decode_ioctl_flags(env, descP, eflags, &ifreq.ifr_flags)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (set) ioctl\r\n",
+ descP->sock) );
+
+ if (ioctl(descP->sock, SIOCSIFFLAGS, (char *) &ifreq) < 0) {
+ int saveErrno = sock_errno();
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failure: "
+ "\r\n reason: %T (%d)"
+ "\r\n", descP->sock, reason, saveErrno) );
+
+ result = esock_make_error(env, reason);
+
+ } else {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> "
+ "updated flags successfully set\r\n",
+ descP->sock) );
+ result = esock_atom_ok;
+ }
+
+ /* We know that if esock_decode_string is successful,
+ * we have "some" form of string, and therefor memory
+ * has been allocated (and need to be freed)... */
+ FREE(ifn);
+
+ } else {
+ result = enif_make_badarg(env);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> done with result: "
+ "\r\n %T"
+ "\r\n",
+ descP->sock, result) );
+
+ return result;
+
+}
+#endif
+
+
+
+/* ===========================================================================
+ * ioctl utility functions
+ *
+ */
+
+static
+ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifconf* ifcP)
+{
+ ERL_NIF_TERM result;
+ unsigned int len = ((ifcP == NULL) ? 0 :
+ (ifcP->ifc_len / sizeof(struct ifreq)));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_ioctl_ifconf -> entry (when len = %d)\r\n", len) );
+
+ if (len > 0) {
+ ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
+ unsigned int i = 0;
+ struct ifreq* p = ifcP->ifc_req;
+
+ for (i = 0 ; i < len ; i++) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_ioctl_ifconf -> encode ifreq entry %d\r\n", i) );
+ array[i] = encode_ioctl_ifconf_ifreq(env, descP, &p[i]);
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf -> all entries encoded\r\n", i) );
+
+ result = esock_make_ok2(env, MKLA(env, array, len));
+ FREE(array);
+
+ } else {
+
+ result = esock_make_ok2(env, MKEL(env));
+
+ }
+
+ return result;
+}
+
+
+#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP)
+static
+ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifmap* mapP)
+{
+ ERL_NIF_TERM mapKeys[] = {esock_atom_mem_start,
+ esock_atom_mem_end,
+ esock_atom_base_addr,
+ esock_atom_irq,
+ esock_atom_dma,
+ esock_atom_port};
+ ERL_NIF_TERM mapVals[] = {MKUL(env, mapP->mem_start),
+ MKUL(env, mapP->mem_end),
+ MKUI(env, mapP->base_addr),
+ MKUI(env, mapP->irq),
+ MKUI(env, mapP->dma),
+ MKUI(env, mapP->port)};
+ unsigned int numMapKeys = NUM(mapKeys);
+ unsigned int numMapVals = NUM(mapVals);
+ ERL_NIF_TERM emap;
+
+ ESOCK_ASSERT( numMapVals == numMapKeys );
+ ESOCK_ASSERT( MKMA(env, mapKeys, mapVals, numMapKeys, &emap) );
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifrmap -> done with"
+ "\r\n Map: %T"
+ "\r\n", emap) );
+
+ return esock_make_ok2(env, emap);;
+}
+#endif
+
+
+#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR)
+static
+ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP)
+{
+ ERL_NIF_TERM eaddr;
+ SOCKLEN_T sz = sizeof(struct sockaddr);
+
+ esock_encode_hwsockaddr(env, addrP, sz, &eaddr);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with"
+ "\r\n Sock Addr: %T"
+ "\r\n", eaddr) );
+
+ return esock_make_ok2(env, eaddr);;
+}
+#endif
+
+
+static
+ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct sockaddr* addrP)
+{
+ ERL_NIF_TERM eaddr;
+
+ esock_encode_sockaddr(env, (ESockAddress*) addrP, -1, &eaddr);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with"
+ "\r\n Sock Addr: %T"
+ "\r\n", eaddr) );
+
+ return esock_make_ok2(env, eaddr);;
+}
+
+
+static
+ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ short flags)
+{
+ int i, flag, num = esock_ioctl_flags_length; // NUM(ioctl_flags);
+ ERL_NIF_TERM eflags, eflag;
+ SocketTArray ta = TARRAY_CREATE(20); // Just to be on the safe side
+
+ if (flags == 0) {
+ eflags = MKEL(env);
+ } else {
+ for (i = 0; (i < num) && (flags != 0); i++) {
+ flag = esock_ioctl_flags[i].flag;
+ if ((flag != 0) && ((flags & flag) == flag)) {
+ eflag = *(esock_ioctl_flags[i].name);
+ flags &= ~flag;
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> "
+ "\r\n i: %d"
+ "\r\n found flag: %T (%d)"
+ "\r\n remaining flags: %d"
+ "\r\n", descP->sock, i, eflag, flag, flags) );
+
+ TARRAY_ADD(ta, eflag);
+ }
+ }
+ if (flags != 0) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> unknown flag(s): %d"
+ "\r\n", descP->sock, flags) );
+
+ TARRAY_ADD(ta, MKI(env, flags));
+ }
+
+ TARRAY_TOLIST(ta, env, &eflags);
+ }
+
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags -> done with"
+ "\r\n Flags: %T (%d)"
+ "\r\n", eflags, flags) );
+
+ return esock_make_ok2(env, eflags);
+}
+
+
+static
+BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eaddr,
+ ESockAddress* addr)
+{
+ SOCKLEN_T addrLen;
+ BOOLEAN_T result;
+
+ result = esock_decode_sockaddr(env, eaddr, (ESockAddress*) addr, &addrLen);
+
+ VOID(addrLen);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_sockaddr {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+
+
+static
+BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM emtu,
+ int* mtu)
+{
+ BOOLEAN_T result;
+
+ if (! GET_INT(env, emtu, mtu)) {
+ result = FALSE;
+ } else {
+ result = TRUE;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_mtu {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+
+
+#if defined(SIOCSIFTXQLEN)
+static
+BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM etxqlen,
+ int* txqlen)
+{
+ return decode_ioctl_ivalue(env, descP, etxqlen, txqlen);
+}
+#endif
+
+/* All uses of the function should be added. For instance:
+ * #if defined(SIOCGIFTXQLEN) || defined(FOOBAR) || defined(YXA)
+ */
+#if defined(SIOCGIFTXQLEN)
+static
+BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eivalue,
+ int* ivalue)
+{
+ BOOLEAN_T result;
+
+ if (! GET_INT(env, eivalue, ivalue)) {
+ result = FALSE;
+ } else {
+ result = TRUE;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_ivalue {%d} -> decode result: %s"
+ "\r\n", descP->sock, B2S(result)) );
+
+ return result;
+}
+#endif
+
+
+static
+BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eflags,
+ short* flags)
+{
+ ERL_NIF_TERM key, value;
+ ErlNifMapIterator iter;
+ int tmpFlags = (int) *flags; // Current value
+ int flag;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> entry with"
+ "\r\n flags: %d"
+ "\r\n",
+ descP->sock, tmpFlags) );
+
+ enif_map_iterator_create(env, eflags, &iter, ERL_NIF_MAP_ITERATOR_FIRST);
+
+ while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
+
+ /* Convert key (eflag) to int */
+ if (! GET_INT(env, key, &flag)) {
+ enif_map_iterator_destroy(env, &iter);
+ return FALSE;
+ }
+
+ // Update flag
+ if (COMPARE(value, esock_atom_true) == 0) {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> set %d\r\n",
+ descP->sock, flag) );
+ tmpFlags |= flag;
+ } else {
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> reset %d\r\n",
+ descP->sock, flag) );
+ tmpFlags &= ~flag;
+ }
+
+ enif_map_iterator_next(env, &iter);
+ }
+
+ enif_map_iterator_destroy(env, &iter);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> done with"
+ "\r\n (new) flags: %d"
+ "\r\n",
+ descP->sock, tmpFlags) );
+
+ *flags = (short) tmpFlags;
+
+ return TRUE;
+}
+
+
+static
+ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int ivalue)
+{
+ ERL_NIF_TERM eivalue = MKI(env, ivalue);
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ivalue -> done with"
+ "\r\n iValue: %T (%d)"
+ "\r\n", eivalue, ivalue) );
+
+ return esock_make_ok2(env, eivalue);;
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ struct ifreq* ifrP)
+{
+ ERL_NIF_TERM ename, eaddr;
+
+ ESOCK_ASSERT( ifrP != NULL );
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode name\r\n") );
+ ename = encode_ioctl_ifreq_name(env, ifrP->ifr_name);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode sockaddr\r\n") );
+ eaddr = encode_ioctl_ifreq_sockaddr(env, &ifrP->ifr_addr);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> make ifreq map with"
+ "\r\n Name: %T"
+ "\r\n Sock Addr: %T"
+ "\r\n", ename, eaddr) );
+ return make_ifreq(env, ename, esock_atom_addr, eaddr);
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env,
+ char* name)
+{
+ return ((name == NULL) ? esock_atom_undefined : MKS(env, name));
+}
+
+static
+ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, struct sockaddr* sa)
+{
+ ERL_NIF_TERM esa;
+
+ if (sa != NULL) {
+
+ esock_encode_sockaddr(env, (ESockAddress*) sa, -1, &esa);
+
+ } else {
+
+ esa = esock_atom_undefined;
+
+ }
+
+ return esa;
+}
+
+
+/* The ifreq structure *always* contain a name
+ * and *one* other element. The second element
+ * depend on the ioctl request.
+ */
+static
+ERL_NIF_TERM make_ifreq(ErlNifEnv* env,
+ ERL_NIF_TERM name,
+ ERL_NIF_TERM key2,
+ ERL_NIF_TERM val2)
+{
+ ERL_NIF_TERM keys[2];
+ ERL_NIF_TERM vals[2];
+ ERL_NIF_TERM res;
+
+ keys[0] = esock_atom_name;
+ vals[0] = name;
+
+ keys[1] = key2;
+ vals[1] = val2;
+
+ ESOCK_ASSERT( MKMA(env, keys, vals, NUM(keys), &res) );
+
+ return res;
+}
+
+
+
+
+/* ----------------------------------------------------------------------
+ * U t i l i t y F u n c t i o n s
+ * ----------------------------------------------------------------------
+ */
+
+/* *** send_check_writer ***
+ *
+ * Checks if we have a current writer and if that is us.
+ * If not (current writer), then we must be made to wait
+ * for our turn. This is done by pushing us unto the writer queue.
+ */
+static
+BOOLEAN_T send_check_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ERL_NIF_TERM* checkResult)
+{
+ if (descP->currentWriterP != NULL) {
+ ErlNifPid caller;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (COMPARE_PIDS(&descP->currentWriter.pid, &caller) != 0) {
+ /* Not the "current writer", so (maybe) push onto queue */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_writer {%d} -> not (current) writer"
+ "\r\n ref: %T"
+ "\r\n", descP->sock, ref) );
+
+ if (! esock_writer_search4pid(env, descP, &caller)) {
+ esock_writer_push(env, descP, caller, ref, NULL);
+ *checkResult = esock_atom_select;
+ } else {
+ /* Writer already in queue */
+ *checkResult = esock_raise_invalid(env, esock_atom_state);
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_writer {%d} -> queue (push) result: %T\r\n"
+ "\r\n ref: %T"
+ "\r\n", descP->sock, *checkResult, ref) );
+
+ return FALSE;
+ }
+ }
+
+ // Does not actually matter in this case, but ...
+ *checkResult = esock_atom_ok;
+
+ return TRUE;
+}
+
+
+/* *** send_check_result ***
+ *
+ * Check the result of a socket send (send, sendto and sendmsg) call.
+ * If a "complete" send has been made, the next (waiting) writer will be
+ * scheduled (if there is one).
+ * If we did not manage to send the entire package, make another select,
+ * so that we can be informed when we can make another try (to send the rest),
+ * and return with the amount we actually managed to send (its up to the caller
+ * (that is the erlang code) to figure out hust much is left to send).
+ * If the write fail, we give up and return with the appropriate error code.
+ *
+ * What about the remaining writers!!
+ *
+ */
+static
+ERL_NIF_TERM send_check_result(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t send_result,
+ ssize_t dataSize,
+ BOOLEAN_T dataInTail,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef)
+{
+ ERL_NIF_TERM res;
+ BOOLEAN_T send_error;
+ int err;
+
+ send_error = ESOCK_IS_ERROR(send_result);
+ err = send_error ? sock_errno() : 0;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "send_check_result(%T) {%d} -> entry with"
+ "\r\n send_result: %ld"
+ "\r\n dataSize: %ld"
+ "\r\n err: %d"
+ "\r\n sendRef: %T"
+ "\r\n", sockRef, descP->sock,
+ (long) send_result, (long) dataSize, err, sendRef) );
+
+ if (send_error) {
+ /* Some kind of send failure - check what kind */
+ if ((err != EAGAIN) && (err != EINTR)) {
+ res = send_check_fail(env, descP, err, sockRef);
+ } else {
+ /* Ok, try again later */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_result(%T) {%d} -> try again"
+ "\r\n", sockRef, descP->sock) );
+
+ res = send_check_retry(env, descP, -1, sockRef, sendRef);
+ }
+ } else {
+ ssize_t written = send_result;
+ ESOCK_ASSERT( dataSize >= written );
+
+ if (written < dataSize) {
+ /* Not the entire package */
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_result(%T) {%d} -> "
+ "not entire package written (%d of %d)"
+ "\r\n", sockRef, descP->sock,
+ written, dataSize) );
+
+ res = send_check_retry(env, descP, written, sockRef, sendRef);
+ } else if (dataInTail) {
+ /* We sent all we could, but not everything (data in tail) */
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_result(%T) {%d} -> "
+ "not entire package written (%d but data in tail)"
+ "\r\n", sockRef, descP->sock,
+ written) );
+
+ res =
+ send_check_retry(env, descP, written, sockRef,
+ esock_atom_iov);
+ } else {
+ res = send_check_ok(env, descP, written, sockRef);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_result(%T) {%d} -> done:"
+ "\r\n res: %T"
+ "\r\n", sockRef, descP->sock,
+ res) );
+
+ return res;
+}
+
+
+/* *** send_check_ok ***
+ *
+ * Processing done upon successful send.
+ */
+static
+ERL_NIF_TERM send_check_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t written,
+ ERL_NIF_TERM sockRef)
+{
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_pkg, &descP->writePkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_byte, &descP->writeByteCnt, written);
+ descP->writePkgMaxCnt += written;
+ if (descP->writePkgMaxCnt > descP->writePkgMax)
+ descP->writePkgMax = descP->writePkgMaxCnt;
+ descP->writePkgMaxCnt = 0;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "send_check_ok(%T) {%d} -> "
+ "everything written (%ld) - done\r\n",
+ sockRef, descP->sock, written) );
+
+ if (descP->currentWriterP != NULL) {
+ ESOCK_ASSERT( DEMONP("send_check_ok -> current writer",
+ env, descP, &descP->currentWriter.mon) == 0);
+ }
+ /*
+ * Ok, this write is done maybe activate the next (if any)
+ */
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "send_check_ok(%T) {%d} -> no more writers\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ return esock_atom_ok;
+}
+
+
+/* *** send_check_fail ***
+ *
+ * Processing done upon failed send.
+ * An actual failure - we (and everyone waiting) give up.
+ */
+static
+ERL_NIF_TERM send_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM reason;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_fails, &descP->writeFails, 1);
+
+ reason = MKA(env, erl_errno_id(saveErrno));
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "send_check_fail(%T) {%d} -> error: %d (%T)\r\n",
+ sockRef, descP->sock, saveErrno, reason) );
+
+ if (saveErrno != EINVAL) {
+
+ /*
+ * We assume that anything other then einval (invalid input)
+ * is basically fatal (=> all waiting sends are aborted)
+ */
+
+ if (descP->currentWriterP != NULL) {
+
+ esock_requestor_release("send_check_fail",
+ env, descP, &descP->currentWriter);
+
+ send_error_waiting_writers(env, descP, sockRef, reason);
+
+ descP->currentWriterP = NULL;
+ }
+ }
+
+ return esock_make_error(env, reason);
+}
+
+
+/* *** send_error_waiting_writers ***
+ *
+ * Process all waiting writers when a fatal error has occurred.
+ * All waiting writers will be "aborted", that is a
+ * nif_abort message will be sent (with ref and reason).
+ */
+static
+void send_error_waiting_writers(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason)
+{
+ ESockRequestor req;
+
+ req.env = NULL; /* read by writer_pop before free */
+ while (esock_writer_pop(env, descP, &req)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_error_waiting_writers(%T) {%d} -> abort"
+ "\r\n pid: %T"
+ "\r\n reason: %T"
+ "\r\n",
+ sockRef, descP->sock, &req.pid, reason) );
+
+ esock_send_abort_msg(env, descP, sockRef, &req, reason);
+
+ (void) DEMONP("send_error_waiting_writers -> pop'ed writer",
+ env, descP, &req.mon);
+ }
+}
+
+
+/* *** send_check_retry ***
+ *
+ * Processing done upon incomplete or blocked send.
+ *
+ * We failed to write the *entire* packet (anything less
+ * then size of the packet, which is 0 <= written < sizeof
+ * packet, so schedule the rest for later.
+ */
+static
+ERL_NIF_TERM send_check_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t written,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef)
+{
+ int sres;
+ ERL_NIF_TERM res;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_retry(%T) {%d} -> %ld"
+ "\r\n", sockRef, descP->sock, (long) written) );
+
+ if (written >= 0) {
+ descP->writePkgMaxCnt += written;
+
+ if (descP->type != SOCK_STREAM) {
+ /* Partial write for packet oriented socket
+ * - done with packet
+ */
+ if (descP->writePkgMaxCnt > descP->writePkgMax)
+ descP->writePkgMax = descP->writePkgMaxCnt;
+ descP->writePkgMaxCnt = 0;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_pkg, &descP->writePkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_byte, &descP->writeByteCnt, written);
+
+ if (descP->currentWriterP != NULL) {
+ ESOCK_ASSERT( DEMONP("send_check_retry -> current writer",
+ env, descP,
+ &descP->currentWriter.mon) == 0);
+ }
+ /*
+ * Ok, this write is done maybe activate the next (if any)
+ */
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "send_check_retry(%T) {%d} -> no more writers\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ return esock_make_ok2(env, MKI64(env, written));
+ } /* else partial write for stream socket */
+ } /* else send would have blocked */
+
+ /* Register this process as current writer */
+
+ if (descP->currentWriterP == NULL) {
+ /* Register writer as current */
+
+ ESOCK_ASSERT( enif_self(env, &descP->currentWriter.pid) != NULL );
+ ESOCK_ASSERT( MONP("send_check_retry -> current writer",
+ env, descP,
+ &descP->currentWriter.pid,
+ &descP->currentWriter.mon) == 0 );
+ ESOCK_ASSERT( descP->currentWriter.env == NULL );
+
+ descP->currentWriter.env = esock_alloc_env("current-writer");
+ descP->currentWriter.ref =
+ CP_TERM(descP->currentWriter.env, sendRef);
+ descP->currentWriterP = &descP->currentWriter;
+ } else {
+ /* Overwrite current writer registration */
+ enif_clear_env(descP->currentWriter.env);
+ descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef);
+ }
+
+ if (COMPARE(sendRef, esock_atom_iov) == 0) {
+ ESOCK_ASSERT( written >= 0 );
+ /* IOV iteration - do not select */
+ return MKT2(env, esock_atom_iov, MKI64(env, written));
+ }
+
+ /* Select write for this process */
+
+ sres = esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef);
+
+ if (sres < 0) {
+ ERL_NIF_TERM reason;
+
+ /* Internal select error */
+ ESOCK_ASSERT( DEMONP("send_check_retry - select error",
+ env, descP, &descP->currentWriter.mon) == 0);
+
+ /* Fail all queued writers */
+ reason = MKT2(env, esock_atom_select_write, MKI(env, sres));
+ esock_requestor_release("send_check_retry - select error",
+ env, descP, &descP->currentWriter);
+ send_error_waiting_writers(env, descP, sockRef, reason);
+ descP->currentWriterP = NULL;
+
+ res =
+ enif_raise_exception(env,
+ MKT2(env, esock_atom_select_write,
+ MKI(env, sres)));
+
+ } else {
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_write_waits, &descP->writeWaits, 1);
+
+ descP->writeState |= ESOCK_STATE_SELECTED;
+
+ if (written >= 0) {
+ /* Partial write success */
+ res = MKT2(env, esock_atom_select, MKI64(env, written));
+ } else {
+ /* No write - try again */
+ res = esock_atom_select;
+ }
+ }
+
+ return res;
+}
+
+
+/* *** Control message utility functions *** */
+
+/* +++ decode_cmsghdrs +++
+ *
+ * Decode a list of cmsg(). There can be 0 or more "blocks".
+ *
+ * Each element can either be a (erlang) map that needs to be decoded,
+ * or a (erlang) binary that just needs to be appended to the control
+ * buffer.
+ *
+ * Our "problem" is that we have no idea how much memory we actually need.
+ *
+ */
+
+static
+BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* cmsgHdrBufP,
+ size_t cmsgHdrBufLen,
+ size_t* cmsgHdrBufUsed)
+{
+ ERL_NIF_TERM elem, tail, list;
+ char* bufP;
+ size_t rem, used, totUsed = 0;
+ unsigned int len;
+ int i;
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdrs {%d} -> entry with"
+ "\r\n eCMsg: %T"
+ "\r\n cmsgHdrBufP: 0x%lX"
+ "\r\n cmsgHdrBufLen: %d"
+ "\r\n", descP->sock,
+ eCMsg, cmsgHdrBufP, cmsgHdrBufLen) );
+
+ if (! GET_LIST_LEN(env, eCMsg, &len))
+ return FALSE;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdrs {%d} -> list length: %d\r\n",
+ descP->sock, len) );
+
+ for (i = 0, list = eCMsg, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP;
+ i < len; i++) {
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdrs {%d} -> process elem %d:"
+ "\r\n (buffer) rem: %u"
+ "\r\n (buffer) totUsed: %u"
+ "\r\n", descP->sock, i, rem, totUsed) );
+
+ /* Extract the (current) head of the (cmsg hdr) list */
+ if (! GET_LIST_ELEM(env, list, &elem, &tail))
+ return FALSE;
+
+ used = 0; // Just in case...
+ if (! decode_cmsghdr(env, descP, elem, bufP, rem, &used))
+ return FALSE;
+
+ bufP = CHARP( ULONG(bufP) + used );
+ rem = SZT( rem - used );
+ list = tail;
+ totUsed += used;
+
+ }
+
+ *cmsgHdrBufUsed = totUsed;
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdrs {%d} -> done"
+ "\r\n all %u ctrl headers processed"
+ "\r\n totUsed = %lu\r\n",
+ descP->sock, len, (unsigned long) totUsed) );
+
+ return TRUE;
+}
+
+
+/* +++ decode_cmsghdr +++
+ *
+ * Decode one cmsg(). Put the "result" into the buffer and advance the
+ * pointer (of the buffer) afterwards. Also update 'rem' accordingly.
+ * But before the actual decode, make sure that there is enough room in
+ * the buffer for the cmsg header (sizeof(*hdr) < rem).
+ *
+ * The eCMsg should be a map with three fields:
+ *
+ * level :: socket | protocol() | integer()
+ * type :: atom() | integer()
+ * What values are valid depend on the level
+ * data :: binary() | integer() | boolean()
+ * The type of the data depends on
+ * or level and type, but can be a binary,
+ * which means that the data is already coded.
+ * value :: term() Which is a term matching the decode function
+ */
+
+static
+BOOLEAN_T decode_cmsghdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eCMsg,
+ char* bufP,
+ size_t rem,
+ size_t* used)
+{
+ ERL_NIF_TERM eLevel, eType, eData, eValue;
+ int level;
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d} -> entry with"
+ "\r\n eCMsg: %T"
+ "\r\n", descP->sock, eCMsg) );
+
+ // Get 'level' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_level, &eLevel))
+ return FALSE;
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d} -> eLevel: %T"
+ "\r\n", descP->sock, eLevel) );
+
+ // Get 'type' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_type, &eType))
+ return FALSE;
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d} -> eType: %T"
+ "\r\n", descP->sock, eType) );
+
+ // Decode Level
+ if (! esock_decode_level(env, eLevel, &level))
+ return FALSE;
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d}-> level: %d\r\n",
+ descP->sock, level) );
+
+ // Get 'data' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_data, &eData)) {
+
+ // Get 'value' field
+ if (! GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue))
+ return FALSE;
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d} -> eValue: %T"
+ "\r\n", descP->sock, eValue) );
+
+ // Decode Value
+ if (! decode_cmsghdr_value(env, descP, level, eType, eValue,
+ bufP, rem, used))
+ return FALSE;
+
+ } else {
+
+ // Verify no 'value' field
+ if (GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue))
+ return FALSE;
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d} -> eData: %T"
+ "\r\n", descP->sock, eData) );
+
+ // Decode Data
+ if (! decode_cmsghdr_data(env, descP, level, eType, eData,
+ bufP, rem, used))
+ return FALSE;
+ }
+
+ SSDBG( descP, ("UNIX-ESSIO", "decode_cmsghdr {%d}-> used: %lu\r\n",
+ descP->sock, (unsigned long) *used) );
+
+ return TRUE;
+}
+
+
+static
+BOOLEAN_T decode_cmsghdr_value(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eValue,
+ char* bufP,
+ size_t rem,
+ size_t* usedP)
+{
+ int type;
+ struct cmsghdr* cmsgP = (struct cmsghdr *) bufP;
+ ESockCmsgSpec* cmsgTable;
+ ESockCmsgSpec* cmsgSpecP = NULL;
+ size_t num = 0;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_value {%d} -> entry \r\n"
+ " eType: %T\r\n"
+ " eValue: %T\r\n",
+ descP->sock, eType, eValue) );
+
+ // We have decode functions only for symbolic (atom) types
+ if (! IS_ATOM(env, eType)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " eType not an atom\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ /* Try to look up the symbolic type
+ */
+ if (((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) ||
+ ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL) ||
+ (cmsgSpecP->decode == NULL)) {
+ /* We found no table for this level,
+ * we found no symbolic type in the level table,
+ * or no decode function for this type
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " cmsgTable: %p\r\n"
+ " cmsgSpecP: %p\r\n",
+ descP->sock, cmsgTable, cmsgSpecP) );
+ return FALSE;
+ }
+
+ if (! cmsgSpecP->decode(env, eValue, cmsgP, rem, usedP)) {
+ // Decode function failed
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_value {%d} -> FALSE:\r\n"
+ " decode function failed\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ // Successful decode
+
+ type = cmsgSpecP->type;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_value {%d} -> TRUE:\r\n"
+ " level: %d\r\n"
+ " type: %d\r\n",
+ " *usedP: %lu\r\n",
+ descP->sock, level, type, (unsigned long) *usedP) );
+
+ cmsgP->cmsg_level = level;
+ cmsgP->cmsg_type = type;
+ return TRUE;
+}
+
+
+static
+BOOLEAN_T decode_cmsghdr_data(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eType,
+ ERL_NIF_TERM eData,
+ char* bufP,
+ size_t rem,
+ size_t* usedP)
+{
+ int type;
+ ErlNifBinary bin;
+ struct cmsghdr* cmsgP = (struct cmsghdr *) bufP;
+ ESockCmsgSpec* cmsgSpecP = NULL;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_data {%d} -> entry \r\n"
+ " eType: %T\r\n"
+ " eData: %T\r\n",
+ descP->sock, eType, eData) );
+
+ // Decode Type
+ if (! GET_INT(env, eType, &type)) {
+ ESockCmsgSpec* cmsgTable = NULL;
+ size_t num = 0;
+
+ /* Try to look up the symbolic (atom) type
+ */
+ if ((! IS_ATOM(env, eType)) ||
+ ((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) ||
+ ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL)) {
+ /* Type was not an atom,
+ * we found no table for this level,
+ * or we found no symbolic type in the level table
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_data {%d} -> FALSE:\r\n"
+ " cmsgTable: %p\r\n"
+ " cmsgSpecP: %p\r\n",
+ descP->sock, cmsgTable, cmsgSpecP) );
+ return FALSE;
+ }
+
+ type = cmsgSpecP->type;
+ }
+
+ // Decode Data
+ if (GET_BIN(env, eData, &bin)) {
+ void *p;
+
+ p = esock_init_cmsghdr(cmsgP, rem, bin.size, usedP);
+ if (p == NULL) {
+ /* No room for the data
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_data {%d} -> FALSE:\r\n"
+ " rem: %lu\r\n"
+ " bin.size: %lu\r\n",
+ descP->sock,
+ (unsigned long) rem,
+ (unsigned long) bin.size) );
+ return FALSE;
+ }
+
+ // Copy the binary data
+ sys_memcpy(p, bin.data, bin.size);
+
+ } else if ((! esock_cmsg_decode_int(env, eData, cmsgP, rem, usedP)) &&
+ (! esock_cmsg_decode_bool(env, eData, cmsgP, rem, usedP))) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_data {%d} -> FALSE\r\n",
+ descP->sock) );
+ return FALSE;
+ }
+
+ // Successful decode
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "decode_cmsghdr_data {%d} -> TRUE:\r\n"
+ " level: %d\r\n"
+ " type: %d\r\n"
+ " *usedP: %lu\r\n",
+ descP->sock, level, type, (unsigned long) *usedP) );
+
+ cmsgP->cmsg_level = level;
+ cmsgP->cmsg_type = type;
+ return TRUE;
+}
+
+
+/* +++ encode_msg +++
+ *
+ * Encode a msg() (recvmsg). In erlang its represented as
+ * a map, which has a specific set of attributes:
+ *
+ * addr (source address) - sockaddr()
+ * iov - [binary()]
+ * ctrl - [cmsg()]
+ * flags - msg_flags()
+ */
+
+static
+void encode_msg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ struct msghdr* msgHdrP,
+ ErlNifBinary* dataBufP,
+ ErlNifBinary* ctrlBufP,
+ ERL_NIF_TERM* eMsg)
+{
+ ERL_NIF_TERM addr, iov, ctrl, flags;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_msg {%d} -> entry with"
+ "\r\n read: %ld"
+ "\r\n", descP->sock, (long) read) );
+
+ /* The address is not used if we are connected (unless, maybe,
+ * family is 'local'), so check (length = 0) before we try to encodel
+ */
+ if (msgHdrP->msg_namelen != 0) {
+ esock_encode_sockaddr(env,
+ (ESockAddress*) msgHdrP->msg_name,
+ msgHdrP->msg_namelen,
+ &addr);
+ } else {
+ addr = esock_atom_undefined;
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_msg {%d} -> encode iov"
+ "\r\n msg_iovlen: %lu"
+ "\r\n",
+ descP->sock,
+ (unsigned long) msgHdrP->msg_iovlen) );
+
+ esock_encode_iov(env, read,
+ msgHdrP->msg_iov, msgHdrP->msg_iovlen, dataBufP,
+ &iov);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_msg {%d} -> try encode cmsgs\r\n",
+ descP->sock) );
+
+ encode_cmsgs(env, descP, ctrlBufP, msgHdrP, &ctrl);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_msg {%d} -> try encode flags\r\n",
+ descP->sock) );
+
+ esock_encode_msg_flags(env, descP, msgHdrP->msg_flags, &flags);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_msg {%d} -> components encoded:"
+ "\r\n addr: %T"
+ "\r\n ctrl: %T"
+ "\r\n flags: %T"
+ "\r\n", descP->sock, addr, ctrl, flags) );
+
+ {
+ ERL_NIF_TERM keys[] = {esock_atom_iov,
+ esock_atom_ctrl,
+ esock_atom_flags,
+ esock_atom_addr};
+ ERL_NIF_TERM vals[] = {iov, ctrl, flags, addr};
+ size_t numKeys = NUM(keys);
+
+ ESOCK_ASSERT( numKeys == NUM(vals) );
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_msg {%d} -> create map\r\n",
+ descP->sock) );
+
+ if (msgHdrP->msg_namelen == 0)
+ numKeys--; // No addr
+ ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eMsg) );
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "encode_msg {%d}-> map encoded\r\n",
+ descP->sock) );
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_msg {%d} -> done\r\n", descP->sock) );
+}
+
+
+
+/* +++ encode_cmsgs +++
+ *
+ * Encode a list of cmsg(). There can be 0 or more cmsghdr "blocks".
+ *
+ * Our "problem" is that we have no idea how many control messages
+ * we have.
+ *
+ * The cmsgHdrP arguments points to the start of the control data buffer,
+ * an actual binary. Its the only way to create sub-binaries. So, what we
+ * need to continue processing this is to turn that into an binary erlang
+ * term (which can then in turn be turned into sub-binaries).
+ *
+ * We need the cmsgBufP (even though cmsgHdrP points to it) to be able
+ * to create sub-binaries (one for each cmsg hdr).
+ *
+ * The TArray (term array) is created with the size of 128, which should
+ * be enough. But if its not, then it will be automatically realloc'ed during
+ * add. Once we are done adding hdr's to it, we convert the tarray to a list.
+ */
+
+static
+void encode_cmsgs(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ErlNifBinary* cmsgBinP,
+ struct msghdr* msgHdrP,
+ ERL_NIF_TERM* eCMsg)
+{
+ ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary
+ SocketTArray cmsghdrs = TARRAY_CREATE(128);
+ struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP);
+ struct cmsghdr* currentP;
+
+ SSDBG( descP, ("UNIX-ESSIO", "encode_cmsgs {%d} -> entry when"
+ "\r\n msg ctrl len: %d"
+ "\r\n (ctrl) firstP: 0x%lX"
+ "\r\n", descP->sock,
+ msgHdrP->msg_controllen, firstP) );
+
+ for (currentP = firstP;
+ /*
+ * In *old* versions of darwin, the CMSG_FIRSTHDR does not
+ * check the msg_controllen, so we do it here.
+ * We should really test this stuff during configure,
+ * but for now, this will have to do.
+ */
+#if defined(__DARWIN__)
+ (msgHdrP->msg_controllen >= sizeof(struct cmsghdr)) &&
+ (currentP != NULL);
+#else
+ (currentP != NULL);
+#endif
+ currentP = CMSG_NXTHDR(msgHdrP, currentP)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> process cmsg header when"
+ "\r\n TArray Size: %d"
+ "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
+
+ /* MUST check this since on Linux the returned "cmsg" may actually
+ * go too far!
+ */
+ if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) >
+ msgHdrP->msg_controllen) {
+
+ /* Ouch, fatal error - give up
+ * We assume we cannot trust any data if this is wrong.
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> check failed when: "
+ "\r\n currentP: 0x%lX"
+ "\r\n (current) cmsg_len: %d"
+ "\r\n firstP: 0x%lX"
+ "\r\n => %d"
+ "\r\n msg ctrl len: %d"
+ "\r\n", descP->sock,
+ CHARP(currentP), currentP->cmsg_len, CHARP(firstP),
+ (CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP),
+ msgHdrP->msg_controllen) );
+
+ TARRAY_ADD(cmsghdrs, esock_atom_bad_data);
+ break;
+
+ } else {
+ unsigned char* dataP = UCHARP(CMSG_DATA(currentP));
+ size_t dataPos = dataP - cmsgBinP->data;
+ size_t dataLen =
+ (UCHARP(currentP) + currentP->cmsg_len) - dataP;
+ ERL_NIF_TERM
+ cmsgHdr,
+ keys[] =
+ {esock_atom_level,
+ esock_atom_type,
+ esock_atom_data,
+ esock_atom_value},
+ vals[NUM(keys)];
+ size_t numKeys = NUM(keys);
+ BOOLEAN_T have_value;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> cmsg header data: "
+ "\r\n dataPos: %d"
+ "\r\n dataLen: %d"
+ "\r\n", descP->sock, dataPos, dataLen) );
+
+ vals[0] = esock_encode_level(env, currentP->cmsg_level);
+ vals[2] = MKSBIN(env, ctrlBuf, dataPos, dataLen);
+ have_value = esock_encode_cmsg(env,
+ currentP->cmsg_level,
+ currentP->cmsg_type,
+ dataP, dataLen, &vals[1], &vals[3]);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> "
+ "\r\n %T: %T"
+ "\r\n %T: %T"
+ "\r\n %T: %T"
+ "\r\n", descP->sock,
+ keys[0], vals[0], keys[1], vals[1], keys[2], vals[2]) );
+ if (have_value)
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> "
+ "\r\n %T: %T"
+ "\r\n", descP->sock, keys[3], vals[3]) );
+
+ /* Guard against cut-and-paste errors */
+ ESOCK_ASSERT( numKeys == NUM(vals) );
+ ESOCK_ASSERT( MKMA(env, keys, vals,
+ numKeys - (have_value ? 0 : 1), &cmsgHdr) );
+
+ /* And finally add it to the list... */
+ TARRAY_ADD(cmsghdrs, cmsgHdr);
+ }
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "encode_cmsgs {%d} -> cmsg headers processed when"
+ "\r\n TArray Size: %d"
+ "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
+
+ /* The tarray is populated - convert it to a list */
+ TARRAY_TOLIST(cmsghdrs, env, eCMsg);
+}
+
+
+
+/* *** Sendfile utility functions *** */
+
+/* Platform independent sendfile() function
+ *
+ * Return < 0 for terminal error
+ * 0 for done
+ * > 0 for retry with select
+ */
+#if defined(HAVE_SENDFILE)
+static
+int essio_sendfile(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ off_t offset,
+ size_t* countP,
+ int* errP)
+{
+ size_t pkgSize = 0; // Total sent in this call
+
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile {%d,%d} -> entry"
+ "\r\n sockRef: %T"
+ "\r\n",
+ descP->sock, descP->sendfileHandle, sockRef) );
+
+ for (;;) {
+ size_t chunk_size = (size_t) 0x20000000UL; // 0.5 GB
+ size_t bytes_sent;
+ ssize_t res;
+ int error;
+
+ /* *countP == 0 means send the whole file - use chunk size */
+ if ((*countP > 0) && (*countP < chunk_size))
+ chunk_size = *countP;
+
+ {
+ /* Platform dependent code:
+ * update and check offset, set and check bytes_sent, and
+ * set res to >= 0 and error to 0, or
+ * set res to < 0 and error to sock_errno()
+ */
+#if defined (__linux__)
+
+ off_t prev_offset;
+
+ prev_offset = offset;
+ res =
+ sendfile(descP->sock, descP->sendfileHandle,
+ &offset, chunk_size);
+ error = (res < 0) ? sock_errno() : 0;
+
+ ESOCK_ASSERT( offset >= prev_offset );
+ ESOCK_ASSERT( (off_t) chunk_size >= (offset - prev_offset) );
+ bytes_sent = (size_t) (offset - prev_offset);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_sendfile(%T) {%d,%d}"
+ "\r\n res: %d"
+ "\r\n bytes_sent: %lu"
+ "\r\n error: %d"
+ "\r\n",
+ sockRef, descP->sock, descP->sendfileHandle,
+ res, (unsigned long) bytes_sent, error) );
+
+#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__DARWIN__)
+
+ off_t sbytes;
+
+#if defined(__DARWIN__)
+ sbytes = (off_t) chunk_size;
+ res = (ssize_t)
+ sendfile(descP->sendfileHandle, descP->sock, offset,
+ &sbytes, NULL, 0);
+#else
+ sbytes = 0;
+ res = (ssize_t)
+ sendfile(descP->sendfileHandle, descP->sock, offset,
+ chunk_size, NULL, &sbytes, 0);
+#endif
+ error = (res < 0) ? sock_errno() : 0;
+
+ /* For an error return, we do not dare trust that sbytes is set
+ * unless the error is ERRNO_BLOCK or EINTR
+ * - the man page is to vague
+ */
+ if ((res < 0) && (error != ERRNO_BLOCK) && (error != EINTR)) {
+ sbytes = 0;
+ } else {
+ ESOCK_ASSERT( sbytes >= 0 );
+ ESOCK_ASSERT( (off_t) chunk_size >= sbytes );
+ ESOCK_ASSERT( offset + sbytes >= offset );
+ offset += sbytes;
+ }
+ bytes_sent = (size_t) sbytes;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_sendfile(%T) {%d,%d}"
+ "\r\n res: %d"
+ "\r\n bytes_sent: %lu"
+ "\r\n error: %d"
+ "\r\n",
+ sockRef, descP->sock, descP->sendfileHandle,
+ res, (unsigned long) bytes_sent, error) );
+
+#elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV)
+
+ sendfilevec_t sfvec[1];
+
+ sfvec[0].sfv_fd = descP->sendfileHandle;
+ sfvec[0].sfv_flag = 0;
+ sfvec[0].sfv_off = offset;
+ sfvec[0].sfv_len = chunk_size;
+
+ res = sendfilev(descP->sock, sfvec, NUM(sfvec), &bytes_sent);
+ error = (res < 0) ? sock_errno() : 0;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_sendfile(%T) {%d,%d}"
+ "\r\n res: %d"
+ "\r\n bytes_sent: %lu"
+ "\r\n error: %d"
+ "\r\n",
+ sockRef, descP->sock, descP->sendfileHandle,
+ res, (unsigned long) bytes_sent, error) );
+
+ if ((res < 0) && (error == EINVAL)) {
+ /* On e.b SunOS 5.10 using sfv_len > file size
+ * lands here - we regard this as a successful send.
+ * All other causes for EINVAL are avoided,
+ * except for .sfv_fd not seekable, which would
+ * give bytes_sent == 0 that we would interpret
+ * as end of file, which is kind of true.
+ */
+ res = 0;
+ }
+ ESOCK_ASSERT( chunk_size >= bytes_sent );
+ ESOCK_ASSERT( offset + bytes_sent >= offset );
+ offset += bytes_sent;
+
+#else
+#error "Unsupported sendfile syscall; update configure test."
+#endif
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile,
+ &descP->sendfileCountersP->cnt, 1);
+
+ if (bytes_sent != 0) {
+
+ pkgSize += bytes_sent;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile_pkg,
+ &descP->sendfileCountersP->pkg,
+ 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile_byte,
+ &descP->sendfileCountersP->byteCnt,
+ bytes_sent);
+
+ if (pkgSize > descP->sendfileCountersP->pkgMax)
+ descP->sendfileCountersP->pkgMax = pkgSize;
+ if ((descP->sendfileCountersP->maxCnt += bytes_sent)
+ > descP->sendfileCountersP->max)
+ descP->sendfileCountersP->max =
+ descP->sendfileCountersP->maxCnt;
+ }
+
+ /* *countP == 0 means send whole file */
+ if (*countP > 0) {
+
+ *countP -= bytes_sent;
+
+ if (*countP == 0) { // All sent
+ *countP = pkgSize;
+ return 0;
+ }
+ }
+
+ if (res < 0) {
+ if (error == ERRNO_BLOCK) {
+ *countP = pkgSize;
+ return 1;
+ }
+ if (error == EINTR)
+ continue;
+ *errP = error;
+ return -1;
+ }
+
+ if (bytes_sent == 0) { // End of input file
+ *countP = pkgSize;
+ return 0;
+ }
+ }
+ } // for (;;)
+}
+
+
+static
+ERL_NIF_TERM essio_sendfile_errno(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ int err)
+{
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(err));
+
+ return essio_sendfile_error(env, descP, sockRef, reason);
+}
+
+
+static
+ERL_NIF_TERM essio_sendfile_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason)
+{
+ SSDBG( descP, ("UNIX-ESSIO",
+ "essio_sendfile_error {%d} -> entry"
+ "\r\n sockRef: %T"
+ "\r\n reason: %T"
+ "\r\n", descP->sock, sockRef, reason) );
+
+ if (descP->sendfileCountersP == NULL) {
+ descP->sendfileCountersP = MALLOC(sizeof(ESockSendfileCounters));
+ *descP->sendfileCountersP = initESockSendfileCounters;
+ }
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile_fails,
+ &descP->sendfileCountersP->fails, 1);
+
+ /* XXX Should we have special treatment for EINVAL,
+ * such as to only fail current operation and activate
+ * the next from the queue?
+ */
+
+ if (descP->currentWriterP != NULL) {
+
+ (void) DEMONP("essio_sendfile_error",
+ env, descP, &descP->currentWriter.mon);
+
+ /* Fail all queued writers */
+ esock_requestor_release("essio_sendfile_error",
+ env, descP, &descP->currentWriter);
+ send_error_waiting_writers(env, descP, sockRef, reason);
+ descP->currentWriterP = NULL;
+
+ }
+
+ return esock_make_error(env, reason);
+}
+
+static
+ERL_NIF_TERM essio_sendfile_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ size_t count)
+{
+ int sres;
+
+ /* Select write for this process */
+ sres = esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef);
+ if (sres < 0) {
+ ERL_NIF_TERM reason;
+
+ /* Internal select error */
+ (void) DEMONP("essio_sendfile_select - failed",
+ env, descP, &descP->currentWriter.mon);
+
+ /* Fail all queued writers */
+ reason = MKT2(env, esock_atom_select_write, MKI(env, sres));
+ esock_requestor_release("essio_sendfile_select - failed",
+ env, descP, &descP->currentWriter);
+ send_error_waiting_writers(env, descP, sockRef, reason);
+ descP->currentWriterP = NULL;
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return enif_raise_exception(env, reason);
+
+ } else {
+ ErlNifUInt64 bytes_sent;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_sendfile_select {%d} -> selected"
+ "\r\n sockRef: %T"
+ "\r\n sendRef: %T"
+ "\r\n count: %lu"
+ "\r\n", descP->sock, sockRef, sendRef, (unsigned long) count) );
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_sendfile_waits,
+ &descP->sendfileCountersP->waits,
+ 1);
+
+ descP->writeState |= ESOCK_STATE_SELECTED;
+ bytes_sent = (ErlNifUInt64) count;
+
+ return MKT2(env, esock_atom_select, MKUI64(env, bytes_sent));
+ }
+}
+
+
+static
+ERL_NIF_TERM essio_sendfile_ok(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ size_t count)
+{
+ ErlNifUInt64 bytes_sent64u;
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_sendfile_ok {%d} -> entry when done"
+ "\r\n sockRef: %T"
+ "\r\n written: %lu"
+ "\r\n", descP->sock, sockRef, (unsigned long) count) );
+
+ if (descP->currentWriterP != NULL) {
+
+ (void) DEMONP("essio_sendfile_ok -> current writer",
+ env, descP, &descP->currentWriter.mon);
+
+ /*
+ * Ok, this write is done maybe activate the next (if any)
+ */
+ if (! esock_activate_next_writer(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_sendfile_ok {%d} -> no more writers"
+ "\r\n sockRef: %T"
+ "\r\n",
+ descP->sock, sockRef) );
+
+ descP->currentWriterP = NULL;
+ }
+ }
+
+ descP->writePkgMaxCnt = 0;
+ bytes_sent64u = (ErlNifUInt64) count;
+
+ (void) close(descP->sendfileHandle);
+ descP->sendfileHandle = INVALID_HANDLE;
+
+ return esock_make_ok2(env, MKUI64(env, bytes_sent64u));
+}
+
+#endif // #ifdef HAVE_SENDFILE
+
+
+/* ====================================================================
+ *
+ * NIF (I/O backend) Resource callback functions: dtor, stop and down
+ *
+ * ====================================================================
+ */
+
+extern
+void essio_dtor(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+ SGDBG( ("UNIX-ESSIO", "dtor -> entry\r\n") );
+
+ if (IS_SELECTED(descP)) {
+ /* We have used the socket in the select machinery,
+ * so we must have closed it properly to get here
+ */
+ ESOCK_ASSERT( IS_CLOSED(descP->readState) );
+ ESOCK_ASSERT( IS_CLOSED(descP->writeState) );
+ ESOCK_ASSERT( descP->sock == INVALID_SOCKET );
+ } else {
+ /* The socket is only opened, should be safe to close nonblocking */
+ (void) sock_close(descP->sock);
+ descP->sock = INVALID_SOCKET;
+ }
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> set state and pattern\r\n") );
+ descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
+ descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED);
+
+ esock_free_env("dtor reader", descP->currentReader.env);
+ descP->currentReader.env = NULL;
+
+ esock_free_env("dtor writer", descP->currentWriter.env);
+ descP->currentWriter.env = NULL;
+
+ esock_free_env("dtor acceptor", descP->currentAcceptor.env);
+ descP->currentAcceptor.env = NULL;
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free readers request queue\r\n") );
+ esock_free_request_queue(&descP->readersQ);
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free writers request queue\r\n") );
+ esock_free_request_queue(&descP->writersQ);
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> try free acceptors request queue\r\n") );
+ esock_free_request_queue(&descP->acceptorsQ);
+
+#ifdef HAVE_SENDFILE
+ ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE );
+ if (descP->sendfileCountersP != NULL) {
+ FREE(descP->sendfileCountersP);
+ descP->sendfileCountersP = NULL;
+ }
+#endif
+
+ esock_free_env("dtor close env", descP->closeEnv);
+ descP->closeEnv = NULL;
+
+ esock_free_env("dtor meta env", descP->meta.env);
+ descP->meta.env = NULL;
+
+ SGDBG( ("UNIX-ESSIO", "dtor -> done\r\n") );
+}
+
+
+extern
+void essio_stop(ErlNifEnv* env,
+ ESockDescriptor* descP)
+{
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileCountersP != NULL) {
+ ESockSendfileCounters* cntP = descP->sendfileCountersP;
+
+ SSDBG( descP, ("UNIX-ESSIO", "esock_stop(%d) -> sendfileCounters:"
+ "\r\n cnt: %lu"
+ "\r\n byteCnt: %lu"
+ "\r\n fails: %lu"
+ "\r\n max: %lu"
+ "\r\n pkg: %lu"
+ "\r\n pkgMax %lu"
+ "\r\n tries: %lu"
+ "\r\n waits: %lu"
+ "\r\n",
+ descP->sock,
+ (unsigned long) cntP->cnt,
+ (unsigned long) cntP->byteCnt,
+ (unsigned long) cntP->fails,
+ (unsigned long) cntP->max,
+ (unsigned long) cntP->pkg,
+ (unsigned long) cntP->pkgMax,
+ (unsigned long) cntP->tries,
+ (unsigned long) cntP->waits) );
+ }
+#endif
+
+ /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ *
+ * Inform waiting Closer, or close socket
+ *
+ * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ */
+
+ if (! enif_is_pid_undefined(&descP->closerPid)) {
+ /* We have a waiting closer process after nif_close()
+ * - send message to trigger nif_finalize_close()
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "esock_stop(%d) -> send close msg to %T\r\n",
+ descP->sock, MKPID(env, &descP->closerPid)) );
+
+ esock_send_close_msg(env, descP, &descP->closerPid);
+ /* Message send frees closeEnv */
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+
+ } else {
+ int err;
+
+ /* We do not have a closer process
+ * - have to do an unclean (non blocking) close */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] Failed closing socket without "
+ "closer process: "
+ "\r\n Controlling Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ descP->ctrlPid, descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ }
+
+}
+
+
+/* A 'down' has occured.
+ * Check the possible processes we monitor in turn:
+ * closer, controlling process (owner), connector, reader, acceptor and writer.
+ *
+ */
+extern
+void essio_down(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) {
+
+ /* The closer process went down
+ * - it will not call nif_finalize_close
+ */
+
+ enif_set_pid_undefined(&descP->closerPid);
+
+ if (MON_EQ(&descP->closerMon, monP)) {
+ MON_INIT(&descP->closerMon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> closer process exit\r\n",
+ descP->sock) );
+
+ } else {
+ // The owner is the closer so we used its monitor
+
+ ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) );
+ MON_INIT(&descP->ctrlMon);
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> closer controlling process exit\r\n",
+ descP->sock) );
+ }
+
+ /* Since the closer went down there was one,
+ * hence esock_close() must have run or scheduled esock_stop(),
+ * or the socket has never been "selected" upon.
+ */
+
+ if (descP->closeEnv == NULL) {
+ int err;
+
+ /* Since there is no closeEnv,
+ * esock_close() did not schedule esock_stop()
+ * and is about to call esock_finalize_close() but died,
+ * or esock_stop() has run, sent close_msg to the closer
+ * and cleared ->closeEnv but the closer died
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] "
+ "Failed closing socket for terminating "
+ "closer process: "
+ "\r\n Closer Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ } else {
+ /* Since there is a closeEnv esock_stop() has not run yet
+ * - when it finds that there is no closer process
+ * it will close the socket and ignore the close_msg
+ */
+ esock_clear_env("essio_down - close-env", descP->closeEnv);
+ esock_free_env("essio_down - close-env", descP->closeEnv);
+ descP->closeEnv = NULL;
+ descP->closeRef = esock_atom_undefined;
+ }
+
+ } else if (MON_EQ(&descP->ctrlMon, monP)) {
+ MON_INIT(&descP->ctrlMon);
+ /* The owner went down */
+ enif_set_pid_undefined(&descP->ctrlPid);
+
+ if (IS_OPEN(descP->readState)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> controller process exit"
+ "\r\n initiate close\r\n",
+ descP->sock) );
+
+ essio_down_ctrl(env, descP, pidP);
+
+ descP->readState |= ESOCK_STATE_CLOSING;
+ descP->writeState |= ESOCK_STATE_CLOSING;
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> controller process exit"
+ "\r\n already closed or closing\r\n",
+ descP->sock) );
+
+ }
+
+ } else if (descP->connectorP != NULL &&
+ MON_EQ(&descP->connector.mon, monP)) {
+ MON_INIT(&descP->connector.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down {%d} -> connector process exit\r\n",
+ descP->sock) );
+
+ /* connectorP is only set during connection.
+ * Forget all about the ongoing connection.
+ * We might end up connected, but the process that initiated
+ * the connection has died and will never know
+ */
+
+ esock_requestor_release("esock_down->connector",
+ env, descP, &descP->connector);
+ descP->connectorP = NULL;
+ descP->writeState &= ~ESOCK_STATE_CONNECTING;
+
+ } else {
+ ERL_NIF_TERM sockRef = enif_make_resource(env, descP);
+
+ /* check all operation queue(s): acceptor, writer and reader.
+ *
+ * Is it really any point in doing this if the socket is closed?
+ *
+ */
+
+ if (IS_CLOSED(descP->readState)) {
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down(%T) {%d} -> stray down: %T\r\n",
+ sockRef, descP->sock, pidP) );
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down(%T) {%d} -> other process term\r\n",
+ sockRef, descP->sock) );
+
+ if (descP->currentReaderP != NULL)
+ essio_down_reader(env, descP, sockRef, pidP, monP);
+ if (descP->currentAcceptorP != NULL)
+ essio_down_acceptor(env, descP, sockRef, pidP, monP);
+ if (descP->currentWriterP != NULL)
+ essio_down_writer(env, descP, sockRef, pidP, monP);
+ }
+ }
+
+}
+
+
+/* ==================================================================== */
+
+/* *** Recv/recvfrom/recvmsg utility functions *** */
+
+/* *** recv_check_reader ***
+ *
+ * Checks if we have a current reader and if that is us. If not,
+ * then we must be made to wait for our turn. This is done by pushing
+ * us unto the reader queue.
+ * Note that we do *not* actually initiate the currentReader structure
+ * here, since we do not actually know yet if we need to! We do that in
+ * the [recv|recvfrom|recvmsg]_check_result function.
+ */
+
+static
+BOOLEAN_T recv_check_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ERL_NIF_TERM* checkResult)
+{
+ if (descP->currentReaderP != NULL) {
+ ErlNifPid caller;
+
+ ESOCK_ASSERT( enif_self(env, &caller) != NULL );
+
+ if (COMPARE_PIDS(&descP->currentReader.pid, &caller) != 0) {
+ /* Not the "current reader", so (maybe) push onto queue */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_reader {%d} -> not (current) reader"
+ "\r\n ref: %T"
+ "\r\n", descP->sock, ref) );
+
+ if (! esock_reader_search4pid(env, descP, &caller)) {
+ if (COMPARE(ref, esock_atom_zero) == 0)
+ goto done_ok;
+ esock_reader_push(env, descP, caller, ref, NULL);
+ *checkResult = esock_atom_select;
+ } else {
+ /* Reader already in queue */
+ *checkResult = esock_raise_invalid(env, esock_atom_state);
+ }
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_reader {%d} -> queue (push) result: %T\r\n",
+ descP->sock, *checkResult) );
+
+ return FALSE;
+ }
+ }
+
+ done_ok:
+ // Does not actually matter in this case, but ...
+ *checkResult = esock_atom_ok;
+ return TRUE;
+}
+
+
+/* *** recv_check_full ***
+ *
+ * This function is called if we filled the allocated buffer.
+ * But are we done yet?
+ *
+ * toRead = 0 means: Give me everything you have => maybe
+ * toRead > 0 means: Yes
+ */
+
+static
+ERL_NIF_TERM recv_check_full(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+
+ if ((toRead == 0) &&
+ (descP->type == SOCK_STREAM)) {
+
+ /* +++ Give us everything you have got => *
+ * (maybe) needs to continue +++ */
+
+ /* Send up each chunk of data for each of the read
+ * and let the erlang code assemble it: {more, Bin}
+ * (when complete it should return {ok, Bin}).
+ * We need to read at least one more time to be sure if its
+ * done...
+ *
+ * Also, we need to check if the rNumCnt has reached its max (rNum),
+ * in which case we will assume the read to be done!
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_full(%T) {%d} -> shall we continue reading?"
+ "\r\n read: %ld"
+ "\r\n rNum: %u"
+ "\r\n rNumCnt: %u"
+ "\r\n", sockRef, descP->sock,
+ (unsigned long) read, descP->rNum, descP->rNumCnt) );
+
+ res = recv_check_full_maybe_done(env, descP, read, bufP,
+ sockRef, recvRef);
+
+ } else {
+
+ /* +++ We got exactly as much as we requested => We are done +++ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_full(%T) {%d} -> [%ld] "
+ "we got exactly what we could fit\r\n",
+ sockRef, descP->sock, (long) toRead) );
+
+ res = recv_check_full_done(env, descP, read, bufP, sockRef);
+
+ }
+
+ return res;
+
+}
+
+
+/* *** recv_check_full_maybe_done ***
+ *
+ * Send up each chunk of data for each of the read
+ * and let the erlang code assemble it: {more, Bin}
+ * (when complete it should return {ok, Bin}).
+ * We need to read at least one more time to be sure if its
+ * done...
+ *
+ * Also, we need to check if the rNumCnt has reached its max (rNum),
+ * in which case we will assume the read to be done!
+ */
+
+static
+ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+ descP->readPkgMaxCnt += read;
+
+ descP->rNumCnt++;
+ if (descP->rNumCnt >= descP->rNum) {
+
+ descP->rNumCnt = 0;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ if (descP->readPkgMaxCnt > descP->readPkgMax)
+ descP->readPkgMax = descP->readPkgMaxCnt;
+ descP->readPkgMaxCnt = 0;
+
+ recv_update_current_reader(env, descP, sockRef);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+
+ return esock_make_ok2(env, MKBIN(env, bufP));
+
+ }
+
+ /* Yes, we *do* need to continue reading */
+
+ recv_init_current_reader(env, descP, recvRef);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_full_maybe_done(%T) {%d} -> [%lu] "
+ "we are done for now - read more\r\n",
+ sockRef, descP->sock, (unsigned long)bufP->size) );
+
+ return MKT2(env, esock_atom_more, MKBIN(env, bufP));
+}
+
+
+
+/* *** recv_check_full_done ***
+ *
+ * A successful recv and we filled the buffer.
+ */
+
+static
+ERL_NIF_TERM recv_check_full_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM data;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ descP->readPkgMaxCnt += read;
+ if (descP->readPkgMaxCnt > descP->readPkgMax)
+ descP->readPkgMax = descP->readPkgMaxCnt;
+ descP->readPkgMaxCnt = 0;
+
+ recv_update_current_reader(env, descP, sockRef);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, bufP);
+
+ return esock_make_ok2(env, data);
+}
+
+
+
+/* *** recv_check_fail ***
+ *
+ * Handle recv failure.
+ */
+
+static
+ERL_NIF_TERM recv_check_fail(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ErlNifBinary* buf1P,
+ ErlNifBinary* buf2P,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+
+ FREE_BIN(buf1P);
+ if (buf2P != NULL) FREE_BIN(buf2P);
+
+ if (saveErrno == ECONNRESET) {
+
+ /* +++ Oops - closed +++ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_fail(%T) {%d} -> econnreset: closed"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, recvRef) );
+
+ // This is a bit overkill (to count here), but just in case...
+ ESOCK_CNT_INC(env, descP, sockRef, esock_atom_read_fails,
+ &descP->readFails, 1);
+
+ res = recv_check_fail_econnreset(env, descP, sockRef, recvRef);
+
+ } else if ((saveErrno == ERRNO_BLOCK) ||
+ (saveErrno == EAGAIN)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_fail(%T) {%d} -> eagain"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, recvRef) );
+
+ if (COMPARE(recvRef, esock_atom_zero) == 0)
+ res = esock_atom_ok;
+ else
+ res = recv_check_retry(env, descP, sockRef, recvRef);
+
+ } else {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_fail(%T) {%d} -> errno: %d\r\n"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, saveErrno, recvRef) );
+
+ ESOCK_CNT_INC(env, descP, sockRef, esock_atom_read_fails,
+ &descP->readFails, 1);
+
+ res = recv_check_fail_gen(env, descP, saveErrno, sockRef);
+ }
+
+ return res;
+}
+
+
+/* *** recv_check_fail_gen ***
+ *
+ * The recv call had a "general" failure.
+ */
+
+static
+ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int saveErrno,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
+
+ recv_error_current_reader(env, descP, sockRef, reason);
+
+ return esock_make_error(env, reason);
+}
+
+
+/* *** recv_check_fail_econnreset ***
+ *
+ * We detected that the socket was closed while reading.
+ * Inform current and waiting readers.
+ */
+
+static
+ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM reason = MKA(env, erl_errno_id(ECONNRESET));
+ ERL_NIF_TERM res = esock_make_error(env, reason);
+
+ /* <KOLLA>
+ *
+ * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING
+ * PROCESS, WE NEED TO INFORM IT!!!
+ *
+ * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!!
+ * HANDLED BY THE STOP (CALLBACK) FUNCTION?
+ *
+ * SINCE THIS IS A REMOTE CLOSE, WE DON'T NEED TO WAIT
+ * FOR OUTPUT TO BE WRITTEN (NO ONE WILL READ), JUST
+ * ABORT THE SOCKET REGARDLESS OF LINGER???
+ *
+ * </KOLLA>
+ */
+
+ recv_error_current_reader(env, descP, sockRef, reason);
+
+ return res;
+}
+
+
+/* *** recv_check_retry ***
+ *
+ * The recv call would have blocked, so retry.
+ */
+
+static
+ERL_NIF_TERM recv_check_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ int sres;
+ ERL_NIF_TERM res;
+
+ descP->rNumCnt = 0;
+ recv_init_current_reader(env, descP, recvRef);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_retry(%T) {%d} -> SELECT for more"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, recvRef) );
+
+ if ((sres = esock_select_read(env, descP->sock, descP, NULL,
+ sockRef, recvRef)) < 0) {
+ /* Unlikely that any next reader will have better luck,
+ * but why not give them a shot - the queue will be cleared
+ */
+ recv_update_current_reader(env, descP, sockRef);
+
+ res = enif_raise_exception(env,
+ MKT2(env, esock_atom_select_read,
+ MKI(env, sres)));
+ } else {
+ descP->readState |= ESOCK_STATE_SELECTED;
+ res = esock_atom_select;
+ }
+
+ return res;
+}
+
+
+
+/* *** recv_check_partial ***
+ *
+ * Handle a successful recv which only partly filled the specified buffer.
+ */
+
+static
+ERL_NIF_TERM recv_check_partial(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ssize_t toRead,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+
+ if ((toRead == 0) ||
+ (descP->type != SOCK_STREAM) ||
+ (COMPARE(recvRef, esock_atom_zero) == 0)) {
+
+ /* +++ We got it all, but since we +++
+ * +++ did not fill the buffer, we +++
+ * +++ must split it into a sub-binary. +++
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_partial(%T) {%d} -> [%ld] split buffer"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, (long) toRead,
+ recvRef) );
+
+ res = recv_check_partial_done(env, descP, read, bufP, sockRef);
+
+ } else {
+ /* A stream socket with specified read size
+ * and not a polling read, we got a partial read
+ * - return a select result to initiate a retry
+ */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_check_partial(%T) {%d} -> [%ld]"
+ " only part of message - expect more"
+ "\r\n recvRef: %T"
+ "\r\n", sockRef, descP->sock, (long) toRead,
+ recvRef) );
+
+ res = recv_check_partial_part(env, descP, read,
+ bufP, sockRef, recvRef);
+ }
+
+ return res;
+}
+
+
+/* *** recv_check_partial_done ***
+ *
+ * A successful but only partial recv, which fulfilled the required read.
+ */
+
+static
+ERL_NIF_TERM recv_check_partial_done(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef)
+{
+ ERL_NIF_TERM data;
+
+ descP->rNumCnt = 0;
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_pkg, &descP->readPkgCnt, 1);
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ descP->readPkgMaxCnt += read;
+ if (descP->readPkgMaxCnt > descP->readPkgMax)
+ descP->readPkgMax = descP->readPkgMaxCnt;
+ descP->readPkgMaxCnt = 0;
+
+ recv_update_current_reader(env, descP, sockRef);
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ data = MKBIN(env, bufP);
+ data = MKSBIN(env, data, 0, read);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recv_check_partial_done(%T) {%d} -> [%ld] done\r\n",
+ sockRef, descP->sock, (long) read) );
+
+ return esock_make_ok2(env, data);
+}
+
+
+/* *** recv_check_partial_part ***
+ *
+ * A successful but only partial recv, which only partly fulfilled
+ * the required read.
+ */
+
+static
+ERL_NIF_TERM recv_check_partial_part(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ssize_t read,
+ ErlNifBinary* bufP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef)
+{
+ ERL_NIF_TERM res;
+ int sres;
+
+ ESOCK_CNT_INC(env, descP, sockRef,
+ esock_atom_read_byte, &descP->readByteCnt, read);
+
+ recv_init_current_reader(env, descP, recvRef);
+
+ /* SELECT for more data */
+
+ sres = esock_select_read(env, descP->sock, descP, NULL,
+ sockRef, recvRef);
+ if (sres < 0) {
+ /* Unlikely that any next reader will have better luck,
+ * but why not give them a shot - the queue will be cleared
+ */
+ recv_update_current_reader(env, descP, sockRef);
+
+ res = enif_raise_exception(env,
+ MKT2(env, esock_atom_select_read,
+ MKI(env, sres)));
+ } else {
+ ERL_NIF_TERM data;
+
+ descP->readState |= ESOCK_STATE_SELECTED;
+ data = MKBIN(env, bufP);
+ data = MKSBIN(env, data, 0, read);
+ res = MKT2(env, esock_atom_select, data);
+ }
+
+ /* This transfers "ownership" of the *allocated* binary to an
+ * erlang term (no need for an explicit free).
+ */
+ return res;
+}
+
+
+/* *** recv_init_current_reader ***
+ *
+ * Initiate (maybe) the currentReader structure of the descriptor.
+ * Including monitoring the calling process.
+ */
+static
+void recv_init_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM recvRef)
+{
+ if (descP->currentReaderP == NULL) {
+
+ ESOCK_ASSERT( enif_self(env, &descP->currentReader.pid) != NULL );
+
+ ESOCK_ASSERT( MONP("recv_init_current_reader -> current reader",
+ env, descP,
+ &descP->currentReader.pid,
+ &descP->currentReader.mon) == 0);
+ ESOCK_ASSERT(!descP->currentReader.env);
+
+ descP->currentReader.env = esock_alloc_env("current-reader");
+ descP->currentReader.ref =
+ CP_TERM(descP->currentReader.env, recvRef);
+ descP->currentReaderP = &descP->currentReader;
+ } else {
+
+ /*
+ * This is a retry:
+ * We have done, for instance, recv(Sock, X), but only received Y < X.
+ * We then call recv again with size = X-Y. So, we then get a new ref.
+ *
+ * Make use of the existing environment
+ */
+
+ enif_clear_env(descP->currentReader.env);
+ descP->currentReader.ref = CP_TERM(descP->currentReader.env, recvRef);
+ }
+}
+
+
+/* *** recv_update_current_reader ***
+ *
+ * Demonitors the current reader process and pop's the reader queue.
+ * If there is a waiting (reader) process, then it will be assigned
+ * as the new current reader and a new (read) select will be done.
+ */
+
+static
+void recv_update_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
+{
+ if (descP->currentReaderP != NULL) {
+
+ ESOCK_ASSERT( DEMONP("recv_update_current_reader",
+ env, descP, &descP->currentReader.mon) == 0);
+
+ if (! esock_activate_next_reader(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "recv_update_current_reader(%T) {%d} -> no more readers\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentReaderP = NULL;
+ }
+ }
+}
+
+
+/* *** recv_error_current_reader ***
+ *
+ * Process the current reader and any waiting readers
+ * when a read (fatal) error has occurred.
+ * All waiting readers will be "aborted", that is a
+ * nif_abort message will be sent (with ref and reason).
+ */
+
+static
+void recv_error_current_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM reason)
+{
+ if (descP->currentReaderP != NULL) {
+ ESockRequestor req;
+
+ esock_requestor_release("recv_error_current_reader",
+ env, descP, &descP->currentReader);
+
+ req.env = NULL; /* read by reader_pop before free */
+ while (esock_reader_pop(env, descP, &req)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO", "recv_error_current_reader(%T) {%d} -> abort"
+ "\r\n pid: %T"
+ "\r\n reason %T"
+ "\r\n", sockRef, descP->sock,
+ req.pid, reason) );
+
+ esock_send_abort_msg(env, descP, sockRef, &req, reason);
+
+ ESOCK_ASSERT( DEMONP("recv_error_current_reader -> pop'ed reader",
+ env, descP, &req.mon) == 0);
+ }
+
+ descP->currentReaderP = NULL;
+ }
+}
+
+
+/* *** essio_down_ctrl ***
+ *
+ * Stop after a downed controller (controlling process = owner process)
+ *
+ * This is 'extern' because its currently called from prim_socket_nif
+ * (esock_setopt_otp_ctrl_proc).
+ */
+extern
+void essio_down_ctrl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ const ErlNifPid* pidP)
+{
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_down_ctrl {%d} ->"
+ "\r\n Pid: %T"
+ "\r\n", descP->sock, MKPID(env, pidP)) );
+
+ if (do_stop(env, descP)) {
+ /* esock_stop() is scheduled
+ * - it has to close the socket
+ */
+ SSDBG( descP,
+ ("UNIX-ESSIO", "essio_down_ctrl {%d} -> stop was scheduled\r\n",
+ descP->sock) );
+ } else {
+ int err;
+
+ /* Socket is not in the select machinery
+ * so esock_stop() will not be called
+ * - we have to do an unclean (non blocking) socket close here
+ */
+
+#ifdef HAVE_SENDFILE
+ if (descP->sendfileHandle != INVALID_HANDLE)
+ esock_send_sendfile_deferred_close_msg(env, descP);
+#endif
+
+ err = esock_close_socket(env, descP, FALSE);
+ if (err != 0)
+ esock_warning_msg("[UNIX-ESSIO] "
+ "Failed closing socket for terminating "
+ "owner process: "
+ "\r\n Owner Process: %T"
+ "\r\n Descriptor: %d"
+ "\r\n Errno: %d (%T)"
+ "\r\n",
+ MKPID(env, pidP), descP->sock,
+ err, MKA(env, erl_errno_id(err)));
+ }
+}
+
+
+
+/* *** essio_down_acceptor ***
+ *
+ * Check and then handle a downed acceptor process.
+ *
+ */
+static
+void essio_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentAcceptor.mon, monP)) {
+ MON_INIT(&descP->currentAcceptor.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> "
+ "current acceptor - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (!esock_activate_next_acceptor(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> no more writers\r\n",
+ sockRef, descP->sock) );
+
+ descP->readState &= ~ESOCK_STATE_ACCEPTING;
+
+ descP->currentAcceptorP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting acceptors */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_acceptor(%T) {%d} -> "
+ "not current acceptor - maybe a waiting acceptor\r\n",
+ sockRef, descP->sock) );
+
+ esock_acceptor_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
+/* *** essio_down_writer ***
+ *
+ * Check and then handle a downed writer process.
+ *
+ */
+
+static
+void essio_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentWriter.mon, monP)) {
+ MON_INIT(&descP->currentWriter.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> "
+ "current writer - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (!esock_activate_next_writer(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> no active writer\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentWriterP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting writer(s) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_writer(%T) {%d} -> "
+ "not current writer - maybe a waiting writer\r\n",
+ sockRef, descP->sock) );
+
+ esock_writer_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
+/* *** essio_down_reader ***
+ *
+ * Check and then handle a downed reader process.
+ *
+ */
+
+static
+void essio_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pidP,
+ const ErlNifMonitor* monP)
+{
+ if (MON_EQ(&descP->currentReader.mon, monP)) {
+ MON_INIT(&descP->currentReader.mon);
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> "
+ "current reader - try activate next\r\n",
+ sockRef, descP->sock) );
+
+ if (! esock_activate_next_reader(env, descP, sockRef)) {
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> no more readers\r\n",
+ sockRef, descP->sock) );
+
+ descP->currentReaderP = NULL;
+ }
+
+ } else {
+
+ /* Maybe unqueue one of the waiting reader(s) */
+
+ SSDBG( descP,
+ ("UNIX-ESSIO",
+ "essio_down_reader(%T) {%d} -> "
+ "not current reader - maybe a waiting reader\r\n",
+ sockRef, descP->sock) );
+
+ esock_reader_unqueue(env, descP, NULL, pidP);
+ }
+}
+
+
+#endif