skip to content
==> src/args.c <==
1/* args.rs, flag by flag. */
2#include "mesh/args.h"
3#include "mesh/abi.h"
4#include "mesh/buf.h"
5#include "mesh/rsfmt.h"
6
7#include <errno.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12static const char *const CUDA_FLAGS[6] = {"--sms", "--nvjitlink", "--cuda-lib", "--isolation", "--mps-dir", "--mps-pool"};
13
14static char *q(const char *s) {
15    mesh_buf b = MESH_BUF_INIT;
16    mesh_rs_debug_cstr(&b, s);
17    return mesh_buf_take(&b);
18}
19
20int mesh_args_backend_of(int argc, char **argv, uint32_t *kind, char **err) {
21    for (int i = 0; i < argc; i++) {
22        if (strcmp(argv[i], "--backend") != 0) continue;
23        if (i + 1 >= argc) { *err = mesh_xstrdup("--backend needs a value"); return -1; }
24        const char *v = argv[i + 1];
25        if (!strcmp(v, "cuda")) *kind = MESH_BACKEND_CUDA;
26        else if (!strcmp(v, "metal")) *kind = MESH_BACKEND_METAL;
27        else if (!strcmp(v, "wasm")) *kind = MESH_BACKEND_WASM;
28        else {
29            char *qv = q(v);
30            *err = mesh_buf_fmt("--backend %s: cuda, metal or wasm", qv);
31            free(qv);
32            return -1;
33        }
34        return 0;
35    }
36    *kind = MESH_DEFAULT_BACKEND;
37    return 0;
38}
39
40static void set(char **dst, const char *v) {
41    free(*dst);
42    *dst = v ? mesh_xstrdup(v) : NULL;
43}
44
45static char *slurp(const char *path, char **err) {
46    FILE *f = fopen(path, "r");
47    if (!f) { *err = mesh_buf_fmt("%s (os error %d)", strerror(errno), errno); return NULL; }
48    mesh_buf b = MESH_BUF_INIT;
49    char chunk[4096];
50    size_t n;
51    while ((n = fread(chunk, 1, sizeof chunk, f)) > 0) mesh_buf_put(&b, chunk, n);
52    fclose(f);
53    mesh_buf_reserve(&b, 0);
54    return mesh_buf_take(&b);
55}
56
57int mesh_args_parse(int argc, char **argv, mesh_args *a, char **err) {
58    memset(a, 0, sizeof *a);
59    if (mesh_args_backend_of(argc, argv, &a->backend, err)) return -1;
60    a->control = mesh_buf_fmt("127.0.0.1:%u", (unsigned)MESH_CONTROL_PORT);
61    a->http = mesh_buf_fmt("0.0.0.0:%u", (unsigned)MESH_HTTP_PORT);
62    a->nvjitlink = mesh_xstrdup(MESH_NVJITLINK_DIR);
63    a->programs = mesh_xstrdup("out/programs");
64    a->isolation = MESH_ISOLATION_MODE_BLINK;
65    a->logs = mesh_xstrdup("logs");
66    a->blobs_cap_mb = 4096;
67    /* (flag, value): --peer, --peer-listen or --cluster, resolved once --system is known. */
68    const char *lflag = NULL, *lval = NULL;
69    for (int i = 0; i < argc; i++) {
70        const char *flag = argv[i];
71        if (a->backend != MESH_BACKEND_CUDA)
72            for (int k = 0; k < 6; k++)
73                if (!strcmp(flag, CUDA_FLAGS[k])) {
74                    *err = mesh_buf_fmt("%s: a CUDA flag; --backend %s has no such setting (no silent ignoring)", flag, MESH_BACKENDS[a->backend]);
75                    return -1;
76                }
77#define VAL(v)                                                                  \
78    const char *v = i + 1 < argc ? argv[++i] : NULL;                           \
79    if (!v) { *err = mesh_buf_fmt("%s needs a value", flag); return -1; }
80#define NUM(v, out)                                                             \
81    do {                                                                        \
82        if (mesh_rs_parse_u32((v), strlen(v), (out))) {                         \
83            char *qv = q(v);                                                    \
84            *err = mesh_buf_fmt("%s: bad number %s", flag, qv);                 \
85            free(qv);                                                           \
86            return -1;                                                          \
87        }                                                                       \
88    } while (0)
89        if (!strcmp(flag, "--backend")) { VAL(v); (void)v; }
90        else if (!strcmp(flag, "--sms")) { VAL(v); NUM(v, &a->sms); a->has_sms = true; }
91        else if (!strcmp(flag, "--control")) { VAL(v); set(&a->control, v); }
92        else if (!strcmp(flag, "--http")) { VAL(v); set(&a->http, v); }
93        else if (!strcmp(flag, "--system")) { VAL(v); NUM(v, &a->system); a->has_system = true; }
94        else if (!strcmp(flag, "--peer") || !strcmp(flag, "--peer-listen") || !strcmp(flag, "--cluster")) {
95            if (lflag) { *err = mesh_buf_fmt("%s: %s given already (one of --cluster, --peer, --peer-listen)", flag, lflag); return -1; }
96            VAL(v);
97            lflag = flag;
98            lval = v;
99        } else if (!strcmp(flag, "--nvjitlink")) { VAL(v); set(&a->nvjitlink, v); }
100        else if (!strcmp(flag, "--cuda-lib")) { VAL(v); (void)v; } /* handled before parsing (mesh_cuda_use_lib re-executes meshd with it) */
101        else if (!strcmp(flag, "--programs")) { VAL(v); set(&a->programs, v); }
102        else if (!strcmp(flag, "--state")) { VAL(v); set(&a->state, v); }
103        else if (!strcmp(flag, "--autoload")) a->autoload = true;
104        else if (!strcmp(flag, "--wasm-cache")) { VAL(v); set(&a->wasm_cache, v); }
105        else if (!strcmp(flag, "--isolation")) {
106            VAL(v);
107            if (!strcmp(v, "blink")) a->isolation = MESH_ISOLATION_MODE_BLINK;
108            else if (!strcmp(v, "mps")) a->isolation = MESH_ISOLATION_MODE_MPS;
109            else { char *qv = q(v); *err = mesh_buf_fmt("--isolation %s: blink or mps", qv); free(qv); return -1; }
110        } else if (!strcmp(flag, "--mps-dir")) { VAL(v); set(&a->mps_dir, v); }
111        else if (!strcmp(flag, "--mps-pool")) { VAL(v); NUM(v, &a->mps_pool); a->has_mps_pool = true; }
112        else if (!strcmp(flag, "--logs")) { VAL(v); set(&a->logs, v); }
113        else if (!strcmp(flag, "--fleet-state")) { VAL(v); set(&a->fleet_state, v); }
114        else if (!strcmp(flag, "--jobs-state")) { VAL(v); set(&a->jobs_state, v); }
115        else if (!strcmp(flag, "--blobs")) { VAL(v); set(&a->blobs, v); }
116        else if (!strcmp(flag, "--blobs-cap")) { VAL(v); uint32_t mb; NUM(v, &mb); a->blobs_cap_mb = mb; }
117        else if (!strcmp(flag, "--models")) { VAL(v); set(&a->models, v); }
118        else if (!strcmp(flag, "-h") || !strcmp(flag, "--help")) { *err = mesh_xstrdup(MESH_ARGS_USAGE); return -1; }
119        else {
120            char *qf = q(flag);
121            *err = mesh_buf_fmt("unknown flag %s\n%s", qf, MESH_ARGS_USAGE);
122            free(qf);
123            return -1;
124        }
125#undef VAL
126#undef NUM
127    }
128    if (!lflag && a->has_system) { *err = mesh_xstrdup("--system needs --cluster FILE (or --peer for system 0, --peer-listen for system 1)"); return -1; }
129    if (lflag && !a->has_system) { *err = mesh_buf_fmt("%s needs --system K", lflag); return -1; }
130    if (lflag) {
131        uint32_t k = a->system;
132        if (!strcmp(lflag, "--peer") && k == 0) a->topo = mcc_pair_dialer(lval);
133        else if (!strcmp(lflag, "--peer-listen") && k == 1) a->topo = mcc_pair_listener(lval);
134        else if (!strcmp(lflag, "--peer")) { *err = mesh_xstrdup("--peer is for --system 0"); return -1; }
135        else if (!strcmp(lflag, "--peer-listen")) { *err = mesh_xstrdup("--peer-listen is for --system 1"); return -1; }
136        else {
137            char *e = NULL;
138            char *text = slurp(lval, &e);
139            if (!text) { *err = mesh_buf_fmt("--cluster %s: %s", lval, e); free(e); return -1; }
140            if (!mcc_keys(text, &a->keys, &e)) { *err = mesh_buf_fmt("--cluster %s: %s", lval, e); free(e); free(text); return -1; }
141            a->has_keys = true;
142            if (!mcc_topology_parse(text, k, &a->topo, &e)) { *err = mesh_buf_fmt("--cluster %s: %s", lval, e); free(e); free(text); return -1; }
143            free(text);
144        }
145        a->cluster = true;
146    }
147    if (a->has_sms && a->sms == 0) { *err = mesh_xstrdup("--sms must be > 0"); return -1; }
148    if (a->cluster && !a->fleet_state) {
149        *err = mesh_xstrdup("a fleet member needs --fleet-state FILE (its term, vote and desired state survive restarts there)");
150        return -1;
151    }
152    if (!a->cluster && a->fleet_state) { *err = mesh_xstrdup("--fleet-state is for a fleet member (--cluster / --peer)"); return -1; }
153    /* In a fleet the job table's log and the blobs sit beside the fleet state (they must survive restarts as that does). */
154    if (!a->jobs_state && a->fleet_state) a->jobs_state = mesh_buf_fmt("%s.jobs", a->fleet_state);
155    if (!a->blobs && a->fleet_state) a->blobs = mesh_buf_fmt("%s.blobs", a->fleet_state);
156    if ((a->mps_dir || a->has_mps_pool) && a->isolation != MESH_ISOLATION_MODE_MPS) {
157        *err = mesh_xstrdup("--mps-dir and --mps-pool are for --isolation mps");
158        return -1;
159    }
160    return 0;
161}
162
163void mesh_args_free(mesh_args *a) {
164    free(a->wasm_cache);
165    free(a->control);
166    free(a->http);
167    if (a->cluster) mcc_topology_free(&a->topo);
168    free(a->nvjitlink);
169    free(a->programs);
170    free(a->state);
171    free(a->mps_dir);
172    free(a->logs);
173    free(a->fleet_state);
174    free(a->jobs_state);
175    free(a->blobs);
176    free(a->models);
177    if (a->has_keys) ms_free(&a->keys);
178    memset(a, 0, sizeof *a);
179}