#define _POSIX_C_SOURCE 200809L
#include "core/poll.h"
#include "core/ipc.h"
#include "core/pool.h"
#include "core/profile.h"
#include "app/repl.h"
#include "core/runtime.h"
#include "store/journal.h"
#include <rayforce.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
int main(int argc, char** argv) {
ray_runtime_t* rt = ray_runtime_create(argc, argv);
if (!rt) { fprintf(stderr, "failed to create runtime\n"); return 1; }
int rc = 0;
int interactive = 0;
const char* file = NULL;
uint16_t port = 0;
int n_cores = -1;
int timeit_init = 0;
const char* auth_pw = NULL;
bool auth_restricted = false;
const char* log_base = NULL;
ray_journal_mode_t log_mode = RAY_JOURNAL_OFF;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--interactive") == 0)
interactive = 1;
else if ((strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--port") == 0) && i + 1 < argc)
port = (uint16_t)atoi(argv[++i]);
else if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--cores") == 0) && i + 1 < argc) {
int v = atoi(argv[++i]);
if (v < 0) v = 0;
n_cores = v;
}
else if ((strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--timeit") == 0) && i + 1 < argc) {
int v = atoi(argv[++i]);
timeit_init = (v != 0);
}
else if ((strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--file") == 0) && i + 1 < argc) {
file = argv[++i];
}
else if (strcmp(argv[i], "-u") == 0 && i + 1 < argc) {
auth_pw = argv[++i];
auth_restricted = false;
}
else if (strcmp(argv[i], "-U") == 0 && i + 1 < argc) {
auth_pw = argv[++i];
auth_restricted = true;
}
else if (strcmp(argv[i], "-l") == 0 && i + 1 < argc) {
log_base = argv[++i];
log_mode = RAY_JOURNAL_ASYNC;
}
else if (strcmp(argv[i], "-L") == 0 && i + 1 < argc) {
log_base = argv[++i];
log_mode = RAY_JOURNAL_SYNC;
}
else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
fprintf(stdout,
"usage: %s [-f file] [-p port] [-c cores] [-t 0|1] [-i]"
" [-u PW | -U PW] [-l BASE | -L BASE] [file.rfl]\n"
" -f, --file FILE run script file (or pass as a positional arg)\n"
" -p, --port PORT listen for IPC clients on PORT\n"
" -c, --cores N worker-pool size (0 = auto: ncpu - 1, default)\n"
" -t, --timeit N enable profiler at startup (N != 0)\n"
" -i, --interactive start the REPL even after running a file\n"
" -u PW set plain auth password\n"
" -U PW set restricted auth password\n"
" -l BASE enable journal logging (BASE.log + BASE.qdb)\n"
" -L BASE enable journal logging with fsync per write\n"
" -h, --help show this message\n"
"\nFor a remote REPL, start rayforce locally and call\n"
"(.repl.connect \"host:port\") inside the REPL.\n"
"\nRunning as a service:\n"
" When stdin and stdout are both not a terminal (systemd\n"
" Type=simple, Docker, nohup &, etc.), rayforce skips the\n"
" REPL and runs only the IPC poll loop. Redirect stdout\n"
" and stderr to a file/journal — anything (println ...)\n"
" prints from the server side goes there. Example:\n"
" nohup rayforce -p 5000 > rayforce.log 2>&1 &\n"
" systemd: StandardOutput=journal StandardError=journal\n",
argv[0]);
ray_runtime_destroy(rt);
return 0;
}
else
file = argv[i];
}
if (n_cores >= 0) {
ray_err_t err = ray_pool_init((uint32_t)n_cores);
if (err != RAY_OK)
fprintf(stderr, "warning: ray_pool_init(%d) failed (%d)\n",
n_cores, (int)err);
}
if (timeit_init)
g_ray_profile.active = true;
ray_poll_t* poll = ray_poll_create();
if (poll) ray_runtime_set_poll(poll);
if (poll && auth_pw) {
size_t pw_len = strlen(auth_pw);
if (pw_len >= sizeof(poll->auth_secret))
pw_len = sizeof(poll->auth_secret) - 1;
memcpy(poll->auth_secret, auth_pw, pw_len);
poll->auth_secret[pw_len] = '\0';
poll->restricted = auth_restricted;
}
if (log_base) {
ray_err_t le = ray_journal_open(log_base, log_mode);
if (le != RAY_OK) {
fprintf(stderr, "log: open failed for base=%s (rc=%d)\n",
log_base, (int)le);
if (poll) ray_poll_destroy(poll);
ray_runtime_destroy(rt);
return 2;
}
}
if (port > 0) {
if (poll && ray_ipc_listen(poll, port) >= 0)
fprintf(stderr, "listening on port %u\n", port);
else
fprintf(stderr, "failed to listen on port %u: %s\n",
port, strerror(errno));
}
if (file) {
rc = ray_repl_run_file(file);
if (!interactive && !(port > 0)) goto done;
}
bool stdin_tty = isatty(STDIN_FILENO);
bool stdout_tty = isatty(STDOUT_FILENO);
bool server_only = poll && port > 0 && !file && !interactive
&& !stdin_tty && !stdout_tty;
if (server_only) {
#ifndef RAY_OS_WINDOWS
signal(SIGPIPE, SIG_IGN);
#endif
fprintf(stderr, "no terminal — running in server-only mode\n");
ray_poll_run(poll);
} else {
ray_repl_t* repl = ray_repl_create(poll);
if (repl) {
ray_repl_run(repl);
ray_repl_destroy(repl);
} else if (poll && port > 0) {
ray_poll_run(poll);
}
}
done:
ray_journal_close();
if (poll) ray_poll_destroy(poll);
ray_runtime_destroy(rt);
return rc;
}