aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--package.json5
-rw-r--r--src/library.js764
-rw-r--r--src/library_fs.js12
-rw-r--r--src/library_sockfs.js554
-rw-r--r--src/settings.js1
-rwxr-xr-xtests/runner.py4
-rw-r--r--tests/sockets/test_sockets_msg.h1
-rw-r--r--tests/sockets/test_sockets_select_server_down_client.c2
-rw-r--r--tests/test_sockets.py110
10 files changed, 1043 insertions, 412 deletions
diff --git a/.gitignore b/.gitignore
index 92043241..747394e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@
*.bc
src/relooper*.js
+node_modules/
+
# Ignore generated files
src/relooper.js
src/relooper.js.raw.js
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..157885fb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "dependencies": {
+ "ws": "~0.4.28"
+ }
+}
diff --git a/src/library.js b/src/library.js
index b5542609..307df9c6 100644
--- a/src/library.js
+++ b/src/library.js
@@ -600,31 +600,32 @@ LibraryManager.library = {
// poll.h
// ==========================================================================
+ __DEFAULT_POLLMASK: {{{ cDefine('POLLIN') }}} | {{{ cDefine('POLLOUT') }}},
__pollfd_struct_layout: Runtime.generateStructInfo([
['i32', 'fd'],
['i16', 'events'],
['i16', 'revents']]),
- poll__deps: ['$FS', '__pollfd_struct_layout'],
+ poll__deps: ['$FS', '__DEFAULT_POLLMASK', '__pollfd_struct_layout'],
poll: function(fds, nfds, timeout) {
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html
- // NOTE: This is pretty much a no-op mimicking glibc.
var offsets = ___pollfd_struct_layout;
var nonzero = 0;
for (var i = 0; i < nfds; i++) {
var pollfd = fds + ___pollfd_struct_layout.__size__ * i;
var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}};
var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}};
- var revents = 0;
+ var mask = {{{ cDefine('POLLNVAL') }}};
var stream = FS.getStream(fd);
if (stream) {
- if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}};
- if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}};
- } else {
- if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}};
+ mask = ___DEFAULT_POLLMASK;
+ if (stream.stream_ops.poll) {
+ mask = stream.stream_ops.poll(stream);
+ }
}
- if (revents) nonzero++;
- {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}}
+ mask &= events | {{{ cDefine('POLLERR') }}} | {{{ cDefine('POLLHUP') }}};
+ if (mask) nonzero++;
+ {{{ makeSetValue('pollfd', 'offsets.revents', 'mask', 'i16') }}}
}
return nonzero;
},
@@ -723,7 +724,7 @@ LibraryManager.library = {
FS.close(stream);
return 0;
} catch (e) {
- FS.handleFSError(e);;
+ FS.handleFSError(e);
return -1;
}
},
@@ -1006,9 +1007,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _recv(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -1139,9 +1142,11 @@ LibraryManager.library = {
return -1;
}
+#if SOCKET_WEBRTC
if (stream && ('socket' in stream)) {
return _send(fildes, buf, nbyte, 0);
}
+#endif
try {
var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
@@ -7537,7 +7542,7 @@ LibraryManager.library = {
['i32', 'sin6_family'],
['i16', 'sin6_port'],
['i32', 'sin6_flowinfo'],
- ['b128', 'sin6_addr'],
+ ['b16', 'sin6_addr'],
['i32', 'sin6_scope_id']
]),
msghdr_layout: Runtime.generateStructInfo([
@@ -7549,6 +7554,10 @@ LibraryManager.library = {
['i32', 'msg_controllen'],
['i32', 'msg_flags'],
]),
+ iovec_layout: Runtime.generateStructInfo([
+ ['i8*', 'iov_base'],
+ ['i32', 'iov_len']
+ ])
},
#if SOCKET_WEBRTC
@@ -7994,424 +8003,471 @@ LibraryManager.library = {
return {};
},
- socket__deps: ['$FS', '$Sockets'],
+ socket__deps: ['$FS', '$SOCKFS'],
socket: function(family, type, protocol) {
- var stream = type == {{{ cDefine('SOCK_STREAM') }}};
- if (protocol) {
- assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp
+ var sock = SOCKFS.createSocket(family, type, protocol);
+ assert(sock.stream.fd < 64); // select() assumes socket fd values are in 0..63
+ return sock.stream.fd;
+ },
+
+ socketpair__deps: ['$ERRNO_CODES', '__setErrNo'],
+ socketpair: function(domain, type, protocol, sv) {
+ // int socketpair(int domain, int type, int protocol, int sv[2]);
+ // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
+ ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
+ return -1;
+ },
+
+ shutdown__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ shutdown: function(fd, how) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var stream = FS.createStream({
- connected: false,
- stream: stream,
- socket: true,
- stream_ops: {}
- });
- assert(stream.fd < 64); // select() assumes socket fd values are in 0..63
- return stream.fd;
+ _close(fd);
},
- connect__deps: ['$FS', '$Sockets', '_inet_ntop4_raw', 'ntohs', '_lookup_addr'],
- connect: function(fd, addr, addrlen) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // TODO support ipv6
- info.connected = true;
- info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32');
- info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16'));
- info.host = __inet_ntop4_raw(info.addr);
- // The incoming address could perhaps be a fake address generated by gethostbyname,
- // look it up to be sure.
- var lookup = __lookup_addr(info.host);
- if (lookup) {
- info.host = lookup;
+ bind__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_addr', '_read_sockaddr'],
+ bind: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- try {
- console.log('opening ws://' + info.host + ':' + info.port);
- info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']);
- info.socket.binaryType = 'arraybuffer';
- var i32Temp = new Uint32Array(1);
- var i8Temp = new Uint8Array(i32Temp.buffer);
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = __lookup_addr(info.addr) || info.addr;
- info.inQueue = [];
- info.hasData = function() { return info.inQueue.length > 0 }
- if (!info.stream) {
- var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message
- }
+ try {
+ sock.sock_ops.bind(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+ },
- info.socket.onmessage = function(event) {
- assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data!
- var data = new Uint8Array(event.data); // make a typed array view on the array buffer
-#if SOCKET_DEBUG
- Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]);
-#endif
- if (info.stream) {
- info.inQueue.push(data);
- } else {
- // we added headers with message sizes, read those to find discrete messages
- if (partialBuffer) {
- // append to the partial buffer
- var newBuffer = new Uint8Array(partialBuffer.length + data.length);
- newBuffer.set(partialBuffer);
- newBuffer.set(data, partialBuffer.length);
- // forget the partial buffer and work on data
- data = newBuffer;
- partialBuffer = null;
- }
- var currPos = 0;
- while (currPos+4 < data.length) {
- i8Temp.set(data.subarray(currPos, currPos+4));
- var currLen = i32Temp[0];
- assert(currLen > 0);
- if (currPos + 4 + currLen > data.length) {
- break; // not enough data has arrived
- }
- currPos += 4;
-#if SOCKET_DEBUG
- Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]);
-#endif
- info.inQueue.push(data.subarray(currPos, currPos+currLen));
- currPos += currLen;
- }
- // If data remains, buffer it
- if (currPos < data.length) {
- partialBuffer = data.subarray(currPos);
- }
- }
- }
- function send(data) {
- // TODO: if browser accepts views, can optimize this
-#if SOCKET_DEBUG
- Module.print('sender actually sending ' + Array.prototype.slice.call(data));
-#endif
- // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning
- info.socket.send(data.buffer);
- }
- var outQueue = [];
- var intervalling = false, interval;
- function trySend() {
- if (info.socket.readyState != info.socket.OPEN) {
- if (!intervalling) {
- intervalling = true;
- console.log('waiting for socket in order to send');
- interval = setInterval(trySend, 100);
- }
- return;
- }
- for (var i = 0; i < outQueue.length; i++) {
- send(outQueue[i]);
- }
- outQueue.length = 0;
- if (intervalling) {
- intervalling = false;
- clearInterval(interval);
- }
- }
- info.sender = function(data) {
- if (!info.stream) {
- // add a header with the message size
- var header = new Uint8Array(4);
- i32Temp[0] = data.length;
- header.set(i8Temp);
- outQueue.push(header);
- }
- outQueue.push(new Uint8Array(data));
- trySend();
- };
- } catch(e) {
- Module.printErr('Error in connect(): ' + e);
- ___setErrNo(ERRNO_CODES.EACCES);
+ connect__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_addr', '_read_sockaddr'],
+ connect: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- // always "fail" in non-blocking mode
- ___setErrNo(ERRNO_CODES.EINPROGRESS);
- return -1;
+ var info = __read_sockaddr(addrp, addrlen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ var port = info.port;
+ var addr = __lookup_addr(info.addr) || info.addr;
+
+ try {
+ sock.sock_ops.connect(sock, addr, port);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- recv__deps: ['$FS'],
- recv: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ listen__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo'],
+ listen: function(fd, backlog) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (!info.hasData()) {
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- // socket has closed
- return 0;
- } else {
- // else, our socket is in a valid state but truly has nothing available
- ___setErrNo(ERRNO_CODES.EAGAIN);
- return -1;
- }
+ try {
+ sock.sock_ops.listen(sock, backlog);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
-#endif
- var buffer = info.inQueue.shift();
-#if SOCKET_DEBUG
- Module.print('recv: ' + [Array.prototype.slice.call(buffer)]);
-#endif
- if (len < buffer.length) {
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- info.inQueue.unshift(buffer.subarray(len));
-#if SOCKET_DEBUG
- Module.print('recv: put back: ' + (len - buffer.length));
-#endif
+ },
+
+ accept__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', 'socket'],
+ accept: function(fd, addrp, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ var newfd = _socket(sock.family, sock.type, sock.protocol);
+ var newsock = SOCKFS.getSocket(newfd);
+ assert(newsock);
+ try {
+ sock.sock_ops.accept(sock, newsock, sock.stream.flags);
+ if (addrp) {
+ var res = __write_sockaddr(addr, newsock.family, __lookup_name(newsock.daddr), newsock.dport);
+ assert(!res.errno);
}
- buffer = buffer.subarray(0, len);
+ return newfd;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- HEAPU8.set(buffer, buf);
- return buffer.length;
},
- send__deps: ['$FS'],
- send: function(fd, buf, len, flags) {
- var info = FS.getStream(fd);
- if (!info) {
+ getsockname__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_lookup_name', '_inet_pton_raw'],
+ getsockname: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
-#if SOCKET_WEBRTC == 0
- if (info.socket.readyState === WebSocket.CLOSING || info.socket.readyState === WebSocket.CLOSED) {
- ___setErrNo(ERRNO_CODES.ENOTCONN);
- return -1;
- } else if (info.socket.readyState === WebSocket.CONNECTING) {
- ___setErrNo(ERRNO_CODES.EAGAIN);
+ try {
+ var info = sock.sock_ops.getname(sock);
+ var res = __write_sockaddr(addr, sock.family, __lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
return -1;
}
-#endif
- info.sender(HEAPU8.subarray(buf, buf+len));
- return len;
},
- sendmsg__deps: ['$FS', '$Sockets', 'connect'],
- sendmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ getpeername__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_write_sockaddr', '_lookup_name', '_inet_pton_raw'],
+ getpeername: function (fd, addr, addrlen) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg vecs: ' + num);
-#endif
- var totalSize = 0;
- for (var i = 0; i < num; i++) {
- totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
+ try {
+ var info = sock.sock_ops.getname(sock, true);
+ var res = __write_sockaddr(addr, sock.family, __lookup_name(info.addr), info.port);
+ assert(!res.errno);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- var buffer = new Uint8Array(totalSize);
- var ret = 0;
- for (var i = 0; i < num; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('sendmsg curr size: ' + currNum);
-#endif
- if (!currNum) continue;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
- buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret);
- ret += currNum;
+ },
+
+ send__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'write'],
+ send: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- info.sender(buffer); // send all the iovs as a single message
- return ret;
+ // TODO honor flags
+ return _write(fd, buf, len);
},
- recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'],
- recvmsg: function(fd, msg, flags) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
-#if SOCKET_DEBUG
- Module.print('recvmsg connecting');
-#endif
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- assert(name, 'sendmsg on non-connected socket, and no name/address in the message');
- _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}});
+ recv__deps: ['$SOCKFS', '$ERRNO_CODES', '__setErrNo', 'read'],
+ recv: function(fd, buf, len, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- if (!info.hasData()) {
- ___setErrNo(ERRNO_CODES.EWOULDBLOCK);
+ // TODO honor flags
+ return _read(fd, buf, len);
+ },
+
+ sendto__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_addr', '_read_sockaddr'],
+ sendto: function(fd, message, length, flags, dest_addr, dest_len) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
return -1;
}
- var buffer = info.inQueue.shift();
- var bytes = buffer.length;
-#if SOCKET_DEBUG
- Module.print('recvmsg bytes: ' + bytes);
-#endif
- // write source
- var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'info.addr', 'i32') }}};
- {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(info.port)', 'i16') }}};
- // write data
- var ret = bytes;
- var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
- var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
- var bufferPos = 0;
- for (var i = 0; i < num && bytes > 0; i++) {
- var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg loop ' + [i, num, bytes, currNum]);
-#endif
- if (!currNum) continue;
- currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..?
- bytes -= currNum;
- var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}};
-#if SOCKET_DEBUG
- Module.print('recvmsg call recv ' + currNum);
-#endif
- HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf);
- bufferPos += currNum;
+
+ // read the address and port to send to
+ var info = __read_sockaddr(dest_addr, dest_len);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
}
- if (info.stream) {
- // This is tcp (reliable), so if not all was read, keep it
- if (bufferPos < bytes) {
- info.inQueue.unshift(buffer.subarray(bufferPos));
-#if SOCKET_DEBUG
- Module.print('recvmsg: put back: ' + (bytes - bufferPos));
-#endif
- }
+ var port = info.port;
+ var addr = __lookup_addr(info.addr) || info.addr;
+
+ // send the message
+ try {
+ var slab = {{{ makeGetSlabs('message', 'i8', true) }}};
+ return sock.sock_ops.sendmsg(sock, slab, message, length, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
- return ret;
},
- recvfrom__deps: ['$FS', 'connect', 'recv'],
+ recvfrom__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_name', '_write_sockaddr'],
recvfrom: function(fd, buf, len, flags, addr, addrlen) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- // if we are not connected, use the address info in the message
- if (!info.connected) {
- //var name = {{{ makeGetValue('addr', '0', '*') }}};
- _connect(fd, addr, addrlen);
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
}
- return _recv(fd, buf, len, flags);
- },
- shutdown__deps: ['$FS'],
- shutdown: function(fd, how) {
- var stream = FS.getStream(fd);
- if (!stream) return -1;
- stream.socket.close();
- FS.closeStream(stream);
- },
+ // read from the socket
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, len);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
- ioctl__deps: ['$FS'],
- ioctl: function(fd, request, varargs) {
- var info = FS.getStream(fd);
- if (!info) return -1;
- var bytes = 0;
- if (info.hasData()) {
- bytes = info.inQueue[0].length;
+ if (!msg) {
+ // socket is closed
+ return 0;
}
- var dest = {{{ makeGetValue('varargs', '0', 'i32') }}};
- {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}};
- return 0;
+
+ // write the source address out
+ if (addr) {
+ var res = __write_sockaddr(addr, sock.family, __lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out
+ HEAPU8.set(msg.buffer, buf);
+
+ return msg.buffer.byteLength;
},
- setsockopt: function(d, level, optname, optval, optlen) {
- console.log('ignoring setsockopt command');
- return 0;
+ sendmsg__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_addr', '_read_sockaddr'],
+ sendmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', '*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // read the address and port to send to
+ var addr;
+ var port;
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ var namelen = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}};
+ if (name) {
+ var info = __read_sockaddr(name, namelen);
+ if (info.errno) {
+ ___setErrNo(info.errno);
+ return -1;
+ }
+ port = info.port;
+ addr = __lookup_addr(info.addr) || info.addr;
+ }
+
+ // concatenate scatter-gather arrays into one message buffer
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+ var view = new Uint8Array(total);
+ var offset = 0;
+ for (var i = 0; i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ for (var j = 0; j < iovlen; j++) {
+ view[offset++] = {{{ makeGetValue('iovbase', 'j', 'i8') }}};
+ }
+ }
+
+ // write the buffer
+ try {
+ return sock.sock_ops.sendmsg(sock, view, 0, total, addr, port);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
- bind__deps: ['connect'],
- bind: function(fd, addr, addrlen) {
- _connect(fd, addr, addrlen);
- return 0;
+ recvmsg__deps: ['$FS', '$SOCKFS', '$ERRNO_CODES', '__setErrNo', '_lookup_name', '_inet_pton_raw', '_write_sockaddr'],
+ recvmsg: function(fd, message, flags) {
+ var sock = SOCKFS.getSocket(fd);
+ if (!sock) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+
+ var iov = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}};
+ var num = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}};
+
+ // get the total amount of data we can read across all arrays
+ var total = 0;
+ for (var i = 0; i < num; i++) {
+ total += {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ }
+
+ // try to read total data
+ var msg;
+ try {
+ msg = sock.sock_ops.recvmsg(sock, total);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
+
+ if (!msg) {
+ // socket is closed
+ return 0;
+ }
+
+ // TODO honor flags:
+ // MSG_OOB
+ // Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific.
+ // MSG_PEEK
+ // Peeks at the incoming message.
+ // MSG_WAITALL
+ // Requests that the function block until the full amount of data requested can be returned. The function may return a smaller amount of data if a signal is caught, if the connection is terminated, if MSG_PEEK was specified, or if an error is pending for the socket.
+
+ // write the source address out
+ var name = {{{ makeGetValue('message', 'Sockets.msghdr_layout.msg_name', '*') }}};
+ if (name) {
+ var res = __write_sockaddr(name, sock.family, __lookup_name(msg.addr), msg.port);
+ assert(!res.errno);
+ }
+ // write the buffer out to the scatter-gather arrays
+ var bytesRead = 0;
+ var bytesRemaining = msg.buffer.byteLength;
+
+ for (var i = 0; bytesRemaining > 0 && i < num; i++) {
+ var iovbase = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_base', 'i8*') }}};
+ var iovlen = {{{ makeGetValue('iov', '(Sockets.iovec_layout.__size__ * i) + Sockets.iovec_layout.iov_len', 'i32') }}};
+ if (!iovlen) {
+ continue;
+ }
+ var length = Math.min(iovlen, bytesRemaining);
+ var buf = msg.buffer.subarray(bytesRead, bytesRead + length);
+ HEAPU8.set(buf, iovbase + bytesRead);
+ bytesRead += length;
+ bytesRemaining -= length;
+ }
+
+ // TODO set msghdr.msg_flags
+ // MSG_EOR
+ // End of record was received (if supported by the protocol).
+ // MSG_OOB
+ // Out-of-band data was received.
+ // MSG_TRUNC
+ // Normal data was truncated.
+ // MSG_CTRUNC
+
+ return bytesRead;
},
- listen: function(fd, backlog) {
+ setsockopt: function(fd, level, optname, optval, optlen) {
+ console.log('ignoring setsockopt command');
return 0;
},
- accept__deps: ['$FS', '$Sockets'],
- accept: function(fd, addr, addrlen) {
- // TODO: webrtc queued incoming connections, etc.
- // For now, the model is that bind does a connect, and we "accept" that one connection,
- // which has host:port the same as ours. We also return the same socket fd.
- var info = FS.getStream(fd);
- if (!info) return -1;
- if (addr) {
- setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32');
- setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32');
- setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32');
- }
- return fd;
- },
+ // ==========================================================================
+ // select.h
+ // ==========================================================================
- select__deps: ['$FS'],
+ select__deps: ['$FS', '__DEFAULT_POLLMASK'],
select: function(nfds, readfds, writefds, exceptfds, timeout) {
// readfds are supported,
// writefds checks socket open status
// exceptfds not supported
// timeout is always 0 - fully async
- assert(!exceptfds);
-
- var errorCondition = 0;
+ assert(nfds <= 64, 'nfds must be less than or equal to 64'); // fd sets have 64 bits
+ assert(!exceptfds, 'exceptfds not supported');
- function canRead(info) {
- return (info.hasData && info.hasData()) ||
- info.socket.readyState == WebSocket.CLOSING || // let recv return 0 once closed
- info.socket.readyState == WebSocket.CLOSED;
- }
+ var total = 0;
+
+ var srcReadLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0),
+ srcReadHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0);
+ var srcWriteLow = (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0),
+ srcWriteHigh = (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0);
+ var srcExceptLow = (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0),
+ srcExceptHigh = (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ var dstReadLow = 0,
+ dstReadHigh = 0;
+ var dstWriteLow = 0,
+ dstWriteHigh = 0;
+ var dstExceptLow = 0,
+ dstExceptHigh = 0;
+
+ var allLow = (readfds ? {{{ makeGetValue('readfds', 0, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 0, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 0, 'i32') }}} : 0);
+ var allHigh = (readfds ? {{{ makeGetValue('readfds', 4, 'i32') }}} : 0) |
+ (writefds ? {{{ makeGetValue('writefds', 4, 'i32') }}} : 0) |
+ (exceptfds ? {{{ makeGetValue('exceptfds', 4, 'i32') }}} : 0);
+
+ function get(fd, low, high, val) {
+ return (fd < 32 ? (low & val) : (high & val));
+ }
+
+ for (var fd = 0; fd < nfds; fd++) {
+ var mask = 1 << (fd % 32);
+ if (!(get(fd, allLow, allHigh, mask))) {
+ continue; // index isn't in the set
+ }
- function canWrite(info) {
- return info.socket && (info.socket.readyState == info.socket.OPEN);
- }
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
- function checkfds(nfds, fds, can) {
- if (!fds) return 0;
+ var flags = ___DEFAULT_POLLMASK;
- var bitsSet = 0;
- var dstLow = 0;
- var dstHigh = 0;
- var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}};
- var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}};
- nfds = Math.min(64, nfds); // fd sets have 64 bits
+ if (stream.stream_ops.poll) {
+ flags = stream.stream_ops.poll(stream);
+ }
- for (var fd = 0; fd < nfds; fd++) {
- var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh;
- if (int_ & mask) {
- // index is in the set, check if it is ready for read
- var info = FS.getStream(fd);
- if (!info) {
- ___setErrNo(ERRNO_CODES.EBADF);
- return -1;
- }
- if (can(info)) {
- // set bit
- fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask);
- bitsSet++;
- }
- }
+ if ((flags & {{{ cDefine('POLLIN') }}}) && get(fd, srcReadLow, srcReadHigh, mask)) {
+ fd < 32 ? (dstReadLow = dstReadLow | mask) : (dstReadHigh = dstReadHigh | mask);
+ total++;
+ }
+ if ((flags & {{{ cDefine('POLLOUT') }}}) && get(fd, srcWriteLow, srcWriteHigh, mask)) {
+ fd < 32 ? (dstWriteLow = dstWriteLow | mask) : (dstWriteHigh = dstWriteHigh | mask);
+ total++;
}
+ if ((flags & {{{ cDefine('POLLPRI') }}}) && get(fd, srcExceptLow, srcExceptHigh, mask)) {
+ fd < 32 ? (dstExceptLow = dstExceptLow | mask) : (dstExceptHigh = dstExceptHigh | mask);
+ total++;
+ }
+ }
- {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}};
- {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}};
- return bitsSet;
+ if (readfds) {
+ {{{ makeSetValue('readfds', '0', 'dstReadLow', 'i32') }}};
+ {{{ makeSetValue('readfds', '4', 'dstReadHigh', 'i32') }}};
}
+ if (writefds) {
+ {{{ makeSetValue('writefds', '0', 'dstWriteLow', 'i32') }}};
+ {{{ makeSetValue('writefds', '4', 'dstWriteHigh', 'i32') }}};
+ }
+ if (exceptfds) {
+ {{{ makeSetValue('exceptfds', '0', 'dstExceptLow', 'i32') }}};
+ {{{ makeSetValue('exceptfds', '4', 'dstExceptHigh', 'i32') }}};
+ }
+
+ return total;
+ },
- var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite);
- if (errorCondition) {
+ // ==========================================================================
+ // sys/ioctl.h
+ // ==========================================================================
+
+ ioctl__deps: ['$FS'],
+ ioctl: function(fd, request, varargs) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
___setErrNo(ERRNO_CODES.EBADF);
return -1;
- } else {
- return totalHandles;
}
+ var arg = {{{ makeGetValue('varargs', '0', 'i32') }}};
+ return FS.ioctl(stream, request, arg);
},
#endif
- socketpair__deps: ['__setErrNo', '$ERRNO_CODES'],
- socketpair: function(domain, type, protocol, sv) {
- // int socketpair(int domain, int type, int protocol, int sv[2]);
- // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html
- ___setErrNo(ERRNO_CODES.EOPNOTSUPP);
- return -1;
- },
-
// pty.h
openpty: function() { throw 'openpty: TODO' },
diff --git a/src/library_fs.js b/src/library_fs.js
index 63ad7c8d..77066059 100644
--- a/src/library_fs.js
+++ b/src/library_fs.js
@@ -159,6 +159,9 @@ mergeInto(LibraryManager.library, {
isFIFO: function(mode) {
return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}};
},
+ isSocket: function(mode) {
+ return (mode & {{{ cDefine('S_IFSOCK') }}}) === {{{ cDefine('S_IFSOCK') }}};
+ },
//
// paths
@@ -400,6 +403,9 @@ mergeInto(LibraryManager.library, {
getStream: function(fd) {
return FS.streams[fd];
},
+ // TODO parameterize this function such that a stream
+ // object isn't directly passed in. not possible until
+ // SOCKFS is completed.
createStream: function(stream, fd_start, fd_end) {
var fd = FS.nextfd(fd_start, fd_end);
stream.fd = fd;
@@ -1459,6 +1465,12 @@ mergeInto(LibraryManager.library, {
throw new FS.errnoError(ERRNO_CODES.ENODEV);
}
return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags);
+ },
+ ioctl: function(stream, cmd, arg) {
+ if (!stream.stream_ops.ioctl) {
+ throw new FS.ErrnoError(ERRNO_CODES.ENOTTY);
+ }
+ return stream.stream_ops.ioctl(stream, cmd, arg);
}
}
});
diff --git a/src/library_sockfs.js b/src/library_sockfs.js
index 13118b71..0736224d 100644
--- a/src/library_sockfs.js
+++ b/src/library_so