configurable trans-membrance hostcall blocklist
This commit is contained in:
+17
-3
@@ -5,12 +5,13 @@
|
||||
#include <cstdlib>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
const u64 UCE_UNIT_ABI_VERSION = 7;
|
||||
const u64 UCE_UNIT_ABI_VERSION = 8;
|
||||
|
||||
struct SharedUnitFilesystemState
|
||||
{
|
||||
@@ -210,15 +211,28 @@ int compiler_open_lock_file(String file_name, String purpose)
|
||||
auto lock_dir = dirname(file_name);
|
||||
if(lock_dir != "")
|
||||
mkdir(lock_dir);
|
||||
int fdlock = file_open_locked(file_name, O_RDWR | O_CREAT, LOCK_EX, 0666);
|
||||
int fdlock = open(file_name.c_str(), O_RDWR | O_CREAT, 0666);
|
||||
if(fdlock == -1)
|
||||
{
|
||||
printf("(!) Could not open lock file %s\n", file_name.c_str());
|
||||
return(fdlock);
|
||||
}
|
||||
fcntl(fdlock, F_SETFD, FD_CLOEXEC);
|
||||
if(flock(fdlock, LOCK_EX) != 0)
|
||||
{
|
||||
close(fdlock);
|
||||
printf("(!) Could not lock file %s\n", file_name.c_str());
|
||||
return(-1);
|
||||
}
|
||||
return(fdlock);
|
||||
}
|
||||
|
||||
void compiler_close_lock_file(int fdlock)
|
||||
{
|
||||
file_close_locked(fdlock);
|
||||
if(fdlock == -1)
|
||||
return;
|
||||
flock(fdlock, LOCK_UN);
|
||||
close(fdlock);
|
||||
}
|
||||
|
||||
String compiler_normalize_unit_path(Request* context, String file_name)
|
||||
|
||||
@@ -344,3 +344,80 @@ f64 draw_float(f64 from, f64 to, f64 decimal_precision)
|
||||
return(gen_float(from, to, context->random_index++, context->random_seed, decimal_precision));
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
struct SHA256_CTX_UCE { u8 data[64]; u32 datalen; unsigned long long bitlen; u32 state[8]; };
|
||||
#define UCE_SHA256_ROTR(a,b) (((a) >> (b)) | ((a) << (32-(b))))
|
||||
#define UCE_SHA256_CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
|
||||
#define UCE_SHA256_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||
#define UCE_SHA256_EP0(x) (UCE_SHA256_ROTR(x,2) ^ UCE_SHA256_ROTR(x,13) ^ UCE_SHA256_ROTR(x,22))
|
||||
#define UCE_SHA256_EP1(x) (UCE_SHA256_ROTR(x,6) ^ UCE_SHA256_ROTR(x,11) ^ UCE_SHA256_ROTR(x,25))
|
||||
#define UCE_SHA256_SIG0(x) (UCE_SHA256_ROTR(x,7) ^ UCE_SHA256_ROTR(x,18) ^ ((x) >> 3))
|
||||
#define UCE_SHA256_SIG1(x) (UCE_SHA256_ROTR(x,17) ^ UCE_SHA256_ROTR(x,19) ^ ((x) >> 10))
|
||||
static const u32 uce_sha256_k[64] = {
|
||||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5,
|
||||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,
|
||||
0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
|
||||
0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967,
|
||||
0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,
|
||||
0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
|
||||
0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3,
|
||||
0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 };
|
||||
static void uce_sha256_transform(SHA256_CTX_UCE* ctx, const u8 data[])
|
||||
{
|
||||
u32 m[64];
|
||||
for(u32 i=0,j=0; i<16; ++i,j+=4) m[i]=((u32)data[j]<<24)|((u32)data[j+1]<<16)|((u32)data[j+2]<<8)|((u32)data[j+3]);
|
||||
for(u32 i=16; i<64; ++i) m[i]=UCE_SHA256_SIG1(m[i-2])+m[i-7]+UCE_SHA256_SIG0(m[i-15])+m[i-16];
|
||||
u32 a=ctx->state[0],b=ctx->state[1],c=ctx->state[2],d=ctx->state[3],e=ctx->state[4],f=ctx->state[5],g=ctx->state[6],h=ctx->state[7];
|
||||
for(u32 i=0; i<64; ++i) { u32 t1=h+UCE_SHA256_EP1(e)+UCE_SHA256_CH(e,f,g)+uce_sha256_k[i]+m[i]; u32 t2=UCE_SHA256_EP0(a)+UCE_SHA256_MAJ(a,b,c); h=g; g=f; f=e; e=d+t1; d=c; c=b; b=a; a=t1+t2; }
|
||||
ctx->state[0]+=a; ctx->state[1]+=b; ctx->state[2]+=c; ctx->state[3]+=d; ctx->state[4]+=e; ctx->state[5]+=f; ctx->state[6]+=g; ctx->state[7]+=h;
|
||||
}
|
||||
static void uce_sha256_init(SHA256_CTX_UCE* ctx)
|
||||
{
|
||||
ctx->datalen=0; ctx->bitlen=0; ctx->state[0]=0x6a09e667; ctx->state[1]=0xbb67ae85; ctx->state[2]=0x3c6ef372; ctx->state[3]=0xa54ff53a; ctx->state[4]=0x510e527f; ctx->state[5]=0x9b05688c; ctx->state[6]=0x1f83d9ab; ctx->state[7]=0x5be0cd19;
|
||||
}
|
||||
static void uce_sha256_update(SHA256_CTX_UCE* ctx, const u8 data[], size_t len)
|
||||
{
|
||||
for(size_t i=0; i<len; ++i) { ctx->data[ctx->datalen++]=data[i]; if(ctx->datalen==64) { uce_sha256_transform(ctx,ctx->data); ctx->bitlen += 512; ctx->datalen=0; } }
|
||||
}
|
||||
static void uce_sha256_final(SHA256_CTX_UCE* ctx, u8 hash[])
|
||||
{
|
||||
u32 i=ctx->datalen;
|
||||
ctx->data[i++]=0x80;
|
||||
if(i>56) { while(i<64) ctx->data[i++]=0; uce_sha256_transform(ctx,ctx->data); i=0; }
|
||||
while(i<56) ctx->data[i++]=0;
|
||||
ctx->bitlen += (unsigned long long)ctx->datalen * 8ull;
|
||||
for(int j=7; j>=0; --j) ctx->data[63-j]=(u8)(ctx->bitlen >> (j*8));
|
||||
uce_sha256_transform(ctx,ctx->data);
|
||||
for(i=0; i<4; ++i) for(u32 j=0; j<8; ++j) hash[i + j*4] = (u8)((ctx->state[j] >> (24 - i*8)) & 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
String sha256_native(String data)
|
||||
{
|
||||
u8 digest[32]; SHA256_CTX_UCE ctx; uce_sha256_init(&ctx); uce_sha256_update(&ctx, (const u8*)data.data(), data.size()); uce_sha256_final(&ctx, digest);
|
||||
return(String((const char*)digest, 32));
|
||||
}
|
||||
String sha256_hex_native(String data)
|
||||
{
|
||||
String digest = sha256_native(data), out; for(unsigned char c : digest) out += to_hex(c, 2); return(to_lower(out));
|
||||
}
|
||||
String hmac_sha256_native(String key, String data)
|
||||
{
|
||||
if(key.size() > 64) key = sha256_native(key);
|
||||
key.resize(64, '\0');
|
||||
String o(64, '\0'), i(64, '\0');
|
||||
for(size_t n=0; n<64; n++) { o[n] = key[n] ^ 0x5c; i[n] = key[n] ^ 0x36; }
|
||||
return(sha256_native(o + sha256_native(i + data)));
|
||||
}
|
||||
String hmac_sha256_hex_native(String key, String data)
|
||||
{
|
||||
String digest = hmac_sha256_native(key, data), out; for(unsigned char c : digest) out += to_hex(c, 2); return(to_lower(out));
|
||||
}
|
||||
bool crypto_equal_native(String a, String b)
|
||||
{
|
||||
u8 diff = (u8)(a.size() ^ b.size());
|
||||
size_t n = a.size() > b.size() ? a.size() : b.size();
|
||||
for(size_t i=0; i<n; i++) { u8 ca = i<a.size() ? (u8)a[i] : 0; u8 cb = i<b.size() ? (u8)b[i] : 0; diff |= ca ^ cb; }
|
||||
return(diff == 0);
|
||||
}
|
||||
|
||||
@@ -19,3 +19,13 @@ f64 gen_float(f64 from, f64 to, u64 index, u64 seed = 0, f64 decimal_precision =
|
||||
|
||||
u64 draw_int(u64 from, u64 to);
|
||||
f64 draw_float(f64 from, f64 to, f64 decimal_precision = 0.000000000001);
|
||||
String sha256_native(String data);
|
||||
String sha256_hex_native(String data);
|
||||
String hmac_sha256_native(String key, String data);
|
||||
String hmac_sha256_hex_native(String key, String data);
|
||||
bool crypto_equal_native(String a, String b);
|
||||
String sha256(String data);
|
||||
String sha256_hex(String data);
|
||||
String hmac_sha256(String key, String data);
|
||||
String hmac_sha256_hex(String key, String data);
|
||||
bool crypto_equal(String a, String b);
|
||||
|
||||
+311
-95
@@ -22,6 +22,25 @@ void uce_host_file_unlink(const char* path, size_t path_len, const char* current
|
||||
size_t uce_host_file_list(const char* path, size_t path_len, const char* current, size_t current_len, char* buf, size_t cap);
|
||||
int uce_host_file_mkdir(const char* path, size_t path_len, const char* current, size_t current_len);
|
||||
int64_t uce_host_file_mtime(const char* path, size_t path_len, const char* current, size_t current_len);
|
||||
uint64_t uce_host_file_open(const char* path, size_t path_len, const char* current, size_t current_len, const char* mode, size_t mode_len);
|
||||
size_t uce_host_file_handle_read(uint64_t handle, uint64_t len, char* buf, size_t cap);
|
||||
size_t uce_host_file_handle_pread(uint64_t handle, uint64_t offset, uint64_t len, char* buf, size_t cap);
|
||||
uint64_t uce_host_file_handle_write(uint64_t handle, const char* data, size_t data_len);
|
||||
uint64_t uce_host_file_handle_pwrite(uint64_t handle, uint64_t offset, const char* data, size_t data_len);
|
||||
int64_t uce_host_file_handle_seek(uint64_t handle, int64_t offset, int whence);
|
||||
int64_t uce_host_file_handle_tell(uint64_t handle);
|
||||
void uce_host_file_handle_close(uint64_t handle);
|
||||
size_t uce_host_file_stat(const char* path, size_t path_len, const char* current, size_t current_len, char* buf, size_t cap);
|
||||
size_t uce_host_dir_list(const char* path, size_t path_len, const char* current, size_t current_len, char* buf, size_t cap);
|
||||
int uce_host_file_rename(const char* from, size_t from_len, const char* to, size_t to_len, const char* current, size_t current_len);
|
||||
int uce_host_file_copy(const char* from, size_t from_len, const char* to, size_t to_len, const char* current, size_t current_len);
|
||||
int uce_host_file_truncate(const char* path, size_t path_len, const char* current, size_t current_len, uint64_t size);
|
||||
int uce_host_dir_remove(const char* path, size_t path_len, const char* current, size_t current_len, int recursive);
|
||||
size_t uce_host_file_temp(const char* prefix, size_t prefix_len, const char* current, size_t current_len, char* buf, size_t cap);
|
||||
int uce_host_file_chmod(const char* path, size_t path_len, const char* current, size_t current_len, uint32_t mode);
|
||||
int uce_host_file_symlink(const char* target, size_t target_len, const char* linkpath, size_t linkpath_len, const char* current, size_t current_len);
|
||||
size_t uce_host_file_readlink(const char* path, size_t path_len, const char* current, size_t current_len, char* buf, size_t cap);
|
||||
int uce_host_file_fsync(uint64_t handle);
|
||||
int uce_host_task_spawn(const char* key, size_t key_len, uint64_t callback_id, double interval, uint64_t timeout, int repeat);
|
||||
int uce_host_task_pid(const char* key, size_t key_len);
|
||||
int uce_host_task_kill(int pid, int sig);
|
||||
@@ -36,11 +55,21 @@ size_t uce_host_memcache_command(uint64_t sockfd, const char* command, size_t co
|
||||
size_t uce_host_mysql(const char* in, size_t in_len, char* out, size_t cap);
|
||||
size_t uce_host_request_perf(const char* in, size_t in_len, char* out, size_t cap);
|
||||
size_t uce_host_shell_exec(const char* cmd, size_t cmd_len, char* buf, size_t cap);
|
||||
int uce_host_file_open_locked(const char* path, size_t path_len, int open_flags, int lock_type, int create_mode, double wait_timeout_seconds, const char* purpose, size_t purpose_len, const char* current, size_t current_len);
|
||||
void uce_host_file_close_locked(int handle);
|
||||
void uce_host_file_release_process_locks(const char* reason, size_t reason_len);
|
||||
size_t uce_host_file_read_locked_fd(int handle, char* buf, size_t cap);
|
||||
int uce_host_file_write_locked_fd(int handle, const char* content, size_t content_len);
|
||||
size_t uce_host_sha256(const char* data, size_t data_len, char* out, size_t cap);
|
||||
size_t uce_host_sha256_hex(const char* data, size_t data_len, char* out, size_t cap);
|
||||
size_t uce_host_hmac_sha256(const char* key, size_t key_len, const char* data, size_t data_len, char* out, size_t cap);
|
||||
size_t uce_host_hmac_sha256_hex(const char* key, size_t key_len, const char* data, size_t data_len, char* out, size_t cap);
|
||||
size_t uce_host_base64_encode(const char* data, size_t data_len, char* out, size_t cap);
|
||||
size_t uce_host_base64_decode(const char* data, size_t data_len, char* out, size_t cap);
|
||||
int uce_host_crypto_equal(const char* a, size_t a_len, const char* b, size_t b_len);
|
||||
size_t uce_host_http_request(const char* in, size_t in_len, char* out, size_t cap);
|
||||
uint64_t uce_host_http_request_async(const char* in, size_t in_len);
|
||||
size_t uce_host_shell_exec_dv(const char* in, size_t in_len, char* out, size_t cap);
|
||||
uint64_t uce_host_shell_spawn(const char* in, size_t in_len);
|
||||
size_t uce_host_job_status(uint64_t job_id, char* out, size_t cap);
|
||||
size_t uce_host_job_result(uint64_t job_id, char* out, size_t cap);
|
||||
size_t uce_host_job_await(uint64_t job_id, uint64_t timeout_ms, char* out, size_t cap);
|
||||
int uce_host_job_cancel(uint64_t job_id);
|
||||
size_t uce_host_path_real(const char* path, size_t path_len, char* buf, size_t cap);
|
||||
int uce_host_path_is_within(const char* path, size_t path_len, const char* root, size_t root_len);
|
||||
size_t uce_host_cwd_get(char* buf, size_t cap);
|
||||
@@ -103,30 +132,6 @@ bool file_exists(String path)
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_exists(path.data(), path.size(), current.data(), current.size()) != 0);
|
||||
}
|
||||
int file_open_locked(String file_name, int open_flags, int lock_type, int create_mode, f64 wait_timeout_seconds, String purpose)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_open_locked(
|
||||
file_name.data(), file_name.size(), open_flags, lock_type, create_mode, wait_timeout_seconds,
|
||||
purpose.data(), purpose.size(), current.data(), current.size()
|
||||
));
|
||||
}
|
||||
void file_close_locked(int fd) { uce_host_file_close_locked(fd); }
|
||||
void file_release_process_locks(String reason) { uce_host_file_release_process_locks(reason.data(), reason.size()); }
|
||||
String file_get_contents_locked_fd(int fd)
|
||||
{
|
||||
size_t required = uce_host_file_read_locked_fd(fd, 0, 0);
|
||||
if(required == 0)
|
||||
return("");
|
||||
String content(required, 0);
|
||||
size_t got = uce_host_file_read_locked_fd(fd, &content[0], required);
|
||||
content.resize(got <= required ? got : 0);
|
||||
return(content);
|
||||
}
|
||||
bool file_put_contents_locked_fd(int fd, String content)
|
||||
{
|
||||
return(uce_host_file_write_locked_fd(fd, content.data(), content.size()) != 0);
|
||||
}
|
||||
String file_get_contents(String file_name)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
@@ -143,11 +148,200 @@ bool file_put_contents(String file_name, String content)
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_write(file_name.data(), file_name.size(), current.data(), current.size(), content.data(), content.size(), 0) != 0);
|
||||
}
|
||||
bool file_append_contents(String file_name, String content)
|
||||
bool file_append(String file_name, String content)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_write(file_name.data(), file_name.size(), current.data(), current.size(), content.data(), content.size(), 1) != 0);
|
||||
}
|
||||
u64 file_open(String path, String mode)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return((u64)uce_host_file_open(path.data(), path.size(), current.data(), current.size(), mode.data(), mode.size()));
|
||||
}
|
||||
String file_read(u64 h, u64 len)
|
||||
{
|
||||
size_t required = uce_host_file_handle_read(h, len, 0, 0);
|
||||
if(required == 0)
|
||||
return("");
|
||||
String content(required, 0);
|
||||
size_t got = uce_host_file_handle_read(h, len, &content[0], required);
|
||||
content.resize(got <= required ? got : 0);
|
||||
return(content);
|
||||
}
|
||||
String file_pread(u64 h, u64 offset, u64 len)
|
||||
{
|
||||
size_t required = uce_host_file_handle_pread(h, offset, len, 0, 0);
|
||||
if(required == 0)
|
||||
return("");
|
||||
String content(required, 0);
|
||||
size_t got = uce_host_file_handle_pread(h, offset, len, &content[0], required);
|
||||
content.resize(got <= required ? got : 0);
|
||||
return(content);
|
||||
}
|
||||
u64 file_write(u64 h, String data) { return(uce_host_file_handle_write(h, data.data(), data.size())); }
|
||||
u64 file_pwrite(u64 h, u64 offset, String data) { return(uce_host_file_handle_pwrite(h, offset, data.data(), data.size())); }
|
||||
s64 file_seek(u64 h, s64 offset, int whence) { return((s64)uce_host_file_handle_seek(h, offset, whence)); }
|
||||
s64 file_tell(u64 h) { return((s64)uce_host_file_handle_tell(h)); }
|
||||
void file_close(u64 h) { uce_host_file_handle_close(h); }
|
||||
static DValue wasm_decode_dvalue_result(String encoded)
|
||||
{
|
||||
DValue out;
|
||||
String error;
|
||||
ucb_decode(encoded, out, &error);
|
||||
return(out);
|
||||
}
|
||||
DValue file_stat(String path)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
size_t required = uce_host_file_stat(path.data(), path.size(), current.data(), current.size(), 0, 0);
|
||||
String encoded(required, 0);
|
||||
size_t got = required ? uce_host_file_stat(path.data(), path.size(), current.data(), current.size(), &encoded[0], required) : 0;
|
||||
encoded.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(encoded));
|
||||
}
|
||||
DValue dir_list(String path)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
size_t required = uce_host_dir_list(path.data(), path.size(), current.data(), current.size(), 0, 0);
|
||||
String encoded(required, 0);
|
||||
size_t got = required ? uce_host_dir_list(path.data(), path.size(), current.data(), current.size(), &encoded[0], required) : 0;
|
||||
encoded.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(encoded));
|
||||
}
|
||||
bool file_rename(String from, String to)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_rename(from.data(), from.size(), to.data(), to.size(), current.data(), current.size()) != 0);
|
||||
}
|
||||
bool file_copy(String from, String to)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_copy(from.data(), from.size(), to.data(), to.size(), current.data(), current.size()) != 0);
|
||||
}
|
||||
bool file_truncate(String path, u64 size)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_truncate(path.data(), path.size(), current.data(), current.size(), size) != 0);
|
||||
}
|
||||
bool dir_remove(String path, bool recursive)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_dir_remove(path.data(), path.size(), current.data(), current.size(), recursive ? 1 : 0) != 0);
|
||||
}
|
||||
String file_temp(String prefix)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
size_t required = uce_host_file_temp(prefix.data(), prefix.size(), current.data(), current.size(), 0, 0);
|
||||
if(required == 0) return("");
|
||||
String out(required, 0);
|
||||
size_t got = uce_host_file_temp(prefix.data(), prefix.size(), current.data(), current.size(), &out[0], required);
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(out);
|
||||
}
|
||||
bool file_chmod(String path, u32 mode)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_chmod(path.data(), path.size(), current.data(), current.size(), mode) != 0);
|
||||
}
|
||||
bool file_symlink(String target, String linkpath)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
return(uce_host_file_symlink(target.data(), target.size(), linkpath.data(), linkpath.size(), current.data(), current.size()) != 0);
|
||||
}
|
||||
String file_readlink(String path)
|
||||
{
|
||||
String current = wasm_current_unit_file();
|
||||
size_t required = uce_host_file_readlink(path.data(), path.size(), current.data(), current.size(), 0, 0);
|
||||
if(required == 0) return("");
|
||||
String out(required, 0);
|
||||
size_t got = uce_host_file_readlink(path.data(), path.size(), current.data(), current.size(), &out[0], required);
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(out);
|
||||
}
|
||||
bool file_fsync(u64 h) { return(uce_host_file_fsync(h) != 0); }
|
||||
|
||||
|
||||
static String wasm_string_hostcall_1(size_t (*fn)(const char*, size_t, char*, size_t), String data)
|
||||
{
|
||||
size_t required = fn(data.data(), data.size(), 0, 0);
|
||||
String out(required, 0);
|
||||
size_t got = required ? fn(data.data(), data.size(), &out[0], required) : 0;
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(out);
|
||||
}
|
||||
String sha256(String data) { return(wasm_string_hostcall_1(uce_host_sha256, data)); }
|
||||
String sha256_hex(String data) { return(wasm_string_hostcall_1(uce_host_sha256_hex, data)); }
|
||||
String hmac_sha256(String key, String data)
|
||||
{
|
||||
size_t required = uce_host_hmac_sha256(key.data(), key.size(), data.data(), data.size(), 0, 0);
|
||||
String out(required, 0); size_t got = required ? uce_host_hmac_sha256(key.data(), key.size(), data.data(), data.size(), &out[0], required) : 0; out.resize(got <= required ? got : 0); return(out);
|
||||
}
|
||||
String hmac_sha256_hex(String key, String data)
|
||||
{
|
||||
size_t required = uce_host_hmac_sha256_hex(key.data(), key.size(), data.data(), data.size(), 0, 0);
|
||||
String out(required, 0); size_t got = required ? uce_host_hmac_sha256_hex(key.data(), key.size(), data.data(), data.size(), &out[0], required) : 0; out.resize(got <= required ? got : 0); return(out);
|
||||
}
|
||||
String base64_decode(String raw) { return(wasm_string_hostcall_1(uce_host_base64_decode, raw)); }
|
||||
String random_bytes(u64 n)
|
||||
{
|
||||
if(n > 1024 * 1024) n = 1024 * 1024;
|
||||
String out(n, 0); size_t got = n ? uce_host_random(&out[0], n) : 0; out.resize(got <= n ? got : 0); return(out);
|
||||
}
|
||||
bool crypto_equal(String a, String b) { return(uce_host_crypto_equal(a.data(), a.size(), b.data(), b.size()) != 0); }
|
||||
|
||||
|
||||
DValue http_request(DValue req)
|
||||
{
|
||||
String encoded = ucb_encode(req);
|
||||
size_t required = uce_host_http_request(encoded.data(), encoded.size(), 0, 0);
|
||||
String out(required, 0); size_t got = required ? uce_host_http_request(encoded.data(), encoded.size(), &out[0], required) : 0; out.resize(got <= required ? got : 0); return(wasm_decode_dvalue_result(out));
|
||||
}
|
||||
u64 http_request_async(DValue req)
|
||||
{
|
||||
String encoded = ucb_encode(req);
|
||||
return((u64)uce_host_http_request_async(encoded.data(), encoded.size()));
|
||||
}
|
||||
|
||||
DValue shell_exec(DValue spec)
|
||||
{
|
||||
String encoded = ucb_encode(spec);
|
||||
size_t required = uce_host_shell_exec_dv(encoded.data(), encoded.size(), 0, 0);
|
||||
String out(required, 0);
|
||||
size_t got = required ? uce_host_shell_exec_dv(encoded.data(), encoded.size(), &out[0], required) : 0;
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(out));
|
||||
}
|
||||
u64 shell_spawn(DValue spec)
|
||||
{
|
||||
String encoded = ucb_encode(spec);
|
||||
return((u64)uce_host_shell_spawn(encoded.data(), encoded.size()));
|
||||
}
|
||||
DValue job_status(u64 job_id)
|
||||
{
|
||||
size_t required = uce_host_job_status(job_id, 0, 0);
|
||||
String out(required, 0);
|
||||
size_t got = required ? uce_host_job_status(job_id, &out[0], required) : 0;
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(out));
|
||||
}
|
||||
DValue job_result(u64 job_id)
|
||||
{
|
||||
size_t required = uce_host_job_result(job_id, 0, 0);
|
||||
String out(required, 0);
|
||||
size_t got = required ? uce_host_job_result(job_id, &out[0], required) : 0;
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(out));
|
||||
}
|
||||
DValue job_await(u64 job_id, u64 timeout_ms)
|
||||
{
|
||||
size_t required = uce_host_job_await(job_id, timeout_ms, 0, 0);
|
||||
String out(required, 0);
|
||||
size_t got = required ? uce_host_job_await(job_id, timeout_ms, &out[0], required) : 0;
|
||||
out.resize(got <= required ? got : 0);
|
||||
return(wasm_decode_dvalue_result(out));
|
||||
}
|
||||
bool job_cancel(u64 job_id) { return(uce_host_job_cancel(job_id) != 0); }
|
||||
|
||||
String cwd_get()
|
||||
{
|
||||
size_t required = uce_host_cwd_get(0, 0);
|
||||
@@ -522,6 +716,15 @@ StringMap default_config()
|
||||
#include <errno.h>
|
||||
#include "sys.h"
|
||||
#include "hash.h"
|
||||
#include "uri.h"
|
||||
|
||||
String sha256(String data) { return(sha256_native(data)); }
|
||||
String sha256_hex(String data) { return(sha256_hex_native(data)); }
|
||||
String hmac_sha256(String key, String data) { return(hmac_sha256_native(key, data)); }
|
||||
String hmac_sha256_hex(String key, String data) { return(hmac_sha256_hex_native(key, data)); }
|
||||
String base64_decode(String raw) { bool ok=false; return(::base64_decode(raw, ok)); }
|
||||
String random_bytes(u64 n) { if(n > 1024*1024) n = 1024*1024; String out(n, 0); int fd=open("/dev/urandom", O_RDONLY); if(fd<0) return(""); size_t off=0; while(off<n) { ssize_t got=read(fd, &out[off], n-off); if(got<0 && errno==EINTR) continue; if(got<=0) break; off += (size_t)got; } close(fd); out.resize(off); return(out); }
|
||||
bool crypto_equal(String a, String b) { return(crypto_equal_native(a, b)); }
|
||||
|
||||
// Single definitions for the native split build (declared extern in sys.h).
|
||||
pid_t parent_pid = 0;
|
||||
@@ -529,7 +732,59 @@ pid_t my_pid = 0;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr f64 FILE_LOCK_WAIT_TIMEOUT_SECONDS = 3.0;
|
||||
u64 file_lock_timeout_ms()
|
||||
{
|
||||
const char* raw = getenv("UCE_FILE_LOCK_TIMEOUT_MS");
|
||||
if(!raw || !*raw)
|
||||
return(2000);
|
||||
char* end = 0;
|
||||
unsigned long long parsed = strtoull(raw, &end, 10);
|
||||
if(end == raw)
|
||||
return(2000);
|
||||
return((u64)parsed);
|
||||
}
|
||||
|
||||
u64 monotonic_ms()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return((u64)ts.tv_sec * 1000ull + (u64)ts.tv_nsec / 1000000ull);
|
||||
}
|
||||
|
||||
int open_locked_file(String file_name, int open_flags, int lock_type, int create_mode = 0644)
|
||||
{
|
||||
int fd = open(file_name.c_str(), open_flags, create_mode);
|
||||
if(fd == -1)
|
||||
return(-1);
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
u64 timeout = file_lock_timeout_ms();
|
||||
u64 deadline = monotonic_ms() + timeout;
|
||||
while(true)
|
||||
{
|
||||
if(flock(fd, lock_type | LOCK_NB) == 0)
|
||||
return(fd);
|
||||
if(errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
|
||||
{
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
if(timeout == 0 || monotonic_ms() >= deadline)
|
||||
{
|
||||
fprintf(stderr, "(!) file lock timeout after %llums: %s\n", (unsigned long long)timeout, file_name.c_str());
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
void close_locked_file(int fd)
|
||||
{
|
||||
if(fd == -1)
|
||||
return;
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -737,36 +992,9 @@ bool file_exists(String path)
|
||||
return(std::filesystem::exists(fp));
|
||||
}
|
||||
|
||||
int file_open_locked(String file_name, int open_flags, int lock_type, int create_mode, f64 wait_timeout_seconds, String purpose)
|
||||
{
|
||||
(void)wait_timeout_seconds;
|
||||
(void)purpose;
|
||||
int fd = open(file_name.c_str(), open_flags, create_mode);
|
||||
if(fd == -1)
|
||||
return(-1);
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
if(flock(fd, lock_type) != 0)
|
||||
{
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
return(fd);
|
||||
}
|
||||
namespace {
|
||||
|
||||
void file_close_locked(int fd)
|
||||
{
|
||||
if(fd == -1)
|
||||
return;
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void file_release_process_locks(String reason)
|
||||
{
|
||||
(void)reason;
|
||||
}
|
||||
|
||||
String file_get_contents_locked_fd(int fd)
|
||||
String file_read_all(int fd)
|
||||
{
|
||||
if(fd == -1)
|
||||
return("");
|
||||
@@ -780,7 +1008,6 @@ String file_get_contents_locked_fd(int fd)
|
||||
return(content);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool file_write_all(int fd, const char* data, size_t remaining)
|
||||
{
|
||||
@@ -803,41 +1030,30 @@ bool file_write_all(int fd, const char* data, size_t remaining)
|
||||
|
||||
}
|
||||
|
||||
bool file_put_contents_locked_fd(int fd, String content)
|
||||
{
|
||||
if(fd == -1)
|
||||
return(false);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
if(ftruncate(fd, 0) != 0)
|
||||
return(false);
|
||||
if(!file_write_all(fd, content.data(), content.length()))
|
||||
return(false);
|
||||
return(true);
|
||||
}
|
||||
|
||||
String file_get_contents(String file_name)
|
||||
{
|
||||
s32 fd = file_open_locked(file_name, O_RDONLY, LOCK_SH, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "file_get_contents:" + file_name);
|
||||
s32 fd = open_locked_file(file_name, O_RDONLY, LOCK_SH);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not read %s\n", file_name.c_str());
|
||||
return("");
|
||||
}
|
||||
String content = file_get_contents_locked_fd(fd);
|
||||
file_close_locked(fd);
|
||||
String content = file_read_all(fd);
|
||||
close_locked_file(fd);
|
||||
return(content);
|
||||
}
|
||||
|
||||
bool file_put_contents(String file_name, String content)
|
||||
{
|
||||
s32 fd = file_open_locked(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "file_put_contents:" + file_name);
|
||||
s32 fd = open_locked_file(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not write %s\n", file_name.c_str());
|
||||
return(false);
|
||||
}
|
||||
bool ok = file_put_contents_locked_fd(fd, content);
|
||||
file_close_locked(fd);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
bool ok = ftruncate(fd, 0) == 0 && file_write_all(fd, content.data(), content.length());
|
||||
close_locked_file(fd);
|
||||
if(!ok)
|
||||
{
|
||||
printf("(!) Could not fully write %s\n", file_name.c_str());
|
||||
@@ -846,9 +1062,9 @@ bool file_put_contents(String file_name, String content)
|
||||
return(true);
|
||||
}
|
||||
|
||||
bool file_append_contents(String file_name, String content)
|
||||
bool file_append(String file_name, String content)
|
||||
{
|
||||
s32 fd = file_open_locked(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "file_append:" + file_name);
|
||||
s32 fd = open_locked_file(file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
|
||||
if(fd == -1)
|
||||
{
|
||||
printf("(!) Could not append %s\n", file_name.c_str());
|
||||
@@ -856,7 +1072,7 @@ bool file_append_contents(String file_name, String content)
|
||||
}
|
||||
lseek(fd, 0, SEEK_END);
|
||||
bool ok = file_write_all(fd, content.data(), content.length());
|
||||
file_close_locked(fd);
|
||||
close_locked_file(fd);
|
||||
if(!ok)
|
||||
{
|
||||
printf("(!) Could not fully append %s\n", file_name.c_str());
|
||||
@@ -1154,7 +1370,6 @@ pid_t spawn_subprocess(std::function<void()> exec_after_spawn)
|
||||
p = fork();
|
||||
if(p == 0)
|
||||
{
|
||||
file_release_process_locks("fork child startup");
|
||||
my_pid = getpid();
|
||||
//printf("(C) child procress started, PID:%i\n", my_pid);
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
@@ -1264,7 +1479,7 @@ pid_t task_pid(String key)
|
||||
{
|
||||
String status_file_name = task_file_prefix(key);
|
||||
String lock_file_name = status_file_name + ".lock";
|
||||
int lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "task-pid:" + key);
|
||||
int lock_fd = open_locked_file(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
|
||||
if(lock_fd == -1)
|
||||
{
|
||||
fprintf(stderr, "task_pid(): could not lock task key '%s'\n", key.c_str());
|
||||
@@ -1276,12 +1491,12 @@ pid_t task_pid(String key)
|
||||
TaskStatus status = task_status_parse(status_file);
|
||||
if(task_status_is_alive(status))
|
||||
{
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
return(status.pid);
|
||||
}
|
||||
file_unlink(status_file_name);
|
||||
}
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -1289,7 +1504,7 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
{
|
||||
String status_file_name = task_file_prefix(key);
|
||||
String lock_file_name = status_file_name + ".lock";
|
||||
int lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "task:" + key);
|
||||
int lock_fd = open_locked_file(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
|
||||
if(lock_fd == -1)
|
||||
{
|
||||
fprintf(stderr, "task(): could not lock task key '%s'\n", key.c_str());
|
||||
@@ -1303,7 +1518,7 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
if(task_status_is_alive(status))
|
||||
{
|
||||
printf("(P) worker process '%s' already running: PID %i\n", key.c_str(), status.pid);
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
return(status.pid);
|
||||
}
|
||||
file_unlink(status_file_name);
|
||||
@@ -1312,13 +1527,12 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
if(p < 0)
|
||||
{
|
||||
fprintf(stderr, "task(): fork failed for key '%s': %s\n", key.c_str(), strerror(errno));
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
return(0);
|
||||
}
|
||||
if(p == 0)
|
||||
{
|
||||
file_release_process_locks("task child startup");
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
my_pid = getpid();
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
if(timeout > 0)
|
||||
@@ -1331,11 +1545,11 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
}
|
||||
task_close_inherited_fds();
|
||||
exec_after_spawn();
|
||||
int exit_lock_fd = file_open_locked(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644, FILE_LOCK_WAIT_TIMEOUT_SECONDS, "task-exit:" + key);
|
||||
int exit_lock_fd = open_locked_file(lock_file_name, O_RDWR | O_CREAT, LOCK_EX, 0644);
|
||||
if(exit_lock_fd != -1)
|
||||
{
|
||||
file_unlink(status_file_name);
|
||||
file_close_locked(exit_lock_fd);
|
||||
close_locked_file(exit_lock_fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1349,10 +1563,10 @@ pid_t task(String key, std::function<void()> exec_after_spawn, u64 timeout)
|
||||
{
|
||||
fprintf(stderr, "task(): could not write status file for key '%s'; terminating child PID %i\n", key.c_str(), p);
|
||||
kill(p, SIGTERM);
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
return(0);
|
||||
}
|
||||
file_close_locked(lock_fd);
|
||||
close_locked_file(lock_fd);
|
||||
printf("(P) worker process '%s' spawned: PID %i\n", key.c_str(), p);
|
||||
return(p);
|
||||
}
|
||||
@@ -1425,6 +1639,8 @@ StringMap make_server_settings()
|
||||
// command batches here at workspace teardown.
|
||||
cfg["WS_BROKER_SOCKET_PATH"] = "/run/uce/ws-broker.sock";
|
||||
cfg["WS_BROKER_OUTBOUND_TIMEOUT_SECONDS"] = "30";
|
||||
// Comma-separated uce_host_* names a sysadmin disables; empty = nothing blocked.
|
||||
cfg["UCE_HOSTCALL_BLOCKLIST"] = "";
|
||||
cfg["TMP_UPLOAD_PATH"] = "/tmp/uce/uploads";
|
||||
cfg["SESSION_PATH"] = "/tmp/uce/sessions";
|
||||
cfg["COMPILER_SYS_PATH"] = ".";
|
||||
|
||||
+38
-8
@@ -25,7 +25,24 @@ int usleep(unsigned int usec);
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
struct DValue;
|
||||
|
||||
String shell_exec(String cmd);
|
||||
DValue http_request(DValue req);
|
||||
u64 http_request_async(DValue req);
|
||||
DValue shell_exec(DValue spec);
|
||||
u64 shell_spawn(DValue spec);
|
||||
DValue job_status(u64 job_id);
|
||||
DValue job_result(u64 job_id);
|
||||
DValue job_await(u64 job_id, u64 timeout_ms);
|
||||
bool job_cancel(u64 job_id);
|
||||
String sha256(String data);
|
||||
String sha256_hex(String data);
|
||||
String hmac_sha256(String key, String data);
|
||||
String hmac_sha256_hex(String key, String data);
|
||||
String base64_decode(String raw);
|
||||
String random_bytes(u64 n);
|
||||
bool crypto_equal(String a, String b);
|
||||
String shell_escape(String raw);
|
||||
String basename(String fn);
|
||||
String dirname(String fn);
|
||||
@@ -34,20 +51,34 @@ String path_real(String path);
|
||||
bool path_is_within(String path, String root);
|
||||
bool mkdir(String path);
|
||||
bool file_exists(String path);
|
||||
int file_open_locked(String file_name, int open_flags, int lock_type = LOCK_SH, int create_mode = 0644, f64 wait_timeout_seconds = 3.0, String purpose = "");
|
||||
void file_close_locked(int fd);
|
||||
void file_release_process_locks(String reason = "");
|
||||
String file_get_contents_locked_fd(int fd);
|
||||
bool file_put_contents_locked_fd(int fd, String content);
|
||||
String file_get_contents(String file_name);
|
||||
bool file_put_contents(String file_name, String content);
|
||||
bool file_append_contents(String file_name, String content);
|
||||
bool file_append(String file_name, String content);
|
||||
u64 file_open(String path, String mode);
|
||||
String file_read(u64 h, u64 len);
|
||||
String file_pread(u64 h, u64 offset, u64 len);
|
||||
u64 file_write(u64 h, String data);
|
||||
u64 file_pwrite(u64 h, u64 offset, String data);
|
||||
s64 file_seek(u64 h, s64 offset, int whence);
|
||||
s64 file_tell(u64 h);
|
||||
void file_close(u64 h);
|
||||
DValue file_stat(String path);
|
||||
DValue dir_list(String path);
|
||||
bool file_rename(String from, String to);
|
||||
bool file_copy(String from, String to);
|
||||
bool file_truncate(String path, u64 size);
|
||||
bool dir_remove(String path, bool recursive = false);
|
||||
String file_temp(String prefix);
|
||||
bool file_chmod(String path, u32 mode);
|
||||
bool file_symlink(String target, String linkpath);
|
||||
String file_readlink(String path);
|
||||
bool file_fsync(u64 h);
|
||||
template <typename... Ts>
|
||||
inline bool file_append(String file_name, Ts... args)
|
||||
{
|
||||
std::ostringstream out;
|
||||
((out << args), ...);
|
||||
return(file_append_contents(file_name, out.str()));
|
||||
return(file_append(file_name, out.str()));
|
||||
}
|
||||
String cwd_get();
|
||||
void cwd_set(String path);
|
||||
@@ -61,7 +92,6 @@ u64 time();
|
||||
String time_format_local(String format = "", u64 timestamp = 0);
|
||||
|
||||
// Runtime timing/profiling snapshot for the active wasm request/workspace.
|
||||
struct DValue;
|
||||
DValue request_perf();
|
||||
|
||||
String time_format_utc(String format = "", u64 timestamp = 0);
|
||||
|
||||
+18
-6
@@ -130,7 +130,7 @@ void render_request_failure(Request& request, String title, String details, Stri
|
||||
if(!request.resources.is_cli)
|
||||
{
|
||||
DValue error_info;
|
||||
error_info["type"] = "runtime_error";
|
||||
error_info["type"] = (title == "function disabled by server policy") ? "policy_blocked" : "runtime_error";
|
||||
error_info["title"] = title;
|
||||
error_info["details"] = details;
|
||||
error_info["source"] = request.params["SCRIPT_FILENAME"];
|
||||
@@ -533,9 +533,23 @@ int handle_complete(FastCGIRequest& request) {
|
||||
request_fault_active = 1;
|
||||
if(wasm_error != "")
|
||||
{
|
||||
failure_title = "wasm runtime error during request";
|
||||
failure_details = "";
|
||||
failure_trace = wasm_error;
|
||||
size_t blocked_at = wasm_error.find("UCE_POLICY_BLOCKED:");
|
||||
if(blocked_at != String::npos)
|
||||
{
|
||||
String fn = wasm_error.substr(blocked_at + 19);
|
||||
size_t fn_end = fn.find_first_of(" \t\r\n\"");
|
||||
if(fn_end != String::npos)
|
||||
fn = fn.substr(0, fn_end);
|
||||
failure_title = "function disabled by server policy";
|
||||
failure_details = "this unit called " + fn + ", which is disabled on this server by configuration (UCE_HOSTCALL_BLOCKLIST)";
|
||||
failure_trace = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
failure_title = "wasm runtime error during request";
|
||||
failure_details = "";
|
||||
failure_trace = wasm_error;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1307,7 +1321,6 @@ void ensure_proactive_compiler()
|
||||
}
|
||||
if(p == 0)
|
||||
{
|
||||
file_release_process_locks("proactive compiler fork");
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
run_proactive_compiler();
|
||||
exit(0);
|
||||
@@ -1329,7 +1342,6 @@ void listen_for_connections()
|
||||
server.on_cli_complete = &handle_cli_complete;
|
||||
for(;;)
|
||||
{
|
||||
file_release_process_locks("worker loop cleanup");
|
||||
server.process(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,17 @@ static String wasm_backend_ensure_started(Request* context)
|
||||
wc.memory_limit = (int64_t)to_u64(cfg["WASM_MEMORY_LIMIT_BYTES"], 512ull * 1024 * 1024);
|
||||
wc.epoch_deadline_ticks = to_u64(cfg["WASM_EPOCH_DEADLINE_TICKS"], 200);
|
||||
wc.verbose = to_bool(cfg["WASM_BACKEND_VERBOSE"], false);
|
||||
// UCE_HOSTCALL_BLOCKLIST: comma-separated uce_host_* names (with or without
|
||||
// the "uce_host_" prefix) the sysadmin disables; each blocked call traps into
|
||||
// the error page (see make_host_import). Parsed once here, per worker process.
|
||||
for(String entry : split(cfg["UCE_HOSTCALL_BLOCKLIST"], ","))
|
||||
{
|
||||
entry = trim(entry);
|
||||
if(entry.rfind("uce_host_", 0) == 0)
|
||||
entry = entry.substr(9);
|
||||
if(entry != "")
|
||||
wc.hostcall_blocklist.insert(entry);
|
||||
}
|
||||
|
||||
g_wasm_worker = new WasmWorker(wc);
|
||||
g_wasm_init_error = g_wasm_worker->init();
|
||||
@@ -131,6 +142,12 @@ String wasm_backend_serve(Request& request, const String& entry_unit, const Stri
|
||||
// Raw request body: cli_input() parses a JSON CLI payload from context.in,
|
||||
// and serve_http handlers read it as req->in; carried into the workspace.
|
||||
ctx["in"] = request.in;
|
||||
// Configurable error pages read context.call["error"] (the error_info DValue
|
||||
// set natively in render_wasm_error_page). apply_context binds context.call to
|
||||
// the decoded ctx, so marshal the native request.call children across the
|
||||
// membrane — otherwise the error page renders with no error data.
|
||||
for(auto& entry : request.call._map)
|
||||
ctx[entry.first] = entry.second;
|
||||
// WebSocket event context: the workspace owns no connections, so the frame's
|
||||
// connection identity goes in and the handler's ws_send/ws_close dispatch
|
||||
// commands come back out (below) for the native broker to apply.
|
||||
|
||||
@@ -3,6 +3,13 @@ uce_host_time_precise
|
||||
uce_host_env
|
||||
uce_host_log
|
||||
uce_host_random
|
||||
uce_host_sha256
|
||||
uce_host_sha256_hex
|
||||
uce_host_hmac_sha256
|
||||
uce_host_hmac_sha256_hex
|
||||
uce_host_base64_encode
|
||||
uce_host_base64_decode
|
||||
uce_host_crypto_equal
|
||||
uce_host_task_spawn
|
||||
uce_host_task_pid
|
||||
uce_host_task_kill
|
||||
@@ -20,6 +27,14 @@ uce_host_units
|
||||
uce_host_component_resolve
|
||||
uce_host_request_perf
|
||||
uce_host_shell_exec
|
||||
uce_host_http_request
|
||||
uce_host_http_request_async
|
||||
uce_host_shell_exec_dv
|
||||
uce_host_shell_spawn
|
||||
uce_host_job_status
|
||||
uce_host_job_result
|
||||
uce_host_job_await
|
||||
uce_host_job_cancel
|
||||
uce_host_file_exists
|
||||
uce_host_file_read
|
||||
uce_host_file_write
|
||||
@@ -27,11 +42,25 @@ uce_host_file_unlink
|
||||
uce_host_file_list
|
||||
uce_host_file_mkdir
|
||||
uce_host_file_mtime
|
||||
uce_host_file_open_locked
|
||||
uce_host_file_close_locked
|
||||
uce_host_file_release_process_locks
|
||||
uce_host_file_read_locked_fd
|
||||
uce_host_file_write_locked_fd
|
||||
uce_host_file_open
|
||||
uce_host_file_handle_read
|
||||
uce_host_file_handle_pread
|
||||
uce_host_file_handle_write
|
||||
uce_host_file_handle_pwrite
|
||||
uce_host_file_handle_seek
|
||||
uce_host_file_handle_tell
|
||||
uce_host_file_handle_close
|
||||
uce_host_file_stat
|
||||
uce_host_dir_list
|
||||
uce_host_file_rename
|
||||
uce_host_file_copy
|
||||
uce_host_file_truncate
|
||||
uce_host_dir_remove
|
||||
uce_host_file_temp
|
||||
uce_host_file_chmod
|
||||
uce_host_file_symlink
|
||||
uce_host_file_readlink
|
||||
uce_host_file_fsync
|
||||
uce_host_path_real
|
||||
uce_host_path_is_within
|
||||
uce_host_cwd_get
|
||||
|
||||
+639
-103
@@ -31,7 +31,9 @@
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -49,6 +51,9 @@
|
||||
#include <algorithm>
|
||||
#include <dirent.h>
|
||||
#include <cerrno>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
|
||||
struct WasmDylinkInfo
|
||||
{
|
||||
@@ -86,6 +91,10 @@ struct WasmWorkerConfig
|
||||
u32 table_headroom = 4096;
|
||||
u64 epoch_deadline_ticks = 200; // ticker period × ticks = CPU budget
|
||||
bool verbose = false;
|
||||
// uce_host_* names (bare, without the "uce_host_" prefix) the sysadmin has
|
||||
// disabled via UCE_HOSTCALL_BLOCKLIST. A blocked hostcall resolves to a trap
|
||||
// stub at workspace birth (see make_host_import); empty = feature off.
|
||||
std::set<String> hostcall_blocklist;
|
||||
};
|
||||
|
||||
struct WasmResponse
|
||||
@@ -100,6 +109,345 @@ struct WasmResponse
|
||||
u64 component_resolve_total_us = 0;
|
||||
};
|
||||
|
||||
static u64 wasm_file_lock_timeout_ms()
|
||||
{
|
||||
const char* raw = getenv("UCE_FILE_LOCK_TIMEOUT_MS");
|
||||
if(!raw || !*raw)
|
||||
return(2000);
|
||||
char* end = 0;
|
||||
unsigned long long parsed = strtoull(raw, &end, 10);
|
||||
return(end == raw ? 2000 : (u64)parsed);
|
||||
}
|
||||
|
||||
static u64 wasm_monotonic_ms()
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return((u64)ts.tv_sec * 1000ull + (u64)ts.tv_nsec / 1000000ull);
|
||||
}
|
||||
|
||||
static int wasm_open_locked_file(const String& file_name, int flags, int lock_type, bool truncate_after_lock)
|
||||
{
|
||||
int fd = open(file_name.c_str(), flags, 0644);
|
||||
if(fd < 0)
|
||||
return(-1);
|
||||
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
||||
u64 timeout = wasm_file_lock_timeout_ms();
|
||||
u64 deadline = wasm_monotonic_ms() + timeout;
|
||||
while(true)
|
||||
{
|
||||
if(flock(fd, lock_type | LOCK_NB) == 0)
|
||||
{
|
||||
if(truncate_after_lock && ftruncate(fd, 0) != 0)
|
||||
{
|
||||
flock(fd, LOCK_UN);
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
return(fd);
|
||||
}
|
||||
if(errno != EWOULDBLOCK && errno != EAGAIN && errno != EINTR)
|
||||
{
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
if(timeout == 0 || wasm_monotonic_ms() >= deadline)
|
||||
{
|
||||
fprintf(stderr, "[wasm] file lock timeout after %llums: %s\n", (unsigned long long)timeout, file_name.c_str());
|
||||
close(fd);
|
||||
return(-1);
|
||||
}
|
||||
usleep(10000);
|
||||
}
|
||||
}
|
||||
|
||||
static bool wasm_fd_write_all(int fd, const char* data, size_t remaining, u64* written_out)
|
||||
{
|
||||
u64 total = 0;
|
||||
while(remaining > 0)
|
||||
{
|
||||
ssize_t n = write(fd, data, remaining);
|
||||
if(n < 0)
|
||||
{
|
||||
if(errno == EINTR)
|
||||
continue;
|
||||
return(false);
|
||||
}
|
||||
if(n == 0)
|
||||
return(false);
|
||||
data += n;
|
||||
remaining -= (size_t)n;
|
||||
total += (u64)n;
|
||||
}
|
||||
if(written_out)
|
||||
*written_out = total;
|
||||
return(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---- file-backed async job registry + bounded process execution ----------
|
||||
|
||||
static String uce_job_root()
|
||||
{
|
||||
const char* env = getenv("UCE_JOB_ROOT");
|
||||
String root = (env && *env) ? String(env) : String("/run/uce/jobs");
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(root, ec);
|
||||
if(ec)
|
||||
{
|
||||
root = "/tmp/uce/jobs";
|
||||
std::filesystem::create_directories(root, ec);
|
||||
}
|
||||
return(root);
|
||||
}
|
||||
|
||||
static String uce_job_path(u64 id) { return(uce_job_root() + "/" + std::to_string(id)); }
|
||||
static String uce_read_text(const String& path) { std::ifstream in(path, std::ios::binary); if(!in) return(""); std::ostringstream ss; ss << in.rdbuf(); return(ss.str()); }
|
||||
static void uce_write_text(const String& path, const String& data) { std::ofstream out(path, std::ios::binary|std::ios::trunc); out.write(data.data(), (std::streamsize)data.size()); }
|
||||
|
||||
static u64 uce_job_new(const String& kind)
|
||||
{
|
||||
String root = uce_job_root();
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(root, ec);
|
||||
u64 seed = ((u64)time(0) << 32) ^ ((u64)getpid() << 16) ^ (u64)rand();
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
u64 id = seed ^ (wasm_monotonic_ms() + (u64)i * 0x9e3779b97f4a7c15ull);
|
||||
String dir = root + "/" + std::to_string(id);
|
||||
if(mkdir(dir.c_str(), 0700) == 0)
|
||||
{
|
||||
uce_write_text(dir + "/kind", kind);
|
||||
uce_write_text(dir + "/created", std::to_string((u64)time(0)));
|
||||
uce_write_text(dir + "/state", "pending");
|
||||
return(id);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
static void uce_job_reap()
|
||||
{
|
||||
String root = uce_job_root();
|
||||
u64 now = (u64)time(0);
|
||||
u64 ttl = 3600;
|
||||
if(const char* raw = getenv("UCE_JOB_TTL_SECONDS")) { char* e=0; unsigned long long v=strtoull(raw,&e,10); if(e!=raw && v>0) ttl=(u64)v; }
|
||||
std::error_code ec;
|
||||
for(auto& e : std::filesystem::directory_iterator(root, ec))
|
||||
{
|
||||
if(!e.is_directory()) continue;
|
||||
u64 created = strtoull(uce_read_text(e.path().string()+"/created").c_str(), 0, 10);
|
||||
if(created > 0 && now > created + ttl)
|
||||
std::filesystem::remove_all(e.path(), ec);
|
||||
}
|
||||
}
|
||||
|
||||
static DValue uce_process_exec(String cmd, String input, StringMap env, u64 timeout_ms)
|
||||
{
|
||||
DValue r;
|
||||
r["exit_code"] = (f64)-1;
|
||||
r["stdout"] = "";
|
||||
r["stderr"] = "";
|
||||
r["timed_out"].set_bool(false);
|
||||
if(timeout_ms == 0) timeout_ms = 5000;
|
||||
int inpipe[2], outpipe[2], errpipe[2];
|
||||
if(pipe(inpipe) || pipe(outpipe) || pipe(errpipe)) { r["stderr"]="pipe failed"; return(r); }
|
||||
pid_t pid = fork();
|
||||
if(pid == 0)
|
||||
{
|
||||
dup2(inpipe[0], 0); dup2(outpipe[1], 1); dup2(errpipe[1], 2);
|
||||
close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); close(errpipe[0]); close(errpipe[1]);
|
||||
for(auto& kv : env) setenv(kv.first.c_str(), kv.second.c_str(), 1);
|
||||
execl("/bin/sh", "sh", "-c", cmd.c_str(), (char*)0);
|
||||
_exit(127);
|
||||
}
|
||||
close(inpipe[0]); close(outpipe[1]); close(errpipe[1]);
|
||||
fcntl(outpipe[0], F_SETFL, fcntl(outpipe[0], F_GETFL, 0) | O_NONBLOCK);
|
||||
fcntl(errpipe[0], F_SETFL, fcntl(errpipe[0], F_GETFL, 0) | O_NONBLOCK);
|
||||
size_t input_off = 0; bool in_open = true, out_open = true, err_open = true; int status = 0; bool exited = false;
|
||||
u64 deadline = wasm_monotonic_ms() + timeout_ms;
|
||||
while(out_open || err_open || !exited)
|
||||
{
|
||||
if(!exited)
|
||||
{
|
||||
pid_t w = waitpid(pid, &status, WNOHANG);
|
||||
if(w == pid) exited = true;
|
||||
}
|
||||
if(in_open)
|
||||
{
|
||||
if(input_off < input.size()) { ssize_t n=write(inpipe[1], input.data()+input_off, input.size()-input_off); if(n>0) input_off += (size_t)n; else if(n<0 && errno!=EINTR) { close(inpipe[1]); in_open=false; } }
|
||||
else { close(inpipe[1]); in_open=false; }
|
||||
}
|
||||
char buf[4096];
|
||||
ssize_t n;
|
||||
while((n=read(outpipe[0], buf, sizeof(buf))) > 0) r["stdout"] = r["stdout"].to_string() + String(buf, n);
|
||||
if(n == 0 && out_open) { close(outpipe[0]); out_open=false; }
|
||||
while((n=read(errpipe[0], buf, sizeof(buf))) > 0) r["stderr"] = r["stderr"].to_string() + String(buf, n);
|
||||
if(n == 0 && err_open) { close(errpipe[0]); err_open=false; }
|
||||
if(!exited && wasm_monotonic_ms() >= deadline)
|
||||
{
|
||||
r["timed_out"].set_bool(true);
|
||||
kill(pid, SIGKILL);
|
||||
waitpid(pid, &status, 0);
|
||||
exited = true;
|
||||
}
|
||||
if((out_open || err_open || !exited)) usleep(10000);
|
||||
}
|
||||
if(WIFEXITED(status)) { r["exit_code"] = (f64)WEXITSTATUS(status); r["timed_out"].set_bool(false); }
|
||||
else if(WIFSIGNALED(status)) r["exit_code"] = (f64)(128 + WTERMSIG(status));
|
||||
return(r);
|
||||
}
|
||||
|
||||
static DValue uce_shell_exec_spec(const DValue& spec)
|
||||
{
|
||||
return(uce_process_exec(spec.key("cmd") ? spec.key("cmd")->to_string() : String(""), spec.key("stdin") ? spec.key("stdin")->to_string() : String(""), spec.key("env") ? spec.key("env")->to_stringmap() : StringMap(), spec.key("timeout_ms") ? spec.key("timeout_ms")->to_u64(5000) : 5000));
|
||||
}
|
||||
|
||||
static void uce_job_finish(u64 id, DValue result, String final_state="done")
|
||||
{
|
||||
String dir = uce_job_path(id);
|
||||
uce_write_text(dir + "/result.tmp", ucb_encode(result));
|
||||
rename((dir + "/result.tmp").c_str(), (dir + "/result").c_str());
|
||||
uce_write_text(dir + "/state", final_state);
|
||||
}
|
||||
|
||||
static u64 uce_shell_spawn_spec(const DValue& spec)
|
||||
{
|
||||
uce_job_reap();
|
||||
u64 id = uce_job_new("shell");
|
||||
if(!id) return(0);
|
||||
pid_t pid = fork();
|
||||
if(pid == 0)
|
||||
{
|
||||
setsid();
|
||||
uce_write_text(uce_job_path(id) + "/worker_pid", std::to_string((long long)getpid()));
|
||||
uce_write_text(uce_job_path(id) + "/state", "running");
|
||||
DValue result = uce_shell_exec_spec(spec);
|
||||
uce_job_finish(id, result, "done");
|
||||
_exit(0);
|
||||
}
|
||||
if(pid < 0) { DValue r; r["error"]="fork failed"; uce_job_finish(id,r,"failed"); return(id); }
|
||||
uce_write_text(uce_job_path(id) + "/worker_pid", std::to_string((long long)pid));
|
||||
uce_write_text(uce_job_path(id) + "/state", "running");
|
||||
return(id);
|
||||
}
|
||||
|
||||
|
||||
static DValue uce_exec_argv_capture(std::vector<String> argv, String input, u64 timeout_ms)
|
||||
{
|
||||
DValue r; r["exit_code"]=(f64)-1; r["stdout"]=""; r["stderr"]=""; r["timed_out"].set_bool(false);
|
||||
if(argv.empty()) { r["stderr"]="empty argv"; return(r); }
|
||||
if(timeout_ms == 0) timeout_ms = 5000;
|
||||
int inpipe[2], outpipe[2], errpipe[2];
|
||||
if(pipe(inpipe)||pipe(outpipe)||pipe(errpipe)) { r["stderr"]="pipe failed"; return(r); }
|
||||
pid_t pid=fork();
|
||||
if(pid==0)
|
||||
{
|
||||
dup2(inpipe[0],0); dup2(outpipe[1],1); dup2(errpipe[1],2);
|
||||
close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); close(errpipe[0]); close(errpipe[1]);
|
||||
std::vector<char*> args; for(auto& a: argv) args.push_back((char*)a.c_str()); args.push_back(0);
|
||||
execvp(args[0], args.data()); _exit(127);
|
||||
}
|
||||
close(inpipe[0]); close(outpipe[1]); close(errpipe[1]);
|
||||
fcntl(outpipe[0], F_SETFL, fcntl(outpipe[0], F_GETFL, 0)|O_NONBLOCK); fcntl(errpipe[0], F_SETFL, fcntl(errpipe[0], F_GETFL, 0)|O_NONBLOCK);
|
||||
size_t input_off=0; bool in_open=true,out_open=true,err_open=true,exited=false; int status=0; u64 deadline=wasm_monotonic_ms()+timeout_ms;
|
||||
while(out_open || err_open || !exited)
|
||||
{
|
||||
if(!exited) { pid_t w=waitpid(pid,&status,WNOHANG); if(w==pid) exited=true; }
|
||||
if(in_open) { if(input_off<input.size()) { ssize_t n=write(inpipe[1], input.data()+input_off, input.size()-input_off); if(n>0) input_off+=(size_t)n; else if(n<0 && errno!=EINTR) { close(inpipe[1]); in_open=false; } } else { close(inpipe[1]); in_open=false; } }
|
||||
char buf[4096]; ssize_t n; while((n=read(outpipe[0],buf,sizeof(buf)))>0) r["stdout"] = r["stdout"].to_string()+String(buf,n); if(n==0&&out_open){close(outpipe[0]);out_open=false;}
|
||||
while((n=read(errpipe[0],buf,sizeof(buf)))>0) r["stderr"] = r["stderr"].to_string()+String(buf,n); if(n==0&&err_open){close(errpipe[0]);err_open=false;}
|
||||
if(!exited && wasm_monotonic_ms() >= deadline) { r["timed_out"].set_bool(true); kill(pid,SIGKILL); waitpid(pid,&status,0); exited=true; }
|
||||
if(out_open || err_open || !exited) usleep(10000);
|
||||
}
|
||||
if(WIFEXITED(status)) { r["exit_code"]=(f64)WEXITSTATUS(status); r["timed_out"].set_bool(false); } else if(WIFSIGNALED(status)) r["exit_code"]=(f64)(128+WTERMSIG(status));
|
||||
return(r);
|
||||
}
|
||||
|
||||
static bool uce_header_name_safe(String name)
|
||||
{
|
||||
if(name=="") return(false);
|
||||
for(unsigned char c: name) if(!(isalnum(c)||c=='-'||c=='_')) return(false);
|
||||
return(true);
|
||||
}
|
||||
|
||||
static DValue uce_http_request_value(const DValue& req)
|
||||
{
|
||||
DValue r; r["status"]=(f64)0; r["headers"].set_array(); r["body"]=""; r["error"]="";
|
||||
String method = req.key("method") ? to_upper(req.key("method")->to_string()) : String("GET");
|
||||
String url = req.key("url") ? req.key("url")->to_string() : String("");
|
||||
if(url=="" || url.find('\0')!=String::npos) { r["error"]="missing url"; return(r); }
|
||||
if(method=="") method="GET";
|
||||
std::vector<String> argv = {"curl", "-sS", "--http1.0", "-X", method, "-D", "-", "-w", "\nUCE_HTTP_STATUS:%{http_code}", "--max-time", std::to_string(std::max<u64>(1, (req.key("timeout_ms") ? req.key("timeout_ms")->to_u64(5000) : 5000) / 1000))};
|
||||
if(req.key("follow_redirects") && req.key("follow_redirects")->to_bool()) argv.push_back("-L");
|
||||
if(req.key("headers")) req.key("headers")->each([&](const DValue& v, String k){ if(uce_header_name_safe(k)) { argv.push_back("-H"); argv.push_back(k + ": " + replace(replace(v.to_string(), "\r", " "), "\n", " ")); } });
|
||||
String body = req.key("body") ? req.key("body")->to_string() : String("");
|
||||
if(req.key("body")) { argv.push_back("--data-binary"); argv.push_back("@-"); }
|
||||
argv.push_back(url);
|
||||
if(access("/usr/bin/curl", X_OK)!=0 && access("/bin/curl", X_OK)!=0) { r["error"]="curl binary not found in runtime PATH"; return(r); }
|
||||
DValue pr = uce_exec_argv_capture(argv, body, req.key("timeout_ms") ? req.key("timeout_ms")->to_u64(5000) : 5000);
|
||||
String out = pr["stdout"].to_string();
|
||||
String marker="\nUCE_HTTP_STATUS:"; size_t mp=out.rfind(marker);
|
||||
if(mp!=String::npos) { r["status"]=(f64)strtoull(out.c_str()+mp+marker.size(),0,10); out=out.substr(0,mp); }
|
||||
else r["error"]="curl did not report status";
|
||||
String sep="\r\n\r\n"; size_t hp=out.rfind(sep); size_t sep_len=4; if(hp==String::npos) { sep="\n\n"; hp=out.rfind(sep); sep_len=2; }
|
||||
String hdrs = hp==String::npos ? String("") : out.substr(0,hp); r["body"] = hp==String::npos ? out : out.substr(hp+sep_len);
|
||||
DValue headers;
|
||||
for(String line: split(replace(hdrs,"\r",""), "\n")) { size_t c=line.find(':'); if(c!=String::npos) headers[trim(line.substr(0,c))] = trim(line.substr(c+1)); }
|
||||
r["headers"] = headers;
|
||||
if(pr["exit_code"].to_s64() != 0 && r["error"].to_string()=="") r["error"] = trim(pr["stderr"].to_string());
|
||||
return(r);
|
||||
}
|
||||
|
||||
static u64 uce_http_spawn_spec(const DValue& req)
|
||||
{
|
||||
uce_job_reap(); u64 id=uce_job_new("http"); if(!id) return(0);
|
||||
pid_t pid=fork();
|
||||
if(pid==0) { setsid(); uce_write_text(uce_job_path(id)+"/worker_pid", std::to_string((long long)getpid())); uce_write_text(uce_job_path(id)+"/state", "running"); DValue result=uce_http_request_value(req); uce_job_finish(id,result,result["error"].to_string()==""?"done":"failed"); _exit(0); }
|
||||
if(pid<0) { DValue r; r["error"]="fork failed"; uce_job_finish(id,r,"failed"); return(id); }
|
||||
uce_write_text(uce_job_path(id)+"/worker_pid", std::to_string((long long)pid)); uce_write_text(uce_job_path(id)+"/state", "running"); return(id);
|
||||
}
|
||||
|
||||
static DValue uce_job_status_value(u64 id)
|
||||
{
|
||||
DValue r; String dir=uce_job_path(id); r["job_id"]=(f64)id;
|
||||
if(id==0 || !std::filesystem::is_directory(dir)) { r["state"]="missing"; return(r); }
|
||||
String state=trim(uce_read_text(dir+"/state")); if(state=="") state="pending"; r["state"]=state;
|
||||
r["kind"]=trim(uce_read_text(dir+"/kind")); r["pid"]=(f64)strtoull(uce_read_text(dir+"/worker_pid").c_str(),0,10);
|
||||
r["done"].set_bool(state=="done"||state=="failed"||state=="cancelled");
|
||||
return(r);
|
||||
}
|
||||
|
||||
static DValue uce_job_result_value(u64 id, u64 timeout_ms)
|
||||
{
|
||||
u64 deadline = wasm_monotonic_ms() + timeout_ms;
|
||||
while(timeout_ms > 0 && wasm_monotonic_ms() < deadline)
|
||||
{
|
||||
DValue st = uce_job_status_value(id);
|
||||
if(st["done"].to_bool()) break;
|
||||
usleep(10000);
|
||||
}
|
||||
DValue r = uce_job_status_value(id);
|
||||
String encoded = uce_read_text(uce_job_path(id)+"/result");
|
||||
if(encoded != "") { DValue decoded; String err; if(ucb_decode(encoded, decoded, &err)) r["result"] = decoded; }
|
||||
return(r);
|
||||
}
|
||||
|
||||
static bool uce_job_cancel_value(u64 id)
|
||||
{
|
||||
DValue st = uce_job_status_value(id);
|
||||
if(st["state"].to_string()=="missing") return(false);
|
||||
String state = st["state"].to_string();
|
||||
if(state == "done" || state == "failed" || state == "cancelled")
|
||||
return(false);
|
||||
pid_t pid = (pid_t)st["pid"].to_u64(0);
|
||||
if(pid > 0) kill(-pid, SIGKILL);
|
||||
DValue result; result["cancelled"].set_bool(true);
|
||||
uce_job_finish(id, result, "cancelled");
|
||||
return(true);
|
||||
}
|
||||
|
||||
// ---- module byte parsing (hardened; carried from the phase 3 spike) -------
|
||||
|
||||
// included into both w3_driver.cpp and the native server TU (via backend.cpp);
|
||||
@@ -411,6 +759,13 @@ public:
|
||||
u64 component_resolve_count = 0;
|
||||
u64 component_resolve_total_us = 0;
|
||||
|
||||
struct FileHandle
|
||||
{
|
||||
int fd = -1;
|
||||
bool writable = false;
|
||||
};
|
||||
std::vector<FileHandle> file_handles;
|
||||
|
||||
struct RequestPerfSnapshot
|
||||
{
|
||||
u64 worker_pid = 0;
|
||||
@@ -435,9 +790,6 @@ public:
|
||||
request_perf.active = true;
|
||||
}
|
||||
|
||||
// Host-owned opaque fd handles opened by wasm file_open_locked().
|
||||
std::vector<int> locked_file_handles;
|
||||
|
||||
#ifdef UCE_WASM_HOST_CONNECTORS
|
||||
// Host-owned resource handle table (§3.1): connections opened by the guest
|
||||
// live here and are closed when the workspace drops at request end.
|
||||
@@ -446,12 +798,15 @@ public:
|
||||
#endif
|
||||
~WasmWorkspace()
|
||||
{
|
||||
for(int fd : locked_file_handles)
|
||||
if(fd >= 0)
|
||||
for(auto& h : file_handles)
|
||||
{
|
||||
if(h.fd >= 0)
|
||||
{
|
||||
flock(fd, LOCK_UN);
|
||||
::close(fd);
|
||||
flock(h.fd, LOCK_UN);
|
||||
close(h.fd);
|
||||
h.fd = -1;
|
||||
}
|
||||
}
|
||||
#ifdef UCE_WASM_HOST_CONNECTORS
|
||||
for(auto* db : sqlite_handles)
|
||||
if(db)
|
||||
@@ -1124,7 +1479,7 @@ private:
|
||||
{
|
||||
char root_real[4096];
|
||||
if(root != "" && realpath(root.c_str(), root_real))
|
||||
root_prefixes.push_back(String(root_real) + "/");
|
||||
root_prefixes.push_back(String(root_real));
|
||||
}
|
||||
for(auto& candidate : candidates)
|
||||
{
|
||||
@@ -1134,7 +1489,7 @@ private:
|
||||
String path(resolved);
|
||||
bool allowed = false;
|
||||
for(auto& prefix : root_prefixes)
|
||||
if(path.rfind(prefix, 0) == 0)
|
||||
if(path == prefix || path.rfind(prefix + "/", 0) == 0)
|
||||
{
|
||||
allowed = true;
|
||||
break;
|
||||
@@ -1304,6 +1659,25 @@ private:
|
||||
return(host_funcs.back());
|
||||
};
|
||||
|
||||
// Hostcall blocklist (UCE_HOSTCALL_BLOCKLIST): a sysadmin-disabled hostcall
|
||||
// resolves to a trap stub instead of its real implementation, so a unit
|
||||
// invoking it fails at runtime into the configurable error page. The
|
||||
// decision is made once per import at workspace birth — no per-call cost,
|
||||
// and zero cost when nothing is blocked. A small core set stays exempt so
|
||||
// the runtime itself cannot be bricked.
|
||||
if(mod == "env" && !worker.cfg.hostcall_blocklist.empty() && name.rfind("uce_host_", 0) == 0)
|
||||
{
|
||||
static const std::set<String> non_blockable = { "component_resolve" };
|
||||
String bare = name.substr(9);
|
||||
if(worker.cfg.hostcall_blocklist.count(bare) && !non_blockable.count(bare))
|
||||
{
|
||||
std::string blocked(name);
|
||||
return(add([blocked](Caller, Span<const Val>, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
return(Trap("UCE_POLICY_BLOCKED:" + blocked));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
if(mod == "env" && name == "uce_host_time")
|
||||
return(add([](Caller, Span<const Val>, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
results[0] = Val((int64_t)::time(0));
|
||||
@@ -1367,6 +1741,20 @@ private:
|
||||
results[0] = Val((int32_t)bytes.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_sha256")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String in; self->hostcall_read(args[0].i32(), args[1].i32(), in); String out=sha256_native(in); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_sha256_hex")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String in; self->hostcall_read(args[0].i32(), args[1].i32(), in); String out=sha256_hex_native(in); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_hmac_sha256")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String key,in; self->hostcall_read(args[0].i32(), args[1].i32(), key); self->hostcall_read(args[2].i32(), args[3].i32(), in); String out=hmac_sha256_native(key,in); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_hmac_sha256_hex")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String key,in; self->hostcall_read(args[0].i32(), args[1].i32(), key); self->hostcall_read(args[2].i32(), args[3].i32(), in); String out=hmac_sha256_hex_native(key,in); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_base64_encode")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String in; self->hostcall_read(args[0].i32(), args[1].i32(), in); String out=base64_encode(in); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_base64_decode")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String in; self->hostcall_read(args[0].i32(), args[1].i32(), in); bool ok=false; String out=base64_decode(in, ok); if(!ok) out=""; u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_crypto_equal")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String a,b; self->hostcall_read(args[0].i32(), args[1].i32(), a); self->hostcall_read(args[2].i32(), args[3].i32(), b); results[0]=Val((int32_t)(crypto_equal_native(a,b)?1:0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_log")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
String text;
|
||||
@@ -1394,6 +1782,36 @@ private:
|
||||
results[0] = Val((int32_t)out.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_http_request")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); String out; String stage_key="http:"+encoded;
|
||||
if(!self->hostcall_staged(stage_key,out)) { DValue req,response; String err; if(ucb_decode(encoded,req,&err)) response=uce_http_request_value(req); else response["error"]="http_request decode failed: "+err; out=ucb_encode(response); if(buf==0) self->hostcall_stage(stage_key,out); }
|
||||
if(buf&&cap>=out.size()) self->hostcall_write(buf,out); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_http_request_async")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); DValue req; String err; u64 id=0; if(ucb_decode(encoded,req,&err)) id=uce_http_spawn_spec(req); results[0]=Val((int64_t)id); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_shell_exec_dv")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded);
|
||||
u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32();
|
||||
String out; String stage_key="shell_dv:"+encoded;
|
||||
if(!self->hostcall_staged(stage_key,out)) { DValue spec, response; String err; if(ucb_decode(encoded,spec,&err)) response=uce_shell_exec_spec(spec); else response["error"]="shell_exec spec decode failed: "+err; out=ucb_encode(response); if(buf==0) self->hostcall_stage(stage_key,out); }
|
||||
if(buf&&cap>=out.size()) self->hostcall_write(buf,out);
|
||||
caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks);
|
||||
results[0]=Val((int32_t)out.size()); return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_shell_spawn")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String encoded; self->hostcall_read(args[0].i32(), args[1].i32(), encoded); DValue spec; String err; u64 id=0; if(ucb_decode(encoded,spec,&err)) id=uce_shell_spawn_spec(spec); results[0]=Val((int64_t)id); return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_job_status")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String out=ucb_encode(uce_job_status_value((u64)args[0].i64())); u32 cap=(u32)args[2].i32(); int32_t buf=args[1].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_job_result")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String out=ucb_encode(uce_job_result_value((u64)args[0].i64(), 100)); u32 cap=(u32)args[2].i32(); int32_t buf=args[1].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_job_await")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { u64 timeout=std::min<u64>((u64)args[1].i64(), 30000); String out=ucb_encode(uce_job_result_value((u64)args[0].i64(), timeout)); u32 cap=(u32)args[3].i32(); int32_t buf=args[2].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); caller.context().set_epoch_deadline(self->worker.cfg.epoch_deadline_ticks); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_job_cancel")
|
||||
return(add([](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { results[0]=Val((int32_t)(uce_job_cancel_value((u64)args[0].i64())?1:0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_path_real")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path;
|
||||
@@ -1484,109 +1902,25 @@ private:
|
||||
results[0] = Val((int64_t)mtime);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_open_locked")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path, purpose, current;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), path);
|
||||
self->hostcall_read(args[6].i32(), args[7].i32(), purpose);
|
||||
self->hostcall_read(args[8].i32(), args[9].i32(), current);
|
||||
int open_flags = args[2].i32();
|
||||
bool may_write = (open_flags & (O_WRONLY | O_RDWR | O_CREAT | O_TRUNC | O_APPEND)) != 0;
|
||||
String resolved = may_write ? self->resolve_guest_write(path, current) : self->resolve_guest_file(path, current);
|
||||
int fd = resolved != "" ? ::file_open_locked(resolved, open_flags, args[3].i32(), args[4].i32(), args[5].f64(), purpose) : -1;
|
||||
int handle = -1;
|
||||
if(fd >= 0)
|
||||
{
|
||||
for(size_t i = 0; i < self->locked_file_handles.size(); i++)
|
||||
if(self->locked_file_handles[i] < 0)
|
||||
{
|
||||
self->locked_file_handles[i] = fd;
|
||||
handle = (int)i + 1;
|
||||
break;
|
||||
}
|
||||
if(handle < 0)
|
||||
{
|
||||
self->locked_file_handles.push_back(fd);
|
||||
handle = (int)self->locked_file_handles.size();
|
||||
}
|
||||
}
|
||||
results[0] = Val((int32_t)handle);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_close_locked")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
int handle = args[0].i32();
|
||||
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
|
||||
{
|
||||
int& fd = self->locked_file_handles[(size_t)handle - 1];
|
||||
if(fd >= 0)
|
||||
{
|
||||
::file_close_locked(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_release_process_locks")
|
||||
return(add([self](Caller, Span<const Val>, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
for(int& fd : self->locked_file_handles)
|
||||
if(fd >= 0)
|
||||
{
|
||||
::file_close_locked(fd);
|
||||
fd = -1;
|
||||
}
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_read_locked_fd")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
int handle = args[0].i32();
|
||||
String content;
|
||||
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
|
||||
{
|
||||
int fd = self->locked_file_handles[(size_t)handle - 1];
|
||||
if(fd >= 0)
|
||||
content = ::file_get_contents_locked_fd(fd);
|
||||
}
|
||||
u32 cap = (u32)args[2].i32();
|
||||
int32_t buf = args[1].i32();
|
||||
if(buf != 0 && cap >= content.size())
|
||||
self->hostcall_write(buf, content);
|
||||
results[0] = Val((int32_t)content.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_write_locked_fd")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
int handle = args[0].i32();
|
||||
String content;
|
||||
self->hostcall_read(args[1].i32(), args[2].i32(), content);
|
||||
bool ok = false;
|
||||
if(handle >= 1 && (size_t)handle <= self->locked_file_handles.size())
|
||||
{
|
||||
int fd = self->locked_file_handles[(size_t)handle - 1];
|
||||
if(fd >= 0)
|
||||
ok = ::file_put_contents_locked_fd(fd, content);
|
||||
}
|
||||
results[0] = Val(ok ? (int32_t)1 : (int32_t)0);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_read")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path, current;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), path);
|
||||
self->hostcall_read(args[2].i32(), args[3].i32(), current);
|
||||
String resolved = self->resolve_guest_file(path, current);
|
||||
std::vector<u8> bytes;
|
||||
if(resolved == "" || !wasm_read_file(resolved, bytes))
|
||||
String stage_key = "file_read:" + path + "\0" + current;
|
||||
String content;
|
||||
if(!self->hostcall_staged(stage_key, content))
|
||||
{
|
||||
results[0] = Val((int32_t)0);
|
||||
return(std::monostate());
|
||||
String resolved = self->resolve_guest_file(path, current);
|
||||
content = resolved == "" ? String("") : ::file_get_contents(resolved);
|
||||
self->hostcall_stage(stage_key, content);
|
||||
}
|
||||
u32 cap = (u32)args[5].i32();
|
||||
int32_t buf = args[4].i32();
|
||||
// length-query convention: no copy unless the buffer fits
|
||||
if(buf != 0 && cap >= bytes.size())
|
||||
self->hostcall_write(buf, String((const char*)bytes.data(), bytes.size()));
|
||||
results[0] = Val((int32_t)bytes.size());
|
||||
if(buf != 0 && cap >= content.size())
|
||||
self->hostcall_write(buf, content);
|
||||
results[0] = Val((int32_t)content.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_list")
|
||||
@@ -1631,12 +1965,214 @@ private:
|
||||
String resolved = self->resolve_guest_write(path, current);
|
||||
bool ok = false;
|
||||
if(resolved != "")
|
||||
ok = append ? file_append_contents(resolved, content) : file_put_contents(resolved, content);
|
||||
ok = append ? file_append(resolved, content) : file_put_contents(resolved, content);
|
||||
else if(self->worker.cfg.verbose)
|
||||
fprintf(stderr, "[wasm] file_write denied: %s\n", path.c_str());
|
||||
results[0] = Val(ok ? (int32_t)1 : (int32_t)0);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_open")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path, current, mode;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), path);
|
||||
self->hostcall_read(args[2].i32(), args[3].i32(), current);
|
||||
self->hostcall_read(args[4].i32(), args[5].i32(), mode);
|
||||
String resolved;
|
||||
int flags = O_RDONLY;
|
||||
int lock_type = LOCK_SH;
|
||||
bool writable = false;
|
||||
bool truncate_after_lock = false;
|
||||
if(mode == "r")
|
||||
resolved = self->resolve_guest_file(path, current);
|
||||
else if(mode == "w")
|
||||
{
|
||||
resolved = self->resolve_guest_write(path, current);
|
||||
flags = O_RDWR | O_CREAT;
|
||||
lock_type = LOCK_EX;
|
||||
writable = true;
|
||||
truncate_after_lock = true;
|
||||
}
|
||||
else if(mode == "a")
|
||||
{
|
||||
resolved = self->resolve_guest_write(path, current);
|
||||
flags = O_RDWR | O_CREAT | O_APPEND;
|
||||
lock_type = LOCK_EX;
|
||||
writable = true;
|
||||
}
|
||||
else if(mode == "r+")
|
||||
{
|
||||
resolved = self->resolve_guest_write(path, current);
|
||||
flags = O_RDWR;
|
||||
lock_type = LOCK_EX;
|
||||
writable = true;
|
||||
}
|
||||
uint64_t handle = 0;
|
||||
if(resolved != "")
|
||||
{
|
||||
int fd = wasm_open_locked_file(resolved, flags, lock_type, truncate_after_lock);
|
||||
if(fd >= 0)
|
||||
{
|
||||
if(mode == "a")
|
||||
lseek(fd, 0, SEEK_END);
|
||||
self->file_handles.push_back({fd, writable});
|
||||
handle = self->file_handles.size();
|
||||
}
|
||||
}
|
||||
results[0] = Val((int64_t)handle);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_read")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
u64 len = (u64)args[1].i64();
|
||||
u32 cap = (u32)args[3].i32();
|
||||
int32_t buf = args[2].i32();
|
||||
String out;
|
||||
String stage_key = "file_handle_read:" + std::to_string(handle) + ":" + std::to_string(len);
|
||||
if(!self->hostcall_staged(stage_key, out))
|
||||
{
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
int fd = self->file_handles[(size_t)handle - 1].fd;
|
||||
if(fd >= 0 && len > 0)
|
||||
{
|
||||
out.resize((size_t)std::min<u64>(len, 16ull * 1024ull * 1024ull));
|
||||
ssize_t n = read(fd, &out[0], out.size());
|
||||
out.resize(n > 0 ? (size_t)n : 0);
|
||||
}
|
||||
}
|
||||
if(buf == 0) self->hostcall_stage(stage_key, out);
|
||||
}
|
||||
if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out);
|
||||
results[0] = Val((int32_t)out.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_pread")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
u64 offset = (u64)args[1].i64();
|
||||
u64 len = (u64)args[2].i64();
|
||||
u32 cap = (u32)args[4].i32();
|
||||
int32_t buf = args[3].i32();
|
||||
String out;
|
||||
String stage_key = "file_handle_pread:" + std::to_string(handle) + ":" + std::to_string(offset) + ":" + std::to_string(len);
|
||||
if(!self->hostcall_staged(stage_key, out))
|
||||
{
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
int fd = self->file_handles[(size_t)handle - 1].fd;
|
||||
if(fd >= 0 && len > 0)
|
||||
{
|
||||
out.resize((size_t)std::min<u64>(len, 16ull * 1024ull * 1024ull));
|
||||
ssize_t n = pread(fd, &out[0], out.size(), (off_t)offset);
|
||||
out.resize(n > 0 ? (size_t)n : 0);
|
||||
}
|
||||
}
|
||||
if(buf == 0) self->hostcall_stage(stage_key, out);
|
||||
}
|
||||
if(buf != 0 && cap >= out.size()) self->hostcall_write(buf, out);
|
||||
results[0] = Val((int32_t)out.size());
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_write")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String data; self->hostcall_read(args[1].i32(), args[2].i32(), data);
|
||||
u64 handle = (u64)args[0].i64(); u64 written = 0;
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
auto& h = self->file_handles[(size_t)handle - 1];
|
||||
if(h.fd >= 0 && h.writable) wasm_fd_write_all(h.fd, data.data(), data.size(), &written);
|
||||
}
|
||||
results[0] = Val((int64_t)written);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_pwrite")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String data; self->hostcall_read(args[2].i32(), args[3].i32(), data);
|
||||
u64 handle = (u64)args[0].i64(); u64 offset = (u64)args[1].i64(); u64 written = 0;
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
auto& h = self->file_handles[(size_t)handle - 1];
|
||||
if(h.fd >= 0 && h.writable)
|
||||
{
|
||||
while(written < data.size())
|
||||
{
|
||||
ssize_t n = pwrite(h.fd, data.data() + written, data.size() - written, (off_t)(offset + written));
|
||||
if(n < 0 && errno == EINTR) continue;
|
||||
if(n <= 0) break;
|
||||
written += (u64)n;
|
||||
}
|
||||
}
|
||||
}
|
||||
results[0] = Val((int64_t)written);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_seek")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64(); s64 pos = -1;
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
int fd = self->file_handles[(size_t)handle - 1].fd;
|
||||
if(fd >= 0) pos = (s64)lseek(fd, (off_t)args[1].i64(), args[2].i32());
|
||||
}
|
||||
results[0] = Val((int64_t)pos);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_tell")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64(); s64 pos = -1;
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
int fd = self->file_handles[(size_t)handle - 1].fd;
|
||||
if(fd >= 0) pos = (s64)lseek(fd, 0, SEEK_CUR);
|
||||
}
|
||||
results[0] = Val((int64_t)pos);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_handle_close")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
if(handle >= 1 && handle <= self->file_handles.size())
|
||||
{
|
||||
auto& h = self->file_handles[(size_t)handle - 1];
|
||||
if(h.fd >= 0) { flock(h.fd, LOCK_UN); close(h.fd); h.fd = -1; }
|
||||
}
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_stat")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path, current; self->hostcall_read(args[0].i32(), args[1].i32(), path); self->hostcall_read(args[2].i32(), args[3].i32(), current);
|
||||
String resolved = self->resolve_guest_file(path, current, true); DValue r; struct stat st;
|
||||
r["exists"].set_bool(resolved != "" && lstat(resolved.c_str(), &st) == 0);
|
||||
if(r["exists"].to_bool()) { r["size"]=(f64)st.st_size; r["mtime"]=(f64)st.st_mtime; r["ctime"]=(f64)st.st_ctime; r["mode"]=(f64)(st.st_mode & 07777); r["is_dir"].set_bool(S_ISDIR(st.st_mode)); r["is_file"].set_bool(S_ISREG(st.st_mode)); r["is_symlink"].set_bool(S_ISLNK(st.st_mode)); }
|
||||
else { r["size"]=(f64)0; r["mtime"]=(f64)0; r["ctime"]=(f64)0; r["mode"]=(f64)0; r["is_dir"].set_bool(false); r["is_file"].set_bool(false); r["is_symlink"].set_bool(false); }
|
||||
String out = ucb_encode(r); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); if(buf && cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_dir_list")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String path, current; self->hostcall_read(args[0].i32(), args[1].i32(), path); self->hostcall_read(args[2].i32(), args[3].i32(), current);
|
||||
String resolved = self->resolve_guest_file(path, current, true); DValue list; list.set_array();
|
||||
if(resolved != "") { std::vector<String> names; if(DIR* d=opendir(resolved.c_str())) { while(struct dirent* e=readdir(d)) { String n=e->d_name; if(n!="."&&n!="..") names.push_back(n); } closedir(d); } std::sort(names.begin(), names.end()); for(auto& n:names) { String p=resolved+"/"+n; struct stat st; DValue item; item["name"]=n; if(lstat(p.c_str(), &st)==0) { item["size"]=(f64)st.st_size; item["mtime"]=(f64)st.st_mtime; item["type"]=S_ISDIR(st.st_mode)?"dir":S_ISLNK(st.st_mode)?"symlink":S_ISREG(st.st_mode)?"file":"other"; } list.push(item); } }
|
||||
String out=ucb_encode(list); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_file_rename")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String from,to,current; self->hostcall_read(args[0].i32(),args[1].i32(),from); self->hostcall_read(args[2].i32(),args[3].i32(),to); self->hostcall_read(args[4].i32(),args[5].i32(),current); String rf=self->resolve_guest_write(from,current), rt=self->resolve_guest_write(to,current); results[0]=Val((int32_t)(rf!=""&&rt!=""&&rename(rf.c_str(),rt.c_str())==0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_copy")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String from,to,current; self->hostcall_read(args[0].i32(),args[1].i32(),from); self->hostcall_read(args[2].i32(),args[3].i32(),to); self->hostcall_read(args[4].i32(),args[5].i32(),current); String rf=self->resolve_guest_file(from,current), rt=self->resolve_guest_write(to,current); bool ok=false; if(rf!=""&&rt!="") { std::ifstream in(rf, std::ios::binary); std::ofstream out(rt, std::ios::binary|std::ios::trunc); out<<in.rdbuf(); struct stat st; if(in&&out) { ok=true; if(stat(rf.c_str(),&st)==0) chmod(rt.c_str(), st.st_mode & 07777); } } results[0]=Val((int32_t)ok); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_truncate")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); results[0]=Val((int32_t)(r!=""&&truncate(r.c_str(),(off_t)args[4].i64())==0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_dir_remove")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); bool rec=args[4].i32()!=0; bool ok=false; if(r!="") { if(rec) ok=std::filesystem::remove_all(r)>0; else ok=::rmdir(r.c_str())==0; } results[0]=Val((int32_t)ok); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_temp")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String prefix,current; self->hostcall_read(args[0].i32(),args[1].i32(),prefix); self->hostcall_read(args[2].i32(),args[3].i32(),current); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); String out; String stage_key="file_temp:"+prefix+"\0"+current; if(!self->hostcall_staged(stage_key,out)) { if(prefix=="") prefix="/tmp/uce-temp"; String templ=self->resolve_guest_write(prefix+"XXXXXX",current); if(templ!="") { std::vector<char> t(templ.begin(), templ.end()); t.push_back(0); int fd=mkstemp(t.data()); if(fd>=0) { close(fd); out=t.data(); } } if(buf==0) self->hostcall_stage(stage_key,out); } if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_chmod")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); results[0]=Val((int32_t)(r!=""&&chmod(r.c_str(),(mode_t)args[4].i32())==0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_symlink")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String target,linkpath,current; self->hostcall_read(args[0].i32(),args[1].i32(),target); self->hostcall_read(args[2].i32(),args[3].i32(),linkpath); self->hostcall_read(args[4].i32(),args[5].i32(),current); String rt=self->resolve_guest_file(target,current), rl=self->resolve_guest_write(linkpath,current); results[0]=Val((int32_t)(rt!=""&&rl!=""&&symlink(rt.c_str(),rl.c_str())==0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_readlink")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); String out; if(r!="") { char b[4096]; ssize_t n=readlink(r.c_str(),b,sizeof(b)); if(n>0) out.assign(b,n); } u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_fsync")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { u64 handle=(u64)args[0].i64(); bool ok=false; if(handle>=1&&handle<=self->file_handles.size()) { int fd=self->file_handles[(size_t)handle-1].fd; ok=fd>=0&&fsync(fd)==0; } results[0]=Val((int32_t)ok); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_zip")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
String encoded;
|
||||
|
||||
Reference in New Issue
Block a user