diff --git a/distrib/sets/lists/minix-comp/mi b/distrib/sets/lists/minix-comp/mi index 33bdb11d6..1bec67497 100644 --- a/distrib/sets/lists/minix-comp/mi +++ b/distrib/sets/lists/minix-comp/mi @@ -1238,6 +1238,7 @@ ./usr/include/minix/sched.h minix-comp ./usr/include/minix/sef.h minix-comp ./usr/include/minix/sffs.h minix-comp +./usr/include/minix/sockdriver.h minix-comp ./usr/include/minix/sound.h minix-comp ./usr/include/minix/spin.h minix-comp ./usr/include/minix/sys_config.h minix-comp @@ -1929,6 +1930,7 @@ ./usr/lib/bc/librmt.a minix-comp bitcode ./usr/lib/bc/libsaslc.a minix-comp bitcode ./usr/lib/bc/libsffs.a minix-comp bitcode +./usr/lib/bc/libsockdriver.a minix-comp bitcode ./usr/lib/bc/libsqlite3.a minix-comp bitcode ./usr/lib/bc/libssl.a minix-comp bitcode ./usr/lib/bc/libsys.a minix-comp bitcode @@ -2088,6 +2090,8 @@ ./usr/lib/librmt.a minix-comp ./usr/lib/libsffs.a minix-comp ./usr/lib/libsffs_pic.a minix-comp +./usr/lib/libsockdriver.a minix-comp +./usr/lib/libsockdriver_pic.a minix-comp ./usr/lib/libsqlite3.a minix-comp ./usr/lib/libsqlite3_pic.a minix-comp ./usr/lib/libstdc++.a minix-comp libstdcxx diff --git a/distrib/sets/lists/minix-debug/mi b/distrib/sets/lists/minix-debug/mi index 18baf7ae5..98d520e13 100644 --- a/distrib/sets/lists/minix-debug/mi +++ b/distrib/sets/lists/minix-debug/mi @@ -98,6 +98,7 @@ ./usr/lib/librmt_g.a minix-debug debuglib ./usr/lib/libsaslc_g.a minix-debug debuglib ./usr/lib/libsffs_g.a minix-debug debuglib +./usr/lib/libsockdriver_g.a minix-debug debuglib ./usr/lib/libsqlite3_g.a minix-debug debuglib ./usr/lib/libssl_g.a minix-debug debuglib ./usr/lib/libstdc++_g.a minix-debug libstdcxx,debuglib diff --git a/lib/Makefile b/lib/Makefile index bb090de11..cc022eed4 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -36,6 +36,7 @@ SUBDIR+= ../minix/lib/libasyn \ ../minix/lib/libminixfs \ ../minix/lib/libnetdriver \ ../minix/lib/libsffs \ + ../minix/lib/libsockdriver \ ../minix/lib/libtimers \ ../minix/lib/libusb \ ../minix/lib/libvtreefs diff --git a/minix/include/minix/Makefile b/minix/include/minix/Makefile index dbc780436..fb3d553b0 100644 --- a/minix/include/minix/Makefile +++ b/minix/include/minix/Makefile @@ -17,7 +17,7 @@ INCS+= acpi.h audio_fw.h bitmap.h \ netdriver.h optset.h padconf.h partition.h portio.h \ priv.h procfs.h profile.h queryparam.h \ rmib.h rs.h safecopies.h sched.h sef.h sffs.h \ - sound.h spin.h sys_config.h sysctl.h sysinfo.h \ + sockdriver.h sound.h spin.h sys_config.h sysctl.h sysinfo.h \ syslib.h sysutil.h timers.h type.h \ u64.h usb.h usb_ch9.h vbox.h \ vboxfs.h vboxif.h vboxtype.h vm.h \ diff --git a/minix/include/minix/sockdriver.h b/minix/include/minix/sockdriver.h new file mode 100644 index 000000000..07974025c --- /dev/null +++ b/minix/include/minix/sockdriver.h @@ -0,0 +1,172 @@ +#ifndef _MINIX_SOCKDRIVER_H +#define _MINIX_SOCKDRIVER_H + +#include + +/* + * The maximum sockaddr structure size. All provided address buffers are of + * this size. The socket driver writer must ensure that all the driver's + * sockaddr structures fit in this size, increasing this variable as necessary. + */ +#define SOCKADDR_MAX (UINT8_MAX + 1) + +/* + * Convenience macro to perform static testing of the above assumption. Usage: + * STATIC_SOCKADDR_MAX_ASSERT(sockaddr_un); + */ +#define STATIC_SOCKADDR_MAX_ASSERT(t) \ + typedef int _STATIC_SOCKADDR_MAX_ASSERT_##t[/* CONSTCOND */ \ + (sizeof(struct t) <= SOCKADDR_MAX) ? 1 : -1] + +/* + * The maximum number of I/O vector elements that can be passed to the + * sockdriver_vcopy functions. + */ +#define SOCKDRIVER_IOV_MAX SCPVEC_NR + +/* Socket identifier. May also be a negative error code upon call return. */ +typedef int32_t sockid_t; + +/* Socket request identifier. To be used in struct sockdriver_call only. */ +typedef int32_t sockreq_t; + +/* + * The following structures are all identity transfer (ixfer) safe, meaning + * that they are guaranteed not to contain pointers. + */ + +/* + * Data structure with call information for later call resumption. The socket + * driver may use the sc_endpt and sc_req fields to index and find suspended + * calls for the purpose of cancellation. A provided sc_endpt value will never + * be NONE, so this value may be used to mark the structure as unused, if + * needed. Otherwise, the structure should be copied around as is. Upon + * cancellation, the original call structure must be used to resume the call, + * and not the call structure passed to sdr_cancel. + */ +struct sockdriver_call { + endpoint_t sc_endpt; /* endpoint of caller */ + sockreq_t sc_req; /* request identifier */ + cp_grant_id_t _sc_grant; /* address storage grant (private) */ + size_t _sc_len; /* size of address storage (private) */ +}; + +/* + * Data structure for the requesting party of select requests. The socket + * driver may use the ss_endpt field to index and find suspended select calls. + * A provided ss_endpt value will never be NONE, so this value may be used to + * mark the structure as unused, if needed. For future compatibility, the + * structure should be copied around in its entirety. + */ +struct sockdriver_select { + endpoint_t ss_endpt; /* endpoint of caller */ +}; + +/* Opaque data structure for copying in and out data. */ +struct sockdriver_data { + endpoint_t _sd_endpt; /* endpoint of grant owner (private) */ + cp_grant_id_t _sd_grant; /* safecopy grant (private) */ + size_t _sd_len; /* size of granted area (private) */ +}; + +/* + * Opaque data structure that may store the contents of sockdriver_data more + * compactly in cases where some of its fields are available through other + * means. Practically, this can save memory when storing suspended calls. + */ +struct sockdriver_packed_data { + cp_grant_id_t _spd_grant; /* safecopy grant (private) */ +}; + +/* Function call table for socket drivers. */ +struct sockdriver { + sockid_t (* sdr_socket)(int domain, int type, int protocol, + endpoint_t user_endpt); + int (* sdr_socketpair)(int domain, int type, int protocol, + endpoint_t user_endpt, sockid_t id[2]); + int (* sdr_bind)(sockid_t id, const struct sockaddr * __restrict addr, + socklen_t addr_len, endpoint_t user_endpt, + const struct sockdriver_call * __restrict call); + int (* sdr_connect)(sockid_t id, + const struct sockaddr * __restrict addr, socklen_t addr_len, + endpoint_t user_endpt, + const struct sockdriver_call * __restrict call); + int (* sdr_listen)(sockid_t id, int backlog); + sockid_t (* sdr_accept)(sockid_t id, struct sockaddr * __restrict addr, + socklen_t * __restrict addr_len, endpoint_t user_endpt, + const struct sockdriver_call * __restrict call); + int (* sdr_send)(sockid_t id, + const struct sockdriver_data * __restrict data, size_t len, + const struct sockdriver_data * __restrict ctl_data, + socklen_t ctl_len, const struct sockaddr * __restrict addr, + socklen_t addr_len, endpoint_t user_endpt, int flags, + const struct sockdriver_call * __restrict call); + int (* sdr_recv)(sockid_t id, + const struct sockdriver_data * __restrict data, size_t len, + const struct sockdriver_data * __restrict ctl_data, + socklen_t * __restrict ctl_len, struct sockaddr * __restrict addr, + socklen_t * __restrict addr_len, endpoint_t user_endpt, + int * __restrict flags, + const struct sockdriver_call * __restrict call); + int (* sdr_ioctl)(sockid_t id, unsigned long request, + const struct sockdriver_data * __restrict data, + endpoint_t user_endpt, + const struct sockdriver_call * __restrict call); + int (* sdr_setsockopt)(sockid_t id, int level, int name, + const struct sockdriver_data * data, socklen_t len); + int (* sdr_getsockopt)(sockid_t id, int level, int name, + const struct sockdriver_data * __restrict data, + socklen_t * __restrict len); + int (* sdr_getsockname)(sockid_t id, struct sockaddr * __restrict addr, + socklen_t * __restrict addr_len); + int (* sdr_getpeername)(sockid_t id, struct sockaddr * __restrict addr, + socklen_t * __restrict addr_len); + int (* sdr_shutdown)(sockid_t id, int how); + int (* sdr_close)(sockid_t id, const struct sockdriver_call * call); + void (* sdr_cancel)(sockid_t id, const struct sockdriver_call * call); + int (* sdr_select)(sockid_t id, unsigned int ops, + const struct sockdriver_select * sel); + void (* sdr_alarm)(clock_t stamp); + void (* sdr_other)(const message * m_ptr, int ipc_status); +}; + +/* Functions defined by libsockdriver. */ +void sockdriver_announce(void); +void sockdriver_process(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr, int ipc_status); +void sockdriver_terminate(void); +void sockdriver_task(const struct sockdriver * sdp); + +void sockdriver_reply_generic(const struct sockdriver_call * call, int reply); +void sockdriver_reply_accept(const struct sockdriver_call * __restrict call, + sockid_t reply, struct sockaddr * __restrict addr, socklen_t addr_len); +void sockdriver_reply_recv(const struct sockdriver_call * __restrict call, + int reply, socklen_t ctl_len, struct sockaddr * __restrict addr, + socklen_t addr_len, int flags); +void sockdriver_reply_select(const struct sockdriver_select * sel, sockid_t id, + int ops); + +int sockdriver_copyin(const struct sockdriver_data * __restrict data, + size_t off, void * __restrict ptr, size_t len); +int sockdriver_copyout(const struct sockdriver_data * __restrict data, + size_t off, const void * __restrict ptr, size_t len); + +int sockdriver_vcopyin(const struct sockdriver_data * __restrict data, + size_t off, const iovec_t * iov, unsigned int iovcnt); +int sockdriver_vcopyout(const struct sockdriver_data * __restrict data, + size_t off, const iovec_t * iov, unsigned int iovcnt); + +int sockdriver_copyin_opt(const struct sockdriver_data * __restrict data, + void * __restrict ptr, size_t len, socklen_t optlen); +int sockdriver_copyout_opt(const struct sockdriver_data * __restrict data, + const void * __restrict ptr, size_t len, + socklen_t * __restrict optlen); + +int sockdriver_pack_data(struct sockdriver_packed_data * pack, + const struct sockdriver_call * call, + const struct sockdriver_data * data, size_t len); +void sockdriver_unpack_data(struct sockdriver_data * data, + const struct sockdriver_call * call, + const struct sockdriver_packed_data * pack, size_t len); + +#endif /* !_MINIX_SOCKDRIVER_H */ diff --git a/minix/lib/Makefile b/minix/lib/Makefile index ff8094050..d15baacd6 100644 --- a/minix/lib/Makefile +++ b/minix/lib/Makefile @@ -18,6 +18,7 @@ SUBDIR+= libminc SUBDIR+= libminixfs SUBDIR+= libnetdriver SUBDIR+= libsffs +SUBDIR+= libsockdriver SUBDIR+= libtimers SUBDIR+= libusb SUBDIR+= libvtreefs diff --git a/minix/lib/libsockdriver/Makefile b/minix/lib/libsockdriver/Makefile new file mode 100644 index 000000000..080dc9a40 --- /dev/null +++ b/minix/lib/libsockdriver/Makefile @@ -0,0 +1,9 @@ +# Makefile for libsockdriver + +CPPFLAGS+= -D_MINIX_SYSTEM + +LIB= sockdriver + +SRCS= sockdriver.c + +.include diff --git a/minix/lib/libsockdriver/sockdriver.c b/minix/lib/libsockdriver/sockdriver.c new file mode 100644 index 000000000..46243c86b --- /dev/null +++ b/minix/lib/libsockdriver/sockdriver.c @@ -0,0 +1,1150 @@ +/* The protocol family independent socket driver framework. */ +/* + * The table below lists all supported socket driver requests, along with + * information on whether the request handler may suspend the call for later + * processing, and which message layout is to be used for the request and reply + * messages for each call. + * + * Type May suspend Request layout Reply layout + * ---- ----------- -------------- ------------ + * SDEV_SOCKET no socket socket_reply + * SDEV_SOCKETPAIR no socket socket_reply + * SDEV_BIND yes addr reply + * SDEV_CONNECT yes addr reply + * SDEV_LISTEN no simple reply + * SDEV_ACCEPT yes addr accept_reply + * SDEV_SEND yes sendrecv reply + * SDEV_RECV yes sendrecv recv_reply + * SDEV_IOCTL yes ioctl reply + * SDEV_SETSOCKOPT no getset reply + * SDEV_GETSOCKOPT no getset reply + * SDEV_GETSOCKNAME no getset reply + * SDEV_GETPEERNAME no getset reply + * SDEV_SHUTDOWN no simple reply + * SDEV_CLOSE yes simple reply + * SDEV_CANCEL n/a simple - + * SDEV_SELECT yes (special) select select_reply + * + * The request message layouts are prefixed with "m_vfs_lsockdriver_". The + * reply message layouts are prefixed with "m_lsockdriver_vfs_", and use + * message types of the format SDEV_{,SOCKET_,ACCEPT_,RECV_}REPLY, matching the + * listed reply layout. One exception is SDEV_CANCEL, which itself has no + * reply at all. The other exception is SDEV_SELECT, which has two reply + * codes: SDEV_SELECT1_REPLY (for immediate replies) and SDEV_SELECT2_REPLY + * (for late replies), both using the select_reply reply layout. + */ + +#include +#include +#include + +static int running; + +/* + * Announce that we are up and running, after a fresh start or a restart. + */ +void +sockdriver_announce(void) +{ + static const char *sockdriver_prefix = "drv.sck."; + char key[DS_MAX_KEYLEN], label[DS_MAX_KEYLEN]; + int r; + + /* Publish a driver up event. */ + if ((r = ds_retrieve_label_name(label, sef_self())) != OK) + panic("sockdriver: unable to get own label: %d", r); + + snprintf(key, sizeof(key), "%s%s", sockdriver_prefix, label); + if ((r = ds_publish_u32(key, DS_DRIVER_UP, DSF_OVERWRITE)) != OK) + panic("sockdriver: unable to publish driver up event: %d", r); +} + +/* + * Copy data from the caller into the local address space. Return OK or a + * negative error code. + */ +int +sockdriver_copyin(const struct sockdriver_data * __restrict data, size_t off, + void * __restrict ptr, size_t len) +{ + + assert(data != NULL); + assert(off + len <= data->_sd_len); + assert(data->_sd_endpt != SELF); + assert(GRANT_VALID(data->_sd_grant)); + + return sys_safecopyfrom(data->_sd_endpt, data->_sd_grant, off, + (vir_bytes)ptr, len); +} + +/* + * Copy data from the local address space to the caller. Return OK or a + * negative error code. + */ +int +sockdriver_copyout(const struct sockdriver_data * __restrict data, size_t off, + const void * __restrict ptr, size_t len) +{ + + assert(data != NULL); + assert(off + len <= data->_sd_len); + assert(data->_sd_endpt != SELF); + assert(GRANT_VALID(data->_sd_grant)); + + return sys_safecopyto(data->_sd_endpt, data->_sd_grant, off, + (vir_bytes)ptr, len); +} + +/* + * Copy data between the caller and the local address space, using a vector of + * at most SOCKDRIVER_IOV_MAX buffers. Return OK or an error code. + */ +static int +sockdriver_vcopy(const struct sockdriver_data * __restrict data, size_t off, + const iovec_t * __restrict iov, unsigned int iovcnt, int copyin) +{ + static struct vscp_vec vec[SOCKDRIVER_IOV_MAX]; + unsigned int i; + + assert(iov != NULL); + assert(iovcnt <= __arraycount(vec)); + + /* We allow zero-element vectors, because we are nice. */ + if (iovcnt == 0) + return OK; + + /* + * Do not use a vector copy operation for single-element copies, as + * this saves the kernel from having to copy in the vector itself. + */ + if (iovcnt == 1) { + if (copyin) + return sockdriver_copyin(data, off, + (void *)iov->iov_addr, iov->iov_size); + else + return sockdriver_copyout(data, off, + (const void *)iov->iov_addr, iov->iov_size); + } + + assert(data != NULL); + assert(data->_sd_endpt != SELF); + assert(GRANT_VALID(data->_sd_grant)); + + for (i = 0; i < iovcnt; i++, iov++) { + if (copyin) { + vec[i].v_from = data->_sd_endpt; + vec[i].v_to = SELF; + } else { + vec[i].v_from = SELF; + vec[i].v_to = data->_sd_endpt; + } + vec[i].v_gid = data->_sd_grant; + vec[i].v_offset = off; + vec[i].v_addr = iov->iov_addr; + vec[i].v_bytes = iov->iov_size; + + off += iov->iov_size; + } + + assert(off <= data->_sd_len); + + return sys_vsafecopy(vec, iovcnt); +} + +/* + * Copy data from the caller into the local address space, using a vector of + * buffers. Return OK or a negative error code. + */ +int +sockdriver_vcopyin(const struct sockdriver_data * __restrict data, size_t off, + const iovec_t * __restrict iov, unsigned int iovcnt) +{ + + return sockdriver_vcopy(data, off, iov, iovcnt, TRUE /*copyin*/); +} + +/* + * Copy data from the local address space to the caller, using a vector of + * buffers. Return OK or a negative error code. + */ +int +sockdriver_vcopyout(const struct sockdriver_data * __restrict data, size_t off, + const iovec_t * __restrict iov, unsigned int iovcnt) +{ + + return sockdriver_vcopy(data, off, iov, iovcnt, FALSE /*copyin*/); +} + +/* + * Copy data from the caller into the local address space, using socket option + * semantics: fail the call with EINVAL if the given 'optlen' is not equal to + * the given 'len'. Return OK or a negative error code. + */ +int +sockdriver_copyin_opt(const struct sockdriver_data * __restrict data, + void * __restrict ptr, size_t len, socklen_t optlen) +{ + + if (len != optlen) + return EINVAL; + else + return sockdriver_copyin(data, 0, ptr, len); +} + +/* + * Copy data from the local address space to the caller, using socket option + * semantics: limit the size of the copied-out data to the size pointed to by + * 'optlen', and return the possibly truncated size in 'optlen' on success. + * Return OK or a negative error code. + */ +int +sockdriver_copyout_opt(const struct sockdriver_data * __restrict data, + const void * __restrict ptr, size_t len, socklen_t * __restrict optlen) +{ + int r; + + if (len > *optlen) + len = *optlen; + + if ((r = sockdriver_copyout(data, 0, ptr, len)) == OK) + *optlen = len; + + return r; +} + +/* + * Compress a sockdriver_data structure to a smaller variant that stores only + * the fields that are not already stored redundantly in/as the given 'call' + * and 'len' parameters. The typical use case here this call suspension. In + * that case, the caller will already store 'call' and 'len' as is, and can + * save memory by storing a packed version of 'data' rather than that structure + * itself. Return OK on success, with 'pack' containing a compressed version + * of 'data'. Return EINVAL if the given parameters do not match; this would + * typically be a sign that the calling application messed up badly. + */ +int +sockdriver_pack_data(struct sockdriver_packed_data * pack, + const struct sockdriver_call * call, + const struct sockdriver_data * data, size_t len) +{ + + if (data->_sd_endpt != call->sc_endpt) + return EINVAL; + if (data->_sd_len != len) + return EINVAL; + + pack->_spd_grant = data->_sd_grant; + return OK; +} + +/* + * Decompress a previously packed sockdriver data structure into a full + * sockdriver_data structure, with the help of the given 'call' and 'len' + * parameters. Return the unpacked version of 'pack' in 'data'. This function + * always succeeds. + */ +void +sockdriver_unpack_data(struct sockdriver_data * data, + const struct sockdriver_call * call, + const struct sockdriver_packed_data * pack, size_t len) +{ + + data->_sd_endpt = call->sc_endpt; + data->_sd_grant = pack->_spd_grant; + data->_sd_len = len; +} + +/* + * Send a reply to a request. + */ +static void +send_reply(endpoint_t endpt, int type, message * m_ptr) +{ + int r; + + m_ptr->m_type = type; + + if ((r = asynsend(endpt, m_ptr)) != OK) + printf("sockdriver: sending reply to %d failed (%d)\n", + endpt, r); +} + +/* + * Send a reply which takes only a result code and no additional reply fields. + */ +static void +send_generic_reply(endpoint_t endpt, sockreq_t req, int reply) +{ + message m; + + assert(reply != SUSPEND && reply != EDONTREPLY); + + memset(&m, 0, sizeof(m)); + m.m_lsockdriver_vfs_reply.req_id = req; + m.m_lsockdriver_vfs_reply.status = reply; + + send_reply(endpt, SDEV_REPLY, &m); +} + +/* + * Send a reply to an earlier suspended request which takes only a result code + * and no additional reply fields. + */ +void +sockdriver_reply_generic(const struct sockdriver_call * call, int reply) +{ + + send_generic_reply(call->sc_endpt, call->sc_req, reply); +} + +/* + * Send a reply to a socket or a socketpair request. Since these calls may not + * be suspended, this function is used internally only. + */ +static void +send_socket_reply(endpoint_t endpt, sockreq_t req, sockid_t reply, + sockid_t reply2) +{ + message m; + + assert(reply != SUSPEND && reply != EDONTREPLY); + + memset(&m, 0, sizeof(m)); + m.m_lsockdriver_vfs_socket_reply.req_id = req; + m.m_lsockdriver_vfs_socket_reply.sock_id = reply; + m.m_lsockdriver_vfs_socket_reply.sock_id2 = reply2; + + send_reply(endpt, SDEV_SOCKET_REPLY, &m); +} + +/* + * Send a reply to an earlier suspended accept request. The given reply is + * either a socket identifier (>= 0) or an error code (< 0). On success, an + * address must be given as 'addr', and its nonzero length must be given as + * 'addr_len'. + */ +void +sockdriver_reply_accept(const struct sockdriver_call * __restrict call, + sockid_t reply, struct sockaddr * __restrict addr, socklen_t addr_len) +{ + sockid_t id; + message m; + + assert(reply != SUSPEND && reply != EDONTREPLY); + + /* + * If the accept was successful, copy out the address, if requested. + * If the copy fails, send both a valid socket ID and an error to VFS. + * VFS will then close the newly created socket immediately, and return + * the error to the caller. + * + * While not particularly nice, the general behavior of closing the + * socket after accepting it seems to be common among other OSes for + * address copy errors. Most importantly, it frees the socket driver + * from having to deal with address copy errors itself. + * + * Letting VFS close the socket is also not all that great. However, + * it is the lesser evil compared to the two main alternatives: 1) + * immediately calling sdr_close() from here, which would seriously + * complicate writing socket drivers due to sockets disappearing from + * under it, so to speak, and 2) queuing a forged incoming SDEV_CLOSE + * request, for which we do not have the necessary infrastructure. + * Additionally, VFS may close the newly accepted socket when out of + * other required resources anyway, so logically this fits in well. + * The only real price to pay is a slightly uglier message protocol. + * + * Copying out the address *length* is not our responsibility at all; + * if VFS chooses to do this itself (as opposed to letting libc do it), + * it too will have to close the socket on failure, using a separate + * close call. This is always multithreading-safe because userland can + * not access the accepted socket yet anyway. + */ + if (reply >= 0) { + id = reply; + reply = OK; + } else + id = -1; + + if (reply == OK && GRANT_VALID(call->_sc_grant)) { + if (addr == NULL || addr_len == 0) + panic("libsockdriver: success but no address given"); + + if (addr_len > call->_sc_len) + addr_len = call->_sc_len; /* truncate addr and len */ + + if (addr_len > 0) { + reply = sys_safecopyto(call->sc_endpt, call->_sc_grant, + 0, (vir_bytes)addr, addr_len); + + /* Intentionally leave 'id' set on failure here. */ + } + } else + addr_len = 0; /* not needed, but cleaner */ + + memset(&m, 0, sizeof(m)); + m.m_lsockdriver_vfs_accept_reply.req_id = call->sc_req; + m.m_lsockdriver_vfs_accept_reply.sock_id = id; + m.m_lsockdriver_vfs_accept_reply.status = reply; + m.m_lsockdriver_vfs_accept_reply.len = addr_len; + + send_reply(call->sc_endpt, SDEV_ACCEPT_REPLY, &m); +} + +/* + * Send a reply to an earlier suspended receive call. The given reply code is + * the number of regular data bytes received (>= 0) or an error code (< 0). + * On success, for connectionless sockets, 'addr' must point to the source + * address and 'addr_len' must contain the address length; for connection- + * oriented sockets, 'addr_len' must be zero, in which case 'addr' is ignored. + */ +void +sockdriver_reply_recv(const struct sockdriver_call * __restrict call, + int reply, socklen_t ctl_len, struct sockaddr * __restrict addr, + socklen_t addr_len, int flags) +{ + message m; + int r; + + assert(reply != SUSPEND && reply != EDONTREPLY); + + /* + * If applicable, copy out the address. If this fails, the result is + * loss of the data received; in the case of AF_UNIX, this may include + * references to file descriptors already created in the receiving + * process. At least Linux and NetBSD behave this way as well, which + * is not an excuse to be lazy, but we need to change just about + * everything for the worse (including having additional grants just + * for storing lengths) in order to fully solve this corner case. + * + * TODO: a reasonable compromise might be to add a callback routine for + * closing file descriptors in any already-written control data. This + * would solve the worst aspect of the data loss, not the loss itself. + */ + if (reply >= 0 && addr_len > 0 && GRANT_VALID(call->_sc_grant)) { + if (addr == NULL) + panic("libsockdriver: success but no address given"); + + if (addr_len > call->_sc_len) + addr_len = call->_sc_len; /* truncate addr and len */ + + if (addr_len > 0 && (r = sys_safecopyto(call->sc_endpt, + call->_sc_grant, 0, (vir_bytes)addr, addr_len)) != OK) + reply = r; + } else + addr_len = 0; + + memset(&m, 0, sizeof(m)); + m.m_lsockdriver_vfs_recv_reply.req_id = call->sc_req; + m.m_lsockdriver_vfs_recv_reply.status = reply; + m.m_lsockdriver_vfs_recv_reply.ctl_len = ctl_len; + m.m_lsockdriver_vfs_recv_reply.addr_len = addr_len; + m.m_lsockdriver_vfs_recv_reply.flags = flags; + + send_reply(call->sc_endpt, SDEV_RECV_REPLY, &m); +} + +/* + * Send a reply to a select request. + */ +static void +send_select_reply(const struct sockdriver_select * sel, int type, sockid_t id, + int ops) +{ + message m; + + assert(ops != SUSPEND && ops != EDONTREPLY); + + memset(&m, 0, sizeof(m)); + m.m_lsockdriver_vfs_select_reply.sock_id = id; + m.m_lsockdriver_vfs_select_reply.status = ops; + + send_reply(sel->ss_endpt, type, &m); +} + +/* + * Send a reply to an earlier select call that requested notifications. + */ +void +sockdriver_reply_select(const struct sockdriver_select * sel, sockid_t id, + int ops) +{ + + send_select_reply(sel, SDEV_SELECT2_REPLY, id, ops); +} + +/* + * Create a new socket. This call may not be suspended. + */ +static void +do_socket(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + sockid_t r; + + if (sdp->sdr_socket != NULL) + r = sdp->sdr_socket(m_ptr->m_vfs_lsockdriver_socket.domain, + m_ptr->m_vfs_lsockdriver_socket.type, + m_ptr->m_vfs_lsockdriver_socket.protocol, + m_ptr->m_vfs_lsockdriver_socket.user_endpt); + else + r = EOPNOTSUPP; + + send_socket_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_socket.req_id, r, -1); +} + +/* + * Create a pair of connected sockets. Relevant for UNIX domain sockets only. + * This call may not be suspended. + */ +static void +do_socketpair(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + sockid_t sockid[2]; + int r; + + if (sdp->sdr_socketpair != NULL) + r = sdp->sdr_socketpair(m_ptr->m_vfs_lsockdriver_socket.domain, + m_ptr->m_vfs_lsockdriver_socket.type, + m_ptr->m_vfs_lsockdriver_socket.protocol, + m_ptr->m_vfs_lsockdriver_socket.user_endpt, sockid); + else + r = EOPNOTSUPP; + + if (r != OK) { + sockid[0] = r; + sockid[1] = -1; + } + + send_socket_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_socket.req_id, sockid[0], sockid[1]); +} + +/* + * Bind a socket to a local address, or connect a socket to a remote address. + * In both cases, this call may be suspended by the socket driver, in which + * case sockdriver_reply_generic() must be used to reply later. + * + * For bind(2), POSIX is not entirely consistent regarding call suspension: the + * bind(2) call may return EINPROGRESS for nonblocking sockets, but this also + * suggests that blocking bind(2) calls may be interrupted by signals (as on + * MINIX3 they can be), yet EINTR is not defined as a valid return code for it. + */ +static void +do_bind_connect(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + int (*proc)(sockid_t, const struct sockaddr * __restrict, socklen_t, + endpoint_t, const struct sockdriver_call * __restrict); + struct sockdriver_call call; + char buf[SOCKADDR_MAX]; + sockid_t id; + cp_grant_id_t grant; + socklen_t len; + endpoint_t user_endpt; + int r, sflags; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_addr.req_id; + + id = m_ptr->m_vfs_lsockdriver_addr.sock_id; + grant = m_ptr->m_vfs_lsockdriver_addr.grant; + len = m_ptr->m_vfs_lsockdriver_addr.len; + user_endpt = m_ptr->m_vfs_lsockdriver_addr.user_endpt; + sflags = m_ptr->m_vfs_lsockdriver_addr.sflags; + + switch (m_ptr->m_type) { + case SDEV_BIND: proc = sdp->sdr_bind; break; + case SDEV_CONNECT: proc = sdp->sdr_connect; break; + default: panic("expected bind or connect"); + } + + r = OK; + if (!GRANT_VALID(grant) || len == 0 || len > sizeof(buf)) + r = EINVAL; + else + r = sys_safecopyfrom(m_ptr->m_source, grant, 0, (vir_bytes)buf, + len); + + if (r == OK) { + if (proc != NULL) + r = proc(id, (struct sockaddr *)buf, len, user_endpt, + (sflags & SDEV_NONBLOCK) ? NULL : &call); + else + r = EOPNOTSUPP; + } + + assert(!(sflags & SDEV_NONBLOCK) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_generic(&call, r); +} + +/* + * Put a socket in listening mode. This call may not be suspended. + */ +static void +do_listen(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + int r; + + if (sdp->sdr_listen != NULL) + r = sdp->sdr_listen(m_ptr->m_vfs_lsockdriver_simple.sock_id, + m_ptr->m_vfs_lsockdriver_simple.param /*backlog*/); + else + r = EOPNOTSUPP; + + send_generic_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_simple.req_id, r); +} + +/* + * Accept a connection on a listening socket, creating a new socket. + * This call may be suspended by the socket driver, in which case + * sockdriver_reply_accept() must be used to reply later. + */ +static void +do_accept(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + char buf[SOCKADDR_MAX]; + struct sockaddr *addr; + socklen_t len; + endpoint_t user_endpt; + int sflags; + sockid_t r; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_addr.req_id; + call._sc_grant = m_ptr->m_vfs_lsockdriver_addr.grant; + call._sc_len = m_ptr->m_vfs_lsockdriver_addr.len; + + addr = (struct sockaddr *)buf; + len = 0; + user_endpt = m_ptr->m_vfs_lsockdriver_addr.user_endpt; + sflags = m_ptr->m_vfs_lsockdriver_addr.sflags; + + if (sdp->sdr_accept != NULL) + r = sdp->sdr_accept(m_ptr->m_vfs_lsockdriver_addr.sock_id, + addr, &len, user_endpt, + (sflags & SDEV_NONBLOCK) ? NULL : &call); + else + r = EOPNOTSUPP; + + assert(!(sflags & SDEV_NONBLOCK) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_accept(&call, r, addr, len); +} + +/* + * Send regular and/or control data. This call may be suspended by the socket + * driver, in which case sockdriver_reply_generic() must be used to reply + * later. + */ +static void +do_send(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + struct sockdriver_data data, ctl_data; + char buf[SOCKADDR_MAX]; + struct sockaddr *addr; + cp_grant_id_t addr_grant; + socklen_t addr_len; + endpoint_t user_endpt; + sockid_t id; + int r, flags; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_sendrecv.req_id; + + data._sd_grant = m_ptr->m_vfs_lsockdriver_sendrecv.data_grant; + data._sd_endpt = m_ptr->m_source; + data._sd_len = m_ptr->m_vfs_lsockdriver_sendrecv.data_len; + + /* The returned size must fit in an 'int'; truncate accordingly. */ + if (data._sd_len > INT_MAX) + data._sd_len = INT_MAX; + + ctl_data._sd_endpt = m_ptr->m_source; + ctl_data._sd_grant = m_ptr->m_vfs_lsockdriver_sendrecv.ctl_grant; + ctl_data._sd_len = m_ptr->m_vfs_lsockdriver_sendrecv.ctl_len; + + id = m_ptr->m_vfs_lsockdriver_sendrecv.sock_id; + addr_grant = m_ptr->m_vfs_lsockdriver_sendrecv.addr_grant; + addr_len = m_ptr->m_vfs_lsockdriver_sendrecv.addr_len; + user_endpt = m_ptr->m_vfs_lsockdriver_sendrecv.user_endpt; + flags = m_ptr->m_vfs_lsockdriver_sendrecv.flags; + + r = OK; + if (GRANT_VALID(addr_grant)) { + if (addr_len == 0 || addr_len > sizeof(buf)) + r = EINVAL; + else + r = sys_safecopyfrom(m_ptr->m_source, addr_grant, 0, + (vir_bytes)buf, addr_len); + addr = (struct sockaddr *)buf; + } else { + addr = NULL; + addr_len = 0; + } + + if (r == OK) { + if (sdp->sdr_send != NULL) + r = sdp->sdr_send(id, &data, data._sd_len, &ctl_data, + ctl_data._sd_len, addr, addr_len, user_endpt, + flags, (flags & MSG_DONTWAIT) ? NULL : &call); + else + r = EOPNOTSUPP; + } + + assert(!(flags & MSG_DONTWAIT) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_generic(&call, r); +} + +/* + * Receive regular and/or control data. This call may be suspended by the + * socket driver, in which case sockdriver_reply_recv() must be used to reply + * later. + */ +static void +do_recv(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + struct sockdriver_data data, ctl_data; + char buf[SOCKADDR_MAX]; + struct sockaddr *addr; + sockid_t id; + socklen_t ctl_len, addr_len; + endpoint_t user_endpt; + int r, flags; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_sendrecv.req_id; + call._sc_grant = m_ptr->m_vfs_lsockdriver_sendrecv.addr_grant; + call._sc_len = m_ptr->m_vfs_lsockdriver_sendrecv.addr_len; + + data._sd_endpt = m_ptr->m_source; + data._sd_grant = m_ptr->m_vfs_lsockdriver_sendrecv.data_grant; + data._sd_len = m_ptr->m_vfs_lsockdriver_sendrecv.data_len; + + /* The returned size must fit in an 'int'; truncate accordingly. */ + if (data._sd_len > INT_MAX) + data._sd_len = INT_MAX; + + ctl_data._sd_endpt = m_ptr->m_source; + ctl_data._sd_grant = m_ptr->m_vfs_lsockdriver_sendrecv.ctl_grant; + ctl_data._sd_len = m_ptr->m_vfs_lsockdriver_sendrecv.ctl_len; + + id = m_ptr->m_vfs_lsockdriver_sendrecv.sock_id; + ctl_len = ctl_data._sd_len; + addr = (struct sockaddr *)buf; + addr_len = 0; /* the default: no source address */ + user_endpt = m_ptr->m_vfs_lsockdriver_sendrecv.user_endpt; + flags = m_ptr->m_vfs_lsockdriver_sendrecv.flags; + + if (sdp->sdr_recv != NULL) + r = sdp->sdr_recv(id, &data, data._sd_len, &ctl_data, &ctl_len, + addr, &addr_len, user_endpt, &flags, + (flags & MSG_DONTWAIT) ? NULL : &call); + else + r = EOPNOTSUPP; + + assert(!(flags & MSG_DONTWAIT) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_recv(&call, r, ctl_len, addr, addr_len, + flags); +} + +/* + * Process an I/O control call. This call may be suspended by the socket + * driver, in which case sockdriver_reply_generic() must be used to reply + * later. + */ +static void +do_ioctl(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + struct sockdriver_data data; + sockid_t id; + unsigned long request; + endpoint_t user_endpt; + int r, sflags; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_ioctl.req_id; + + id = m_ptr->m_vfs_lsockdriver_ioctl.sock_id; + request = m_ptr->m_vfs_lsockdriver_ioctl.request; + user_endpt = m_ptr->m_vfs_lsockdriver_ioctl.user_endpt; + sflags = m_ptr->m_vfs_lsockdriver_ioctl.sflags; + + data._sd_endpt = m_ptr->m_source; + data._sd_grant = m_ptr->m_vfs_lsockdriver_ioctl.grant; + if (_MINIX_IOCTL_BIG(request)) + data._sd_len = _MINIX_IOCTL_SIZE_BIG(request); + else + data._sd_len = _MINIX_IOCTL_SIZE(request); + + if (sdp->sdr_ioctl != NULL) + r = sdp->sdr_ioctl(id, request, &data, user_endpt, + (sflags & SDEV_NONBLOCK) ? NULL : &call); + else + r = EOPNOTSUPP; + + assert(!(sflags & SDEV_NONBLOCK) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_generic(&call, r); +} + +/* + * Set socket options. This call may not be suspended. + */ +static void +do_setsockopt(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_data data; + int r; + + data._sd_endpt = m_ptr->m_source; + data._sd_grant = m_ptr->m_vfs_lsockdriver_getset.grant; + data._sd_len = m_ptr->m_vfs_lsockdriver_getset.len; + + if (sdp->sdr_setsockopt != NULL) + r = sdp->sdr_setsockopt( + m_ptr->m_vfs_lsockdriver_getset.sock_id, + m_ptr->m_vfs_lsockdriver_getset.level, + m_ptr->m_vfs_lsockdriver_getset.name, &data, data._sd_len); + else + r = EOPNOTSUPP; + + send_generic_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_getset.req_id, r); +} + +/* + * Retrieve socket options. This call may not be suspended. + */ +static void +do_getsockopt(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_data data; + socklen_t len; + int r; + + data._sd_endpt = m_ptr->m_source; + data._sd_grant = m_ptr->m_vfs_lsockdriver_getset.grant; + data._sd_len = m_ptr->m_vfs_lsockdriver_getset.len; + + len = data._sd_len; + + if (sdp->sdr_setsockopt != NULL) + r = sdp->sdr_getsockopt( + m_ptr->m_vfs_lsockdriver_getset.sock_id, + m_ptr->m_vfs_lsockdriver_getset.level, + m_ptr->m_vfs_lsockdriver_getset.name, &data, &len); + else + r = EOPNOTSUPP; + + /* + * For these requests, the main reply code is used to return the + * resulting data length on success. The length will never large + * enough to overflow, and we save on API calls and messages this way. + */ + if (r == OK) { + assert(len <= INT_MAX); + + r = (int)len; + } else if (r > 0) + panic("libsockdriver: invalid reply"); + + send_generic_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_getset.req_id, r); +} + +/* + * Get local or remote address. This call may not be suspended. + */ +static void +do_getname(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + int (*proc)(sockid_t, struct sockaddr * __restrict, + socklen_t * __restrict); + char buf[SOCKADDR_MAX]; + socklen_t addr_len, len; + int r; + + switch (m_ptr->m_type) { + case SDEV_GETSOCKNAME: proc = sdp->sdr_getsockname; break; + case SDEV_GETPEERNAME: proc = sdp->sdr_getpeername; break; + default: panic("expected getsockname or getpeername"); + } + + /* The 'name' and 'level' message fields are unused for these calls. */ + + addr_len = m_ptr->m_vfs_lsockdriver_getset.len; + len = 0; + + if (proc != NULL) + r = proc(m_ptr->m_vfs_lsockdriver_getset.sock_id, + (struct sockaddr *)buf, &len); + else + r = EOPNOTSUPP; + + if (r == OK) { + if (len == 0) + panic("libsockdriver: success but no address given"); + + if (addr_len > len) + addr_len = len; + + /* As above, use the reply code for the resulting length. */ + if (addr_len > 0 && (r = sys_safecopyto(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_getset.grant, 0, (vir_bytes)buf, + addr_len)) == OK) { + assert(addr_len <= INT_MAX); + + /* + * The Open Group wording has changed recently, now + * suggesting that when truncating the "stored address" + * the resulting length should be truncated as well. + */ + r = addr_len; + } + } else if (r > 0) + panic("libsockdriver: invalid reply"); + + send_generic_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_getset.req_id, r); +} + +/* + * Shut down socket send and receive operations. This call may not be + * suspended. + */ +static void +do_shutdown(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + int r; + + if (sdp->sdr_shutdown != NULL) + r = sdp->sdr_shutdown( + m_ptr->m_vfs_lsockdriver_simple.sock_id, + m_ptr->m_vfs_lsockdriver_simple.param /*how*/); + else + r = EOPNOTSUPP; + + send_generic_reply(m_ptr->m_source, + m_ptr->m_vfs_lsockdriver_simple.req_id, r); +} + +/* + * Close a socket. This call may be suspended by the socket driver, in which + * case sockdriver_reply_generic() must be used to reply later. Note that VFS + * currently does not support blocking close operations, and will mark all + * close operations as nonblocking. This will be changed in the future. + */ +static void +do_close(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + int r, sflags; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_simple.req_id; + + sflags = m_ptr->m_vfs_lsockdriver_simple.param; + + if (sdp->sdr_close != NULL) + r = sdp->sdr_close(m_ptr->m_vfs_lsockdriver_simple.sock_id, + (sflags & SDEV_NONBLOCK) ? NULL : &call); + else + r = OK; /* exception: this must never fail */ + + assert(!(sflags & SDEV_NONBLOCK) || (r != SUSPEND && r != EDONTREPLY)); + + if (r != SUSPEND && r != EDONTREPLY) + sockdriver_reply_generic(&call, r); +} + +/* + * Cancel a previous operation which may currently be suspended. The cancel + * operation itself does not have a reply. Instead, if the provided operation + * was found to be currently suspended, that operation must be aborted and a + * reply (typically EINTR) must be sent for it. If no matching operation was + * found, no reply must be sent at all. + */ +static void +do_cancel(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_call call; + + call.sc_endpt = m_ptr->m_source; + call.sc_req = m_ptr->m_vfs_lsockdriver_simple.req_id; + + /* The 'param' message field is unused by this request. */ + + if (sdp->sdr_cancel != NULL) + sdp->sdr_cancel(m_ptr->m_vfs_lsockdriver_simple.sock_id, + &call); +} + +/* + * Process a select request. Select requests have their own rules with respect + * to suspension and later notification. The basic idea is: an immediate reply + * is always sent with the subset of requested operations that are ready. If + * SDEV_NOTIFY is given, the remaining operations are to be combined with any + * previous operations requested (with SDEV_NOTIFY) by the calling endpoint. + * If any of the pending previous operations become ready, a late reply is sent + * and only those ready operations are forgotten, leaving any other non-ready + * operations for other late replies. + */ +static void +do_select(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr) +{ + struct sockdriver_select sel; + sockid_t id; + int r, ops; + + sel.ss_endpt = m_ptr->m_source; + id = m_ptr->m_vfs_lsockdriver_select.sock_id; + ops = m_ptr->m_vfs_lsockdriver_select.ops; + + if (sdp->sdr_select != NULL) + r = sdp->sdr_select(id, ops, + (ops & SDEV_NOTIFY) ? &sel : NULL); + else + r = EOPNOTSUPP; + + send_select_reply(&sel, SDEV_SELECT1_REPLY, id, r); +} + +/* + * Return TRUE if the given endpoint may initiate socket requests. + */ +static int +may_request(endpoint_t endpt) +{ + + /* + * For now, we allow only VFS to initiate socket calls. In the future, + * we may allow networked file systems to call into the network stack + * directly. The sockdriver API has already been designed to allow for + * that, but this check will then need to change. Ideally it would be + * using some sort of ACL system. For now, this check prevents that + * network drivers themselves create and use sockets. + */ + return (endpt == VFS_PROC_NR); +} + +/* + * Process an incoming message, and (typically) send a reply. + */ +void +sockdriver_process(const struct sockdriver * __restrict sdp, + const message * __restrict m_ptr, int ipc_status) +{ + + /* Handle notifications separately. */ + if (is_ipc_notify(ipc_status)) { + switch (m_ptr->m_source) { + case CLOCK: + if (sdp->sdr_alarm != NULL) + sdp->sdr_alarm(m_ptr->m_notify.timestamp); + break; + default: + if (sdp->sdr_other != NULL) + sdp->sdr_other(m_ptr, ipc_status); + } + + return; /* do not send a reply */ + } + + /* Is this a socket request from an acceptable party? */ + if (!IS_SDEV_RQ(m_ptr->m_type) || !may_request(m_ptr->m_source)) { + if (sdp->sdr_other != NULL) + sdp->sdr_other(m_ptr, ipc_status); + + return; /* do not send a reply */ + } + + /* + * Process the request. If the request is not recognized, we cannot + * send a reply either, because we do not know the reply message + * format. Passing the request message to the sdr_other hook serves no + * practical purpose either: if the request is legitimate, this library + * should know about it. + */ + switch (m_ptr->m_type) { + case SDEV_SOCKET: do_socket(sdp, m_ptr); break; + case SDEV_SOCKETPAIR: do_socketpair(sdp, m_ptr); break; + case SDEV_BIND: do_bind_connect(sdp, m_ptr); break; + case SDEV_CONNECT: do_bind_connect(sdp, m_ptr); break; + case SDEV_LISTEN: do_listen(sdp, m_ptr); break; + case SDEV_ACCEPT: do_accept(sdp, m_ptr); break; + case SDEV_SEND: do_send(sdp, m_ptr); break; + case SDEV_RECV: do_recv(sdp, m_ptr); break; + case SDEV_IOCTL: do_ioctl(sdp, m_ptr); break; + case SDEV_SETSOCKOPT: do_setsockopt(sdp, m_ptr); break; + case SDEV_GETSOCKOPT: do_getsockopt(sdp, m_ptr); break; + case SDEV_GETSOCKNAME: do_getname(sdp, m_ptr); break; + case SDEV_GETPEERNAME: do_getname(sdp, m_ptr); break; + case SDEV_SHUTDOWN: do_shutdown(sdp, m_ptr); break; + case SDEV_CLOSE: do_close(sdp, m_ptr); break; + case SDEV_CANCEL: do_cancel(sdp, m_ptr); break; + case SDEV_SELECT: do_select(sdp, m_ptr); break; + } +} + +/* + * Break out of the main loop after finishing the current request. + */ +void +sockdriver_terminate(void) +{ + + running = FALSE; + + sef_cancel(); +} + +/* + * Main program of any socket driver. + */ +void +sockdriver_task(const struct sockdriver * sdp) +{ + message m; + int r, ipc_status; + + /* The main message loop. */ + running = TRUE; + + while (running) { + if ((r = sef_receive_status(ANY, &m, &ipc_status)) != OK) { + if (r == EINTR) + continue; /* sef_cancel() was called */ + + panic("sockdriver: sef_receive_status failed: %d", r); + } + + sockdriver_process(sdp, &m, ipc_status); + } +} diff --git a/share/mk/bsd.prog.mk b/share/mk/bsd.prog.mk index a0892ee0a..2e122a60e 100644 --- a/share/mk/bsd.prog.mk +++ b/share/mk/bsd.prog.mk @@ -237,6 +237,7 @@ LIB${_lib:tu}= ${DESTDIR}/usr/lib/lib${_lib:S/xx/++/:S/atf_c/atf-c/}.a netdriver \ netsock \ sffs \ + sockdriver \ sys \ timers \ usb \