feat(el-runtime): native Windows port of el_runtime.c (winsock/dlsym/CreateProcess)
Compiles for Windows x64 via mingw-w64 and still compiles clean on POSIX (darwin/linux) — all Windows code is behind #ifdef _WIN32, POSIX path unchanged. - el_platform_win.h (new): winsock2 + auto WSAStartup, el_closesocket(), dlsym->GetProcAddress, popen/_popen, mkdir/_mkdir, setenv/_putenv_s, timegm/_mkgmtime, localtime_r/gmtime_r. Threading unchanged — mingw winpthreads supplies <pthread.h> + -lpthread. - el_runtime.c: include block guarded; 10 socket-close sites -> el_closesocket(); setsockopt arg4 cast; tm_zone guarded; exec_bg fork/exec -> CreateProcess. Part of feat/windows-port. Core-el change, for Will's review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
|
||||
#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 <strings.h> /* strcasecmp */
|
||||
#include <stdint.h>
|
||||
@@ -42,6 +46,10 @@
|
||||
#include <dirent.h>
|
||||
#include <errno.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
|
||||
#include <curl/curl.h>
|
||||
#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);
|
||||
if (sock < 0) { perror("socket"); return 0; }
|
||||
int yes = 1; int no = 0;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no));
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons((uint16_t)p);
|
||||
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);
|
||||
while (1) {
|
||||
struct sockaddr_in6 cli;
|
||||
@@ -1614,11 +1622,11 @@ el_val_t http_serve(el_val_t port, el_val_t handler) {
|
||||
_http_conn_active++;
|
||||
pthread_mutex_unlock(&_http_conn_mu);
|
||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||
if (!arg) { close(cfd); continue; }
|
||||
if (!arg) { el_closesocket(cfd); continue; }
|
||||
arg->fd = cfd;
|
||||
pthread_t tid;
|
||||
if (pthread_create(&tid, NULL, http_worker, arg) != 0) {
|
||||
close(cfd); free(arg);
|
||||
el_closesocket(cfd); free(arg);
|
||||
pthread_mutex_lock(&_http_conn_mu);
|
||||
_http_conn_active--;
|
||||
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);
|
||||
}
|
||||
close(sock);
|
||||
el_closesocket(sock);
|
||||
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);
|
||||
if (sock < 0) { perror("socket"); return 0; }
|
||||
int yes = 1; int no = 0;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &no, sizeof(no));
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes));
|
||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&no, sizeof(no));
|
||||
struct sockaddr_in6 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
addr.sin6_addr = in6addr_any;
|
||||
addr.sin6_port = htons((uint16_t)p);
|
||||
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);
|
||||
while (1) {
|
||||
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++;
|
||||
pthread_mutex_unlock(&_http_conn_mu);
|
||||
HttpWorkerArg* arg = malloc(sizeof(HttpWorkerArg));
|
||||
if (!arg) { close(cfd); continue; }
|
||||
if (!arg) { el_closesocket(cfd); continue; }
|
||||
arg->fd = cfd;
|
||||
pthread_t tid;
|
||||
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);
|
||||
_http_conn_active--;
|
||||
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);
|
||||
}
|
||||
close(sock);
|
||||
el_closesocket(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2051,6 +2059,23 @@ el_val_t exec(el_val_t cmdv) {
|
||||
el_val_t exec_bg(el_val_t cmdv) {
|
||||
const char* cmd = EL_CSTR(cmdv);
|
||||
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();
|
||||
if (pid < 0) {
|
||||
/* fork failed */
|
||||
@@ -2073,6 +2098,7 @@ el_val_t exec_bg(el_val_t cmdv) {
|
||||
char pidbuf[32];
|
||||
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid);
|
||||
return el_wrap_str(el_strdup(pidbuf));
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
*tm_out = tm;
|
||||
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 : "";
|
||||
#endif
|
||||
size_t n = strlen(z_str);
|
||||
if (n >= abbr_cap) n = abbr_cap - 1;
|
||||
memcpy(abbr_buf, z_str, n);
|
||||
|
||||
Reference in New Issue
Block a user