536 lines
14 KiB
C
536 lines
14 KiB
C
#include "cell.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/sysctl.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <bcrypt.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <sys/utsname.h>
|
|
#include <dlfcn.h>
|
|
#ifdef __linux__
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/syscall.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
|
|
static JSClassID js_dylib_class_id;
|
|
|
|
static void js_dylib_finalizer(JSRuntime *rt, JSValue val) {
|
|
void *handle = JS_GetOpaque(val, js_dylib_class_id);
|
|
if (handle) {
|
|
#ifdef _WIN32
|
|
FreeLibrary((HMODULE)handle);
|
|
#else
|
|
dlclose(handle);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static JSClassDef js_dylib_class = {
|
|
"dylib",
|
|
.finalizer = js_dylib_finalizer,
|
|
};
|
|
|
|
#include <mach/mach_time.h>
|
|
|
|
uint64_t cell_ns()
|
|
{
|
|
#ifdef _WIN32
|
|
LARGE_INTEGER frequency, counter;
|
|
QueryPerformanceFrequency(&frequency);
|
|
QueryPerformanceCounter(&counter);
|
|
return (uint64_t)((double)counter.QuadPart / (double)frequency.QuadPart * 1e9);
|
|
#elif defined(__APPLE__)
|
|
static mach_timebase_info_data_t timebase = {0, 0};
|
|
if (timebase.denom == 0) {
|
|
mach_timebase_info(&timebase);
|
|
}
|
|
uint64_t time = mach_absolute_time();
|
|
return time * timebase.numer / timebase.denom;
|
|
#else
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
|
|
#endif
|
|
}
|
|
|
|
JSC_CCALL(os_now, return number2js(js, cell_ns()); )
|
|
|
|
void cell_sleep(double seconds)
|
|
{
|
|
#ifdef _WIN32
|
|
Sleep((DWORD)(seconds * 1000));
|
|
#else
|
|
struct timespec ts;
|
|
ts.tv_sec = (time_t)seconds;
|
|
ts.tv_nsec = (long)((seconds - ts.tv_sec) * 1000000000);
|
|
nanosleep(&ts, NULL);
|
|
#endif
|
|
}
|
|
JSC_CCALL(os_sleep,
|
|
double secs = js2number(js,argv[0]);
|
|
cell_sleep(secs);
|
|
)
|
|
|
|
static JSValue js_os_totalmem(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#ifdef _WIN32
|
|
MEMORYSTATUSEX statex;
|
|
statex.dwLength = sizeof(statex);
|
|
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
|
|
return JS_NewInt64(js,(int64_t)(statex.ullTotalPhys / (1024 * 1024)));
|
|
#elif defined(__linux__)
|
|
struct sysinfo info;
|
|
if (sysinfo(&info) == 0)
|
|
return JS_NewInt64(js,(int64_t)((info.totalram * info.mem_unit) / (1024 * 1024)));
|
|
return JS_NewInt64(js,0);
|
|
#elif defined(__APPLE__)
|
|
int mib[2] = {CTL_HW, HW_MEMSIZE};
|
|
int64_t memsize;
|
|
size_t len = sizeof(memsize);
|
|
if (sysctl(mib, 2, &memsize, &len, NULL, 0) == 0)
|
|
return JS_NewInt64(js, memsize / (1024 * 1024));
|
|
return JS_NewInt64(js,0);
|
|
#else
|
|
long pages = sysconf(_SC_PHYS_PAGES);
|
|
long page_size = sysconf(_SC_PAGE_SIZE);
|
|
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
|
|
return JS_NewInt64(js,(int64_t)((pages * page_size) / (1024 * 1024)));
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_os_platform(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#ifdef _WIN32
|
|
return JS_NewString(js,"Windows");
|
|
#elif defined(__linux__)
|
|
return JS_NewString(js,"Linux");
|
|
#elif defined(__APPLE__)
|
|
return JS_NewString(js,"macOS");
|
|
#elif defined(__FreeBSD__)
|
|
return JS_NewString(js,"FreeBSD");
|
|
#elif defined(__OpenBSD__)
|
|
return JS_NewString(js,"OpenBSD");
|
|
#elif defined(__NetBSD__)
|
|
return JS_NewString(js,"NetBSD");
|
|
#else
|
|
return JS_NewString(js,"Unknown");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_os_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#ifdef _WIN32
|
|
TCHAR buffer[256] = TEXT("");
|
|
DWORD size = sizeof(buffer) / sizeof(TCHAR);
|
|
GetComputerName(buffer, &size);
|
|
return JS_NewString(js, buffer);
|
|
#else
|
|
struct utsname buffer;
|
|
if (uname(&buffer) != 0) return JS_ThrowReferenceError(js,"Could not get hostname.");
|
|
return JS_NewString(js, buffer.nodename);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
return JS_NewString(js,"x64");
|
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
return JS_NewString(js,"arm64");
|
|
#elif defined(__arm__) || defined(_M_ARM)
|
|
return JS_NewString(js,"arm");
|
|
#elif defined(__i386__) || defined(_M_IX86)
|
|
return JS_NewString(js,"ia32");
|
|
#elif defined(__loongarch__) || defined(__loongarch32) || defined(__loongarch64)
|
|
return JS_NewString(js,"loong64");
|
|
#elif defined(__mips__) || defined(__mips) || defined(_M_MIPS)
|
|
// You might want to distinguish mips vs mipsel
|
|
return JS_NewString(js,"mips");
|
|
#elif defined(__ppc64__) || defined(__powerpc64__) || defined(_M_PPC)
|
|
// You might want to distinguish ppc vs ppc64, big-endian vs little-endian
|
|
return JS_NewString(js,"ppc64");
|
|
#elif defined(__riscv) && __riscv_xlen == 64
|
|
return JS_NewString(js,"riscv64");
|
|
#elif defined(__s390x__)
|
|
return JS_NewString(js,"s390x");
|
|
#else
|
|
return JS_NewString(js,"unknown");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#ifdef _WIN32
|
|
MEMORYSTATUSEX statex;
|
|
statex.dwLength = sizeof(statex);
|
|
if (!GlobalMemoryStatusEx(&statex)) return JS_ThrowInternalError(js,"GlobalMemoryStatusEx failed");
|
|
return JS_NewInt64(js,(int64_t)statex.ullAvailPhys);
|
|
#elif defined(__linux__)
|
|
struct sysinfo info;
|
|
if (sysinfo(&info) == 0)
|
|
return JS_NewInt64(js,(int64_t)info.freeram * info.mem_unit);
|
|
return JS_NewInt64(js,0);
|
|
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
|
// A very rough fallback using the same sysconf approach
|
|
// (macOS or *BSD typically need specialized APIs to get free mem accurately)
|
|
// This is often only "unused" pages, ignoring caches, etc.
|
|
long pages = sysconf(_SC_AVPHYS_PAGES);
|
|
long page_size = sysconf(_SC_PAGE_SIZE);
|
|
if (pages < 0 || page_size < 0) return JS_NewInt64(js,0);
|
|
return JS_NewInt64(js,(int64_t)pages * (int64_t)page_size);
|
|
#else
|
|
// Fallback: unknown
|
|
return JS_NewInt64(js,0);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
#ifdef _WIN32
|
|
typedef LONG (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
|
|
HMODULE h = GetModuleHandleA("ntdll.dll");
|
|
if (h) {
|
|
RtlGetVersionPtr fx = (RtlGetVersionPtr)GetProcAddress(h, "RtlGetVersion");
|
|
if (fx) {
|
|
RTL_OSVERSIONINFOW ver;
|
|
memset(&ver, 0, sizeof(ver));
|
|
ver.dwOSVersionInfoSize = sizeof(ver);
|
|
if (!fx(&ver)) {
|
|
char buf[128];
|
|
sprintf(buf, "%u.%u.%u",
|
|
(unsigned)ver.dwMajorVersion,
|
|
(unsigned)ver.dwMinorVersion,
|
|
(unsigned)ver.dwBuildNumber
|
|
);
|
|
return JS_NewString(js, buf);
|
|
}
|
|
}
|
|
}
|
|
OSVERSIONINFOW wver;
|
|
memset(&wver, 0, sizeof(wver));
|
|
wver.dwOSVersionInfoSize = sizeof(wver);
|
|
if (GetVersionExW(&wver)) {
|
|
char buf[128];
|
|
sprintf(buf, "%u.%u.%u",
|
|
(unsigned)wver.dwMajorVersion,
|
|
(unsigned)wver.dwMinorVersion,
|
|
(unsigned)wver.dwBuildNumber
|
|
);
|
|
return JS_NewString(js, buf);
|
|
}
|
|
return JS_NewString(js, "Windows_Unknown");
|
|
#else
|
|
struct utsname info;
|
|
if (!uname(&info)) return JS_NewString(js, info.release);
|
|
return JS_NewString(js, "");
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
JSC_CCALL(os_buffer2string,
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(js, "buffer2string expects an ArrayBuffer");
|
|
}
|
|
|
|
size_t len;
|
|
uint8_t *buf = js_get_blob_data(js, &len, argv[0]);
|
|
if (!buf) {
|
|
return JS_ThrowTypeError(js, "First argument must be an ArrayBuffer");
|
|
}
|
|
|
|
// Create a null-terminated string from the buffer
|
|
char *str = js_malloc(js, len + 1);
|
|
if (!str) {
|
|
return JS_ThrowInternalError(js, "Failed to allocate memory");
|
|
}
|
|
|
|
memcpy(str, buf, len);
|
|
str[len] = '\0';
|
|
|
|
JSValue result = JS_NewString(js, str);
|
|
js_free(js, str);
|
|
|
|
return result;
|
|
)
|
|
|
|
#define JSOBJ_ADD_FIELD(OBJ, STRUCT, FIELD, TYPE) \
|
|
JS_SetPropertyStr(js, OBJ, #FIELD, TYPE##2js(js,STRUCT.FIELD));\
|
|
|
|
#define JSJMEMRET(FIELD) JSOBJ_ADD_FIELD(ret, jsmem, FIELD, number)
|
|
|
|
JSC_CCALL(os_mallinfo,
|
|
ret = JS_NULL;
|
|
/*struct mallinfo jsmem = mallinfo();
|
|
ret = JS_NewObject(js);
|
|
JSJMEMRET(arena);
|
|
JSJMEMRET(ordblks);
|
|
JSJMEMRET(smblks);
|
|
JSJMEMRET(hblks);
|
|
JSJMEMRET(hblkhd);
|
|
JSJMEMRET(usmblks);
|
|
JSJMEMRET(fsmblks);
|
|
JSJMEMRET(uordblks);
|
|
JSJMEMRET(fordblks);
|
|
JSJMEMRET(keepcost);*/
|
|
)
|
|
|
|
static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
JSValue ret = JS_NULL;
|
|
ret = JS_NewObject(js);
|
|
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
struct rusage jsmem;
|
|
getrusage(RUSAGE_SELF, &jsmem);
|
|
JSJMEMRET(ru_maxrss);
|
|
JSJMEMRET(ru_ixrss);
|
|
JSJMEMRET(ru_idrss);
|
|
JSJMEMRET(ru_isrss);
|
|
JSJMEMRET(ru_minflt);
|
|
JSJMEMRET(ru_majflt);
|
|
JSJMEMRET(ru_nswap);
|
|
JSJMEMRET(ru_inblock);
|
|
JSJMEMRET(ru_oublock);
|
|
JSJMEMRET(ru_msgsnd);
|
|
JSJMEMRET(ru_msgrcv);
|
|
JSJMEMRET(ru_nsignals);
|
|
JSJMEMRET(ru_nvcsw);
|
|
JSJMEMRET(ru_nivcsw);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
JSC_SCALL(os_system,
|
|
int err = system(str);
|
|
ret = number2js(js,err);
|
|
)
|
|
|
|
JSC_CCALL(os_exit,
|
|
exit(0);
|
|
)
|
|
|
|
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
if (argc < 1) {
|
|
return JS_ThrowTypeError(js, "dylib_open requires a path argument");
|
|
}
|
|
|
|
const char *path = JS_ToCString(js, argv[0]);
|
|
if (!path) {
|
|
return JS_ThrowTypeError(js, "path must be a string");
|
|
}
|
|
|
|
void *handle;
|
|
#ifdef _WIN32
|
|
handle = LoadLibraryA(path);
|
|
#else
|
|
handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
|
|
#endif
|
|
|
|
JS_FreeCString(js, path);
|
|
|
|
if (!handle) {
|
|
const char *error_msg = "Could not load library";
|
|
#ifndef _WIN32
|
|
const char *dl_error = dlerror();
|
|
if (dl_error) {
|
|
error_msg = dl_error;
|
|
}
|
|
#endif
|
|
return JS_ThrowReferenceError(js, "Failed to load library: %s", error_msg);
|
|
}
|
|
|
|
JSValue dylib_obj = JS_NewObjectClass(js, js_dylib_class_id);
|
|
if (JS_IsException(dylib_obj)) {
|
|
#ifdef _WIN32
|
|
FreeLibrary((HMODULE)handle);
|
|
#else
|
|
dlclose(handle);
|
|
#endif
|
|
return dylib_obj;
|
|
}
|
|
|
|
JS_SetOpaque(dylib_obj, handle);
|
|
return dylib_obj;
|
|
}
|
|
|
|
static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
if (argc < 2) {
|
|
return JS_ThrowTypeError(js, "dylib_symbol requires dylib object and symbol name");
|
|
}
|
|
|
|
void *handle = JS_GetOpaque(argv[0], js_dylib_class_id);
|
|
if (!handle) {
|
|
return JS_ThrowTypeError(js, "First argument must be a dylib object");
|
|
}
|
|
|
|
const char *symbol_name = JS_ToCString(js, argv[1]);
|
|
if (!symbol_name) {
|
|
return JS_ThrowTypeError(js, "symbol name must be a string");
|
|
}
|
|
|
|
JSValue (*symbol)(JSContext *js);
|
|
#ifdef _WIN32
|
|
symbol = GetProcAddress((HMODULE)handle, symbol_name);
|
|
#else
|
|
symbol = dlsym(handle, symbol_name);
|
|
#endif
|
|
|
|
JS_FreeCString(js, symbol_name);
|
|
|
|
if (!symbol) {
|
|
const char *error_msg = "Symbol not found";
|
|
#ifndef _WIN32
|
|
const char *dl_error = dlerror();
|
|
if (dl_error) {
|
|
error_msg = dl_error;
|
|
}
|
|
#endif
|
|
return JS_ThrowReferenceError(js, "Failed to get symbol: %s", error_msg);
|
|
}
|
|
|
|
// Return the symbol as a pointer value
|
|
return symbol(js);
|
|
}
|
|
|
|
JSC_CCALL(os_print,
|
|
size_t len;
|
|
const char *str = JS_ToCStringLen(js, &len, argv[0]);
|
|
printf("%.*s", (int)len, str);
|
|
JS_FreeCString(js, str);
|
|
)
|
|
|
|
static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
void *handle;
|
|
#ifdef _WIN32
|
|
handle = GetModuleHandle(NULL);
|
|
#else
|
|
handle = dlopen(NULL, RTLD_LAZY);
|
|
#endif
|
|
if (argc < 1)
|
|
return JS_ThrowTypeError(js, "load_internal requires a symbol name");
|
|
|
|
const char *symbol_name = JS_ToCString(js, argv[0]);
|
|
if (!symbol_name)
|
|
return JS_ThrowTypeError(js, "symbol name must be a string");
|
|
|
|
JSValue (*symbol)(JSContext *js);
|
|
#if defined(_WIN32)
|
|
symbol = GetProcAddress((HMODULE)handle, symbol_name);
|
|
#else
|
|
symbol = dlsym(handle, symbol_name);
|
|
#endif
|
|
|
|
JS_FreeCString(js, symbol_name);
|
|
|
|
if (!symbol)
|
|
return JS_NULL;
|
|
|
|
return symbol(js);
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
// ------- Windows: use BCryptGenRandom -------
|
|
int randombytes(void *buf, size_t n) {
|
|
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)n, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
return (status == 0) ? 0 : -1;
|
|
}
|
|
|
|
#elif defined(__linux__)
|
|
// ------- Linux: try getrandom, fall back to /dev/urandom -------
|
|
static int randombytes_fallback(void *buf, size_t n) {
|
|
int fd = open("/dev/urandom", O_RDONLY);
|
|
if (fd < 0) return -1;
|
|
ssize_t r = read(fd, buf, n);
|
|
close(fd);
|
|
return (r == (ssize_t)n) ? 0 : -1;
|
|
}
|
|
|
|
int randombytes(void *buf, size_t n) {
|
|
#ifdef SYS_getrandom
|
|
// Try getrandom(2) if available
|
|
ssize_t ret = syscall(SYS_getrandom, buf, n, 0);
|
|
if (ret < 0) {
|
|
// If getrandom is not supported or fails, fall back
|
|
if (errno == ENOSYS) {
|
|
return randombytes_fallback(buf, n);
|
|
}
|
|
return -1;
|
|
}
|
|
return (ret == (ssize_t)n) ? 0 : -1;
|
|
#else
|
|
// getrandom not available, just fallback
|
|
return randombytes_fallback(buf, n);
|
|
#endif
|
|
}
|
|
|
|
#else
|
|
// ------- Other Unix: read from /dev/urandom -------
|
|
static int rand_fd = -1;
|
|
int randombytes(void *buf, size_t n) {
|
|
if (rand_fd < 0) {
|
|
rand_fd = open("/dev/urandom", O_RDONLY);
|
|
if (rand_fd < 0) return -1;
|
|
}
|
|
ssize_t r = read(rand_fd, buf, n);
|
|
return (r == (ssize_t)n) ? 0 : -1;
|
|
}
|
|
#endif
|
|
|
|
JSC_CCALL(os_random,
|
|
return JS_NewFloat64(js, cell_random_fit());
|
|
)
|
|
|
|
static const JSCFunctionListEntry js_os_funcs[] = {
|
|
MIST_FUNC_DEF(os, platform, 0),
|
|
MIST_FUNC_DEF(os, arch, 0),
|
|
MIST_FUNC_DEF(os, totalmem, 0),
|
|
MIST_FUNC_DEF(os, freemem, 0),
|
|
MIST_FUNC_DEF(os, hostname, 0),
|
|
MIST_FUNC_DEF(os, version, 0),
|
|
MIST_FUNC_DEF(os, now, 0),
|
|
MIST_FUNC_DEF(os, rusage, 0),
|
|
MIST_FUNC_DEF(os, mallinfo, 0),
|
|
MIST_FUNC_DEF(os, buffer2string, 1),
|
|
MIST_FUNC_DEF(os, system, 1),
|
|
MIST_FUNC_DEF(os, exit, 0),
|
|
MIST_FUNC_DEF(os, sleep, 1),
|
|
MIST_FUNC_DEF(os, dylib_open, 1),
|
|
MIST_FUNC_DEF(os, dylib_symbol, 2),
|
|
MIST_FUNC_DEF(os, load_internal, 1),
|
|
MIST_FUNC_DEF(os, print, 1),
|
|
MIST_FUNC_DEF(os, random, 0),
|
|
};
|
|
|
|
JSValue js_os_use(JSContext *js) {
|
|
JS_NewClassID(&js_dylib_class_id);
|
|
JS_NewClass(JS_GetRuntime(js), js_dylib_class_id, &js_dylib_class);
|
|
|
|
JSValue mod = JS_NewObject(js);
|
|
JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs));
|
|
return mod;
|
|
}
|