==> 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}