Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a36a62ca14 | |||
| 28ef43264a |
@@ -0,0 +1,94 @@
|
|||||||
|
#ifndef EL_PLATFORM_WIN_H
|
||||||
|
#define EL_PLATFORM_WIN_H
|
||||||
|
/*
|
||||||
|
* el_platform_win.h — Windows OS-boundary shim for el_runtime.c.
|
||||||
|
*
|
||||||
|
* Branch: feat/windows-el-runtime. Included ONLY when _WIN32 is defined; the POSIX build is
|
||||||
|
* untouched. Goal: let el_runtime.c (a BSD-sockets / dlfcn / fork host) compile and link with
|
||||||
|
* mingw-w64 into a native neuron.exe, with no behavioural change to the Linux/macOS build.
|
||||||
|
*
|
||||||
|
* What it maps:
|
||||||
|
* - sockets : winsock2 (same call names: socket/bind/listen/accept/recv/send/setsockopt).
|
||||||
|
* Sockets close with closesocket() (see el_closesocket), and the stack must be
|
||||||
|
* started once with WSAStartup — done automatically via a load-time constructor.
|
||||||
|
* - dlsym : el_runtime.c uses dlsym(RTLD_DEFAULT, name) to resolve callback/tool symbols
|
||||||
|
* exported by the main module. Windows equivalent: GetProcAddress on the process
|
||||||
|
* module. Link the soul with -Wl,--export-all-symbols so the symbols are findable.
|
||||||
|
* - popen : mapped to _popen/_pclose.
|
||||||
|
* - threads : UNCHANGED. mingw-w64 ships winpthreads, so <pthread.h> + -lpthread just work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <process.h>
|
||||||
|
|
||||||
|
/* Portable headers mingw-w64 provides (verified present). */
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <strings.h> /* strcasecmp */
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h> /* mingw-w64 provides gettimeofday here */
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/* ── socket close ─────────────────────────────────────────────────────────── */
|
||||||
|
/* Winsock closes sockets with closesocket(), not close() (close() is for file fds). The POSIX
|
||||||
|
build defines the same helper as close() so the call sites are identical across platforms. */
|
||||||
|
static inline int el_closesocket(int s) { return closesocket((SOCKET)s); }
|
||||||
|
|
||||||
|
/* ── winsock init (once, at load) ─────────────────────────────────────────── */
|
||||||
|
static void el__win_net_init(void) {
|
||||||
|
static int inited = 0;
|
||||||
|
if (!inited) { WSADATA w; WSAStartup(MAKEWORD(2, 2), &w); inited = 1; }
|
||||||
|
}
|
||||||
|
__attribute__((constructor)) static void el__win_ctor(void) { el__win_net_init(); }
|
||||||
|
|
||||||
|
/* ── dlsym → GetProcAddress ───────────────────────────────────────────────── */
|
||||||
|
#ifndef RTLD_DEFAULT
|
||||||
|
#define RTLD_DEFAULT ((void*)0)
|
||||||
|
#endif
|
||||||
|
static inline void* el_win_dlsym(void* handle, const char* name) {
|
||||||
|
(void)handle;
|
||||||
|
return (void*)(uintptr_t)GetProcAddress(GetModuleHandleA(NULL), name);
|
||||||
|
}
|
||||||
|
#define dlsym(h, n) el_win_dlsym((h), (n))
|
||||||
|
|
||||||
|
/* ── popen / pclose ───────────────────────────────────────────────────────── */
|
||||||
|
#define popen _popen
|
||||||
|
#define pclose _pclose
|
||||||
|
|
||||||
|
/* ── misc POSIX → Win32 shims ─────────────────────────────────────────────── */
|
||||||
|
#include <direct.h> /* _mkdir */
|
||||||
|
#define mkdir(path, mode) _mkdir(path) /* POSIX mkdir(path,mode) → _mkdir(path) */
|
||||||
|
#define timegm _mkgmtime /* UTC tm → time_t */
|
||||||
|
|
||||||
|
/* setenv/unsetenv: not in the Windows CRT; map to _putenv_s. */
|
||||||
|
static inline int setenv(const char* name, const char* value, int overwrite) {
|
||||||
|
(void)overwrite;
|
||||||
|
return _putenv_s(name, value ? value : "");
|
||||||
|
}
|
||||||
|
static inline int unsetenv(const char* name) { return _putenv_s(name, ""); }
|
||||||
|
|
||||||
|
/* localtime_r/gmtime_r: Windows offers localtime_s/gmtime_s with reversed arg order. */
|
||||||
|
static inline struct tm* localtime_r(const time_t* t, struct tm* out) {
|
||||||
|
return localtime_s(out, t) == 0 ? out : (struct tm*)0;
|
||||||
|
}
|
||||||
|
static inline struct tm* gmtime_r(const time_t* t, struct tm* out) {
|
||||||
|
return gmtime_s(out, t) == 0 ? out : (struct tm*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* EL_PLATFORM_WIN_H */
|
||||||
@@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
#include "el_runtime.h"
|
#include "el_runtime.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Windows OS-boundary shim (winsock/dlsym/popen). Threading stays on <pthread.h> (winpthreads). */
|
||||||
|
#include "el_platform_win.h"
|
||||||
|
#else
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <strings.h> /* strcasecmp */
|
#include <strings.h> /* strcasecmp */
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -42,6 +46,10 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
/* On POSIX, sockets close with the same close() as files; el_platform_win.h supplies the Windows
|
||||||
|
variant. Defined here so the socket call sites are identical across platforms. */
|
||||||
|
static inline int el_closesocket(int s) { return close(s); }
|
||||||
|
#endif
|
||||||
#ifdef HAVE_CURL
|
#ifdef HAVE_CURL
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -1587,17 +1595,17 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
|||||||
int sock = socket(AF_INET6, SOCK_STREAM, 0);
|
int sock = socket(AF_INET6, SOCK_STREAM, 0);
|
||||||
if (sock < 0) { perror("socket"); return 0; }
|
if (sock < 0) { perror("socket"); return 0; }
|
||||||
int yes = 1; int no = 0;
|
int yes = 1; int no = 0;
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
|
||||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no));
|
||||||
struct sockaddr_in6 addr;
|
struct sockaddr_in6 addr;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin6_family = AF_INET6;
|
addr.sin6_family = AF_INET6;
|
||||||
addr.sin6_addr = in6addr_any;
|
addr.sin6_addr = in6addr_any;
|
||||||
addr.sin6_port = htons((uint16_t)p);
|
addr.sin6_port = htons((uint16_t)p);
|
||||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
perror("bind"); close(sock); return 0;
|
perror("bind"); el_closesocket(sock); return 0;
|
||||||
}
|
}
|
||||||
if (listen(sock, 64) < 0) { perror("listen"); close(sock); return 0; }
|
if (listen(sock, 64) < 0) { perror("listen"); el_closesocket(sock); return 0; }
|
||||||
fprintf(stderr, "[http] listening on [::]:%d (dual-stack)\n", p);
|
fprintf(stderr, "[http] listening on [::]:%d (dual-stack)\n", p);
|
||||||
while (1) {
|
while (1) {
|
||||||
struct sockaddr_in6 cli;
|
struct sockaddr_in6 cli;
|
||||||
@@ -1614,11 +1622,11 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
|||||||
_http_conn_active++;
|
_http_conn_active++;
|
||||||
pthread_mutex_unlock(&_http_conn_mu);
|
pthread_mutex_unlock(&_http_conn_mu);
|
||||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||||
if (!arg) { close(cfd); continue; }
|
if (!arg) { el_closesocket(cfd); continue; }
|
||||||
arg->fd = cfd;
|
arg->fd = cfd;
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
if (pthread_create(&tid, NULL, http_worker, arg) != 0) {
|
if (pthread_create(&tid, NULL, http_worker, arg) != 0) {
|
||||||
close(cfd); free(arg);
|
el_closesocket(cfd); free(arg);
|
||||||
pthread_mutex_lock(&_http_conn_mu);
|
pthread_mutex_lock(&_http_conn_mu);
|
||||||
_http_conn_active--;
|
_http_conn_active--;
|
||||||
pthread_cond_signal(&_http_conn_cv);
|
pthread_cond_signal(&_http_conn_cv);
|
||||||
@@ -1627,7 +1635,7 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
|||||||
}
|
}
|
||||||
pthread_detach(tid);
|
pthread_detach(tid);
|
||||||
}
|
}
|
||||||
close(sock);
|
el_closesocket(sock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1837,17 +1845,17 @@ el_val_t http_serve_v2(el_val_t port, el_val_t handler) {
|
|||||||
int sock = socket(AF_INET6, SOCK_STREAM, 0);
|
int sock = socket(AF_INET6, SOCK_STREAM, 0);
|
||||||
if (sock < 0) { perror("socket"); return 0; }
|
if (sock < 0) { perror("socket"); return 0; }
|
||||||
int yes = 1; int no = 0;
|
int yes = 1; int no = 0;
|
||||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
|
||||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no));
|
||||||
struct sockaddr_in6 addr;
|
struct sockaddr_in6 addr;
|
||||||
memset(&addr, 0, sizeof(addr));
|
memset(&addr, 0, sizeof(addr));
|
||||||
addr.sin6_family = AF_INET6;
|
addr.sin6_family = AF_INET6;
|
||||||
addr.sin6_addr = in6addr_any;
|
addr.sin6_addr = in6addr_any;
|
||||||
addr.sin6_port = htons((uint16_t)p);
|
addr.sin6_port = htons((uint16_t)p);
|
||||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||||
perror("bind"); close(sock); return 0;
|
perror("bind"); el_closesocket(sock); return 0;
|
||||||
}
|
}
|
||||||
if (listen(sock, 64) < 0) { perror("listen"); close(sock); return 0; }
|
if (listen(sock, 64) < 0) { perror("listen"); el_closesocket(sock); return 0; }
|
||||||
fprintf(stderr, "[http v2] listening on [::]:%d (dual-stack)\n", p);
|
fprintf(stderr, "[http v2] listening on [::]:%d (dual-stack)\n", p);
|
||||||
while (1) {
|
while (1) {
|
||||||
struct sockaddr_in6 cli;
|
struct sockaddr_in6 cli;
|
||||||
@@ -1864,11 +1872,11 @@ el_val_t http_serve_v2(el_val_t port, el_val_t handler) {
|
|||||||
_http_conn_active++;
|
_http_conn_active++;
|
||||||
pthread_mutex_unlock(&_http_conn_mu);
|
pthread_mutex_unlock(&_http_conn_mu);
|
||||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||||
if (!arg) { close(cfd); continue; }
|
if (!arg) { el_closesocket(cfd); continue; }
|
||||||
arg->fd = cfd;
|
arg->fd = cfd;
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
if (pthread_create(&tid, NULL, http_worker_v2, arg) != 0) {
|
if (pthread_create(&tid, NULL, http_worker_v2, arg) != 0) {
|
||||||
close(cfd); free(arg);
|
el_closesocket(cfd); free(arg);
|
||||||
pthread_mutex_lock(&_http_conn_mu);
|
pthread_mutex_lock(&_http_conn_mu);
|
||||||
_http_conn_active--;
|
_http_conn_active--;
|
||||||
pthread_cond_signal(&_http_conn_cv);
|
pthread_cond_signal(&_http_conn_cv);
|
||||||
@@ -1877,7 +1885,7 @@ el_val_t http_serve_v2(el_val_t port, el_val_t handler) {
|
|||||||
}
|
}
|
||||||
pthread_detach(tid);
|
pthread_detach(tid);
|
||||||
}
|
}
|
||||||
close(sock);
|
el_closesocket(sock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2051,6 +2059,23 @@ el_val_t exec(el_val_t cmdv) {
|
|||||||
el_val_t exec_bg(el_val_t cmdv) {
|
el_val_t exec_bg(el_val_t cmdv) {
|
||||||
const char* cmd = EL_CSTR(cmdv);
|
const char* cmd = EL_CSTR(cmdv);
|
||||||
if (!cmd || !*cmd) return el_wrap_str(el_strdup(""));
|
if (!cmd || !*cmd) return el_wrap_str(el_strdup(""));
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Windows: no fork/exec. Launch a detached `cmd /c <command>` with no console window via
|
||||||
|
CreateProcess (DETACHED_PROCESS | CREATE_NO_WINDOW). Returns the PID as a string, "" on fail.
|
||||||
|
Mirrors the POSIX branch: child runs independently, caller is not blocked. */
|
||||||
|
char cmdline[8192];
|
||||||
|
snprintf(cmdline, sizeof(cmdline), "cmd.exe /c %s", cmd);
|
||||||
|
STARTUPINFOA si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si);
|
||||||
|
PROCESS_INFORMATION pi; ZeroMemory(&pi, sizeof(pi));
|
||||||
|
BOOL ok = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE,
|
||||||
|
DETACHED_PROCESS | CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
|
||||||
|
if (!ok) return el_wrap_str(el_strdup(""));
|
||||||
|
char pidbuf[32];
|
||||||
|
snprintf(pidbuf, sizeof(pidbuf), "%lu", (unsigned long)pi.dwProcessId);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
return el_wrap_str(el_strdup(pidbuf));
|
||||||
|
#else
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
/* fork failed */
|
/* fork failed */
|
||||||
@@ -2073,6 +2098,7 @@ el_val_t exec_bg(el_val_t cmdv) {
|
|||||||
char pidbuf[32];
|
char pidbuf[32];
|
||||||
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid);
|
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid);
|
||||||
return el_wrap_str(el_strdup(pidbuf));
|
return el_wrap_str(el_strdup(pidbuf));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
el_val_t fs_list(el_val_t pathv) {
|
el_val_t fs_list(el_val_t pathv) {
|
||||||
@@ -4337,7 +4363,12 @@ static int _el_decompose_earth(el_caltime_t* ct, struct tm* tm_out, int* abbr_le
|
|||||||
localtime_r(&s, &tm);
|
localtime_r(&s, &tm);
|
||||||
*tm_out = tm;
|
*tm_out = tm;
|
||||||
if (abbr_buf && abbr_cap > 0) {
|
if (abbr_buf && abbr_cap > 0) {
|
||||||
|
/* mingw's struct tm has no tm_zone (BSD/glibc extension); no abbrev available there. */
|
||||||
|
#ifdef _WIN32
|
||||||
|
const char* z_str = "";
|
||||||
|
#else
|
||||||
const char* z_str = tm.tm_zone ? tm.tm_zone : "";
|
const char* z_str = tm.tm_zone ? tm.tm_zone : "";
|
||||||
|
#endif
|
||||||
size_t n = strlen(z_str);
|
size_t n = strlen(z_str);
|
||||||
if (n >= abbr_cap) n = abbr_cap - 1;
|
if (n >= abbr_cap) n = abbr_cap - 1;
|
||||||
memcpy(abbr_buf, z_str, n);
|
memcpy(abbr_buf, z_str, n);
|
||||||
|
|||||||
@@ -52,6 +52,12 @@
|
|||||||
|
|
||||||
typedef int64_t el_val_t;
|
typedef int64_t el_val_t;
|
||||||
|
|
||||||
|
/* HTTP request-handler function-pointer types. Public because soul modules (routes/chat/etc.)
|
||||||
|
* register handlers across translation units; previously defined only inside el_runtime.c, which
|
||||||
|
* made cross-module references (and the Windows build) fail. Home in the shared header. */
|
||||||
|
typedef el_val_t (*http_handler_fn)(el_val_t method, el_val_t path, el_val_t body);
|
||||||
|
typedef el_val_t (*http_handler4_fn)(el_val_t method, el_val_t path, el_val_t body, el_val_t headers);
|
||||||
|
|
||||||
#define EL_STR(s) ((el_val_t)(uintptr_t)(s))
|
#define EL_STR(s) ((el_val_t)(uintptr_t)(s))
|
||||||
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
|
#define EL_CSTR(v) ((const char*)(uintptr_t)(v))
|
||||||
#define EL_INT(v) (v)
|
#define EL_INT(v) (v)
|
||||||
|
|||||||
Reference in New Issue
Block a user