--- /dev/null
+#include <quickjs/quickjs.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#define countof(x) ( sizeof(x) / sizeof(x[0]) )
+#define libc_error(ctx) ( JS_ThrowInternalError(ctx, "%s", strerror(errno)) )
+
+static JSValue js_os_fork(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+ return JS_NewInt32(ctx, fork());
+}
+
+static JSValue js_os_socket(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+ int family, type, protocol, fd;
+
+ if ( argc >= 3
+ && ! JS_ToInt32(ctx, &family, argv[0])
+ && ! JS_ToInt32(ctx, &type, argv[1])
+ && ! JS_ToInt32(ctx, &protocol, argv[2])
+ && ( fd = socket(family, type, protocol))
+ ) {
+ return JS_NewInt32(ctx, fd);
+ } else {
+ return JS_EXCEPTION;
+ }
+}
+
+static JSValue js_os_bind(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv, int con
+) {
+ struct addrinfo *addr, *waddr;
+ struct sockaddr_un addr_un = { .sun_family = AF_UNIX };
+ int fd, b = -1; const char *host, *port, *path;
+
+ if ( argc < 2 ) return JS_EXCEPTION;
+ if ( JS_ToInt32(ctx, &fd, argv[0]) ) return JS_EXCEPTION;
+
+ if ( argc == 2 ) {
+ path = JS_ToCString(ctx, argv[1]);
+ if (strlen(path) < sizeof(addr_un.sun_path)) {
+ strcpy((char *) &(addr_un.sun_path), path);
+ if (!con) b = bind(fd, (struct sockaddr *) &addr_un, sizeof(addr_un));
+ else b = connect(fd, (struct sockaddr *) &addr_un, sizeof(addr_un));
+ }
+ JS_FreeCString(ctx, path);
+ if (b) return libc_error(ctx); else return JS_UNDEFINED;
+
+ } else { // argc >= 3
+ host = JS_ToCString(ctx, argv[1]);
+ port = JS_ToCString(ctx, argv[2]);
+ if (! getaddrinfo(host, port, NULL, &addr))
+ waddr = addr;
+ JS_FreeCString(ctx, host); JS_FreeCString(ctx, port);
+
+ while (waddr) {
+ if (!con) b = bind(fd, addr->ai_addr, addr->ai_addrlen);
+ else b = connect(fd, addr->ai_addr, addr->ai_addrlen);
+ if (!b) break;
+ waddr = waddr->ai_next;
+ }
+ freeaddrinfo(addr);
+ if (b) return libc_error(ctx); else return JS_UNDEFINED;
+ }
+}
+
+static JSValue js_os_listen(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+ int fd, backlog = 1;
+
+ if (argc < 1 ) return JS_EXCEPTION;
+ if ( JS_ToInt32(ctx, &fd, argv[0]) )
+ return JS_EXCEPTION;
+ if ( argc >= 2 && JS_ToInt32(ctx, &backlog, argv[1]) )
+ return JS_EXCEPTION;
+
+ if (! listen(fd, backlog)) return JS_UNDEFINED;
+ else return libc_error(ctx);
+}
+
+static JSValue js_os_accept(
+ JSContext *ctx, JSValueConst this, int argc, JSValueConst *argv
+) {
+ struct sockaddr peer; socklen_t ps = sizeof(struct sockaddr);
+ char host[40], port[6];
+ int fd, new;
+
+ if ( argc < 1 ) return JS_EXCEPTION;
+ if ( JS_ToInt32(ctx, &fd, argv[0]) ) return JS_EXCEPTION;
+
+ // FIXME: use numeric port number (int instead of string)
+ if ( argc >= 2 ) {
+ if ((new = accept(fd, &peer, &ps)) != -1) {
+ JS_SetPropertyStr(ctx, argv[1], "protocol", JS_NewInt32(ctx, peer.sa_family));
+ if ( peer.sa_family != AF_UNIX ) {
+ getnameinfo(&peer, ps, host, 40, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);
+ JS_SetPropertyStr(ctx, argv[1], "host", JS_NewString(ctx, host));
+ JS_SetPropertyStr(ctx, argv[1], "port", JS_NewString(ctx, port));
+ }
+ }
+ } else {
+ new = accept(fd, NULL, 0);
+ }
+ if (new != -1)
+ return JS_NewInt32(ctx, new);
+ else return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry os_socket_funcs[] = {
+ JS_PROP_INT32_DEF("AF_UNIX", AF_UNIX, JS_PROP_CONFIGURABLE),
+ JS_PROP_INT32_DEF("AF_INET", AF_INET, JS_PROP_CONFIGURABLE),
+ JS_PROP_INT32_DEF("AF_INET6", AF_INET6, JS_PROP_CONFIGURABLE),
+ JS_PROP_INT32_DEF("SOCK_STREAM", SOCK_STREAM, JS_PROP_CONFIGURABLE),
+ JS_PROP_INT32_DEF("SOCK_DGRAM", SOCK_DGRAM, JS_PROP_CONFIGURABLE),
+ JS_CFUNC_DEF("socket", 3, js_os_socket),
+ JS_CFUNC_DEF("listen", 1, js_os_listen),
+ JS_CFUNC_DEF("accept", 1, js_os_accept),
+ JS_CFUNC_DEF("fork", 0, js_os_fork),
+ JS_CFUNC_MAGIC_DEF("bind", 2, js_os_bind, 0),
+ JS_CFUNC_MAGIC_DEF("connect", 2, js_os_bind, 1),
+ // JS_CFUNC_DEF("send", 2, js_os_send), // would be same as os.write
+ // JS_CFUNC_DEF("recv", 2, js_os_recv), // would be same as os.read
+ // JS_CFUNC_DEF("close", 1, js_os_close), // would be same as os.close
+};
+
+static int sock_modinit(JSContext *ctx, JSModuleDef *mod ) {
+ return JS_SetModuleExportList(
+ ctx, mod, os_socket_funcs , countof(os_socket_funcs)
+ );
+}
+
+JSModuleDef *js_init_module(JSContext *ctx, const char *name) {
+ JSModuleDef *mod = JS_NewCModule(ctx, name, sock_modinit);
+ if (mod) JS_AddModuleExportList(
+ ctx, mod, os_socket_funcs, countof(os_socket_funcs)
+ );
+
+ return mod;
+}