add exec() and exec_bg() builtins to El runtime
- exec(cmd) -> String: runs shell command, captures stdout, 30s timeout - exec_bg(cmd) -> String: forks command in background, returns PID string - add both to codegen arity table (builtin_arity) - rebuild elc with updated arity table (self-hosting, identity-verified) - update release snapshot at releases/v1.0.0-20260501/
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <dlfcn.h> /* dlsym for http_set_handler fallback */
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
@@ -1876,6 +1877,61 @@ el_val_t exec_capture(el_val_t cmdv) {
|
||||
return el_wrap_str(b.buf);
|
||||
}
|
||||
|
||||
// exec — run a shell command via /bin/sh, capture stdout, return as String.
|
||||
// Times out after 30 seconds. Returns "" on any error.
|
||||
// El name: exec(cmd) -> String
|
||||
el_val_t exec(el_val_t cmdv) {
|
||||
const char* cmd = EL_CSTR(cmdv);
|
||||
if (!cmd || !*cmd) return el_wrap_str(el_strdup(""));
|
||||
/* Build a time-limited command: wrap with timeout(1) if available,
|
||||
* otherwise rely on the 30s read loop guard below. We use the simple
|
||||
* popen approach with a deadline measured by wall clock so the caller
|
||||
* is never blocked indefinitely. */
|
||||
FILE* f = popen(cmd, "r");
|
||||
if (!f) return el_wrap_str(el_strdup(""));
|
||||
JsonBuf b; jb_init(&b);
|
||||
char buf[4096];
|
||||
/* 30-second wall-clock deadline */
|
||||
time_t deadline = time(NULL) + 30;
|
||||
while (time(NULL) < deadline) {
|
||||
if (fgets(buf, sizeof(buf), f) == NULL) break;
|
||||
jb_puts(&b, buf);
|
||||
}
|
||||
pclose(f);
|
||||
return el_wrap_str(b.buf);
|
||||
}
|
||||
|
||||
// exec_bg — run a shell command in background, return PID as String.
|
||||
// The child process runs independently; the caller is not blocked.
|
||||
// Returns "" on fork failure.
|
||||
// El name: exec_bg(cmd) -> String
|
||||
el_val_t exec_bg(el_val_t cmdv) {
|
||||
const char* cmd = EL_CSTR(cmdv);
|
||||
if (!cmd || !*cmd) return el_wrap_str(el_strdup(""));
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
/* fork failed */
|
||||
return el_wrap_str(el_strdup(""));
|
||||
}
|
||||
if (pid == 0) {
|
||||
/* child: detach from parent's stdio, exec via shell */
|
||||
setsid();
|
||||
int devnull = open("/dev/null", O_RDWR);
|
||||
if (devnull >= 0) {
|
||||
dup2(devnull, STDIN_FILENO);
|
||||
dup2(devnull, STDOUT_FILENO);
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
close(devnull);
|
||||
}
|
||||
execl("/bin/sh", "sh", "-c", cmd, (char*)NULL);
|
||||
_exit(127);
|
||||
}
|
||||
/* parent: convert pid to string and return immediately */
|
||||
char pidbuf[32];
|
||||
snprintf(pidbuf, sizeof(pidbuf), "%d", (int)pid);
|
||||
return el_wrap_str(el_strdup(pidbuf));
|
||||
}
|
||||
|
||||
el_val_t fs_list(el_val_t pathv) {
|
||||
const char* path = EL_CSTR(pathv);
|
||||
el_val_t lst = el_list_empty();
|
||||
|
||||
@@ -742,6 +742,8 @@ el_val_t map_set(el_val_t map, el_val_t key, el_val_t value); /* el_map_set */
|
||||
/* ── Subprocess execution ────────────────────────────────────────────────── */
|
||||
el_val_t exec_command(el_val_t cmd); /* run shell command, return exit code */
|
||||
el_val_t exec_capture(el_val_t cmd); /* run shell command, capture stdout */
|
||||
el_val_t exec(el_val_t cmd); /* exec(cmd) → stdout String (30s timeout) */
|
||||
el_val_t exec_bg(el_val_t cmd); /* exec_bg(cmd) → PID String (non-blocking) */
|
||||
|
||||
el_val_t emit_log(el_val_t level, el_val_t msg, el_val_t fields_json);
|
||||
el_val_t emit_metric(el_val_t name, el_val_t value, el_val_t tags_json);
|
||||
|
||||
@@ -2049,6 +2049,11 @@ fn builtin_arity(name: String) -> Int {
|
||||
if str_eq(name, "bool_to_str") { return 1 }
|
||||
// Process
|
||||
if str_eq(name, "exit_program") { return 1 }
|
||||
// Subprocess execution
|
||||
if str_eq(name, "exec_command") { return 1 }
|
||||
if str_eq(name, "exec_capture") { return 1 }
|
||||
if str_eq(name, "exec") { return 1 }
|
||||
if str_eq(name, "exec_bg") { return 1 }
|
||||
// CGI / DHARMA
|
||||
if str_eq(name, "dharma_connect") { return 1 }
|
||||
if str_eq(name, "dharma_send") { return 2 }
|
||||
|
||||
Reference in New Issue
Block a user