PatchworkOS  621ae6b
A non-POSIX operating system.
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1#include "manifest.h"
2
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <sys/argsplit.h>
8#include <sys/defs.h>
9#include <sys/io.h>
10#include <sys/proc.h>
11
12/**
13 * @brief Box Daemon.
14 * @defgroup programs_boxd Box Daemon
15 * @ingroup programs
16 *
17 * The box daemon is responsible for spawning and managing boxes.
18 *
19 * ## Spawning Boxes
20 *
21 * To spawn a box a request should be sent to the "boxspawn" socket in the format:
22 *
23 * ```
24 * [key=value ...] -- <box_name> [arg1 arg2 ...]
25 * ```
26 *
27 * Where the following values can be specified:
28 * - `stdin`: A shared file descriptor to use as standard input.
29 * - `stdout`: A shared file descriptor to use as standard output.
30 * - `stderr`: A shared file descriptor to use as standard error.
31 * - `group`: A shared file descriptor to use as the process group (`/proc/[pid]/group`)
32 * - `namespace`: A shared file descriptor to use as the process namespace (`/proc/[pid]/ns`).
33 *
34 * @note The `stdin`, `stdout`, `stderr` and `group` values will only be used if the box is a foreground box,
35 * meanwhile the `namespace` will only be used if the box uses the `inherit` sandbox profile.
36 *
37 * @todo Implement group and namespace specification for foreground boxes and the inherit profile.
38 *
39 * The "boxspawn" socket will send a response in the format:
40 *
41 * ```
42 * <background|foreground [key]|error [msg]>
43 * ```
44 *
45 * On success, the response will either contain `background` if the box is a background box, or `foreground`
46 * followed by a key for the boxes `/proc/[pid]/wait` file if the box is a foreground box.
47 *
48 * On failure, the response will contain `error` followed by an error message.
49 *
50 * @todo Once filesystem servers are implemented the box deamon should use them instead of sockets.
51 *
52 * @todo Add a system for specifying environment variables.
53 *
54 * @{
55 */
56
57#define ARGV_MAX 512
58#define BUFFER_MAX 0x1000
59
60typedef struct
61{
62 char input[BUFFER_MAX];
63 char result[BUFFER_MAX];
65
66typedef struct
67{
68 const char* box;
69 const char** argv;
71 fd_t stdio[3];
73 fd_t namespace;
75
76static uint64_t box_args_parse(box_args_t* args, uint64_t argc, const char** argv, box_spawn_t* ctx)
77{
78 for (uint64_t i = 0; i < argc; i++)
79 {
80 if (strcmp(argv[i], "--") == 0)
81 {
82 if (i + 1 >= argc)
83 {
84 snprintf(ctx->result, sizeof(ctx->result), "error due to missing box name");
85 return ERR;
86 }
87
88 args->box = argv[i + 1];
89 args->argv = &argv[i + 1];
90 args->argc = argc - (i + 1);
91 break;
92 }
93
94 char* equalSign = strchr(argv[i], '=');
95 if (equalSign == NULL)
96 {
97 continue;
98 }
99
100 *equalSign = '\0';
101
102 const char* key = argv[i];
103 const char* value = equalSign + 1;
104
105 if (strcmp(key, "stdin") == 0)
106 {
107 args->stdio[STDIN_FILENO] = claim(value);
108 if (args->stdio[STDIN_FILENO] == ERR)
109 {
110 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid stdin");
111 return ERR;
112 }
113 }
114 else if (strcmp(key, "stdout") == 0)
115 {
116 args->stdio[STDOUT_FILENO] = claim(value);
117 if (args->stdio[STDOUT_FILENO] == ERR)
118 {
119 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid stdout");
120 return ERR;
121 }
122 }
123 else if (strcmp(key, "stderr") == 0)
124 {
125 args->stdio[STDERR_FILENO] = claim(value);
126 if (args->stdio[STDERR_FILENO] == ERR)
127 {
128 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid stderr");
129 return ERR;
130 }
131 }
132 else if (strcmp(key, "group") == 0)
133 {
134 args->group = claim(value);
135 if (args->group == ERR)
136 {
137 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid group");
138 return ERR;
139 }
140 }
141 else if (strcmp(key, "namespace") == 0)
142 {
143 args->namespace = claim(value);
144 if (args->namespace == ERR)
145 {
146 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid namespace");
147 return ERR;
148 }
149 }
150 else
151 {
152 snprintf(ctx->result, sizeof(ctx->result), "error due to unknown argument '%s'", key);
153 return ERR;
154 }
155 }
156
157 if (args->box == NULL || strchr(args->box, '/') != NULL || strchr(args->box, '.') != NULL)
158 {
159 snprintf(ctx->result, sizeof(ctx->result), "error due to missing box name");
160 return ERR;
161 }
162
163 return 0;
164}
165
166static void box_spawn(box_spawn_t* ctx)
167{
168 box_args_t args = {.box = NULL, .stdio = {FD_NONE}, .group = FD_NONE, .namespace = FD_NONE};
169 fd_t ctl = FD_NONE;
170 pid_t pid = ERR;
171
172 char argBuffer[BUFFER_MAX];
173 uint64_t argc;
174 const char** argv = argsplit_buf(argBuffer, sizeof(argBuffer), ctx->input, BUFFER_MAX, &argc);
175 if (argv == NULL || argc == 0)
176 {
177 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid request");
178 goto error;
179 }
180
181 if (box_args_parse(&args, argc, argv, ctx) == ERR)
182 {
183 goto error;
184 }
185
187 if (manifest_parse(&manifest, F("/box/%s/manifest", args.box)) == ERR)
188 {
189 snprintf(ctx->result, sizeof(ctx->result), "error due to invalid manifest for box '%s'", args.box);
190 goto error;
191 }
192
194 {"BOX", F("/box/%s/", args.box)},
195 };
197
198 section_t* exec = &manifest.sections[SECTION_EXEC];
199 char* bin = manifest_get_value(exec, "bin");
200 if (bin == NULL)
201 {
202 snprintf(ctx->result, sizeof(ctx->result), "error due to manifest of '%s' missing 'bin' entry", args.box);
203 goto error;
204 }
205
207 if (priority == ERR)
208 {
209 snprintf(ctx->result, sizeof(ctx->result), "error due to manifest of '%s' missing 'priority' entry", args.box);
210 goto error;
211 }
212
214 const char* profile = manifest_get_value(sandbox, "profile");
215 if (profile == NULL)
216 {
217 profile = "empty";
218 }
219
220 const char* foreground = manifest_get_value(sandbox, "foreground");
221 bool isForeground = foreground != NULL && strcmp(foreground, "true") == 0;
222 bool shouldInheritNamespace = false;
223
225 if (strcmp(profile, "empty") == 0)
226 {
228 }
229 else if (strcmp(profile, "inherit") == 0)
230 {
232 }
233 else
234 {
235 snprintf(ctx->result, sizeof(ctx->result), "error due to manifest of '%s' having invalid 'profile' entry",
236 args.box);
237 goto error;
238 }
239
240 args.argv[0] = bin;
241 pid = spawn(args.argv, flags);
242 if (pid == ERR)
243 {
244 snprintf(ctx->result, sizeof(ctx->result), "error due to spawn failure for '%s' (%s)", args.box,
245 strerror(errno));
246 goto error;
247 }
248
249 if (swritefile(F("/proc/%llu/prio", pid), F("%llu", priority)) == ERR)
250 {
251 snprintf(ctx->result, sizeof(ctx->result), "error due to priority failure for '%s' (%s)", args.box,
252 strerror(errno));
253 goto error;
254 }
255
256 section_t* env = &manifest.sections[SECTION_ENV];
257 for (uint64_t i = 0; i < env->amount; i++)
258 {
259 if (swritefile(F("/proc/%llu/env/%s:cw", pid, env->entries[i].key), env->entries[i].value) == ERR)
260 {
261 snprintf(ctx->result, sizeof(ctx->result), "error due to env var failure for '%s' (%s)", args.box,
262 strerror(errno));
263 goto error;
264 }
265 }
266
267 ctl = open(F("/proc/%llu/ctl", pid));
268 if (ctl == ERR)
269 {
270 snprintf(ctx->result, sizeof(ctx->result), "error due to ctl open failure for '%s' (%s)", args.box,
271 strerror(errno));
272 goto error;
273 }
274
276 {
277 if (swrite(ctl, F("setns %llu", args.namespace)) == ERR)
278 {
279 snprintf(ctx->result, sizeof(ctx->result), "error due to setns failure for '%s' (%s)", args.box,
280 strerror(errno));
281 goto error;
282 }
283 }
284 else
285 {
286 if (swrite(ctl, "mount /:Lrwx tmpfs") == ERR)
287 {
288 snprintf(ctx->result, sizeof(ctx->result), "error due to root mount failure for '%s' (%s)", args.box,
289 strerror(errno));
290 goto error;
291 }
292 }
293
294 section_t* namespace = &manifest.sections[SECTION_NAMESPACE];
295 for (uint64_t i = 0; i < namespace->amount; i++)
296 {
297 char* key = namespace->entries[i].key;
298 char* value = namespace->entries[i].value;
299
300 if (swrite(ctl, F("touch %s:rwcp && bind %s %s", key, key, value)) == ERR)
301 {
302 printf("boxd: failed to bind '%s' to '%s' (%s)\n", key, value, strerror(errno));
303 goto error;
304 }
305 }
306
307 if (isForeground)
308 {
309 for (uint8_t i = 0; i < 3; i++)
310 {
311 if (args.stdio[i] == FD_NONE)
312 {
313 continue;
314 }
315
316 if (swrite(ctl, F("dup2 %llu %llu", args.stdio[i], i)) == ERR)
317 {
318 snprintf(ctx->result, sizeof(ctx->result), "error due to dup2 failure for '%s' (%s)", args.box,
319 strerror(errno));
320 goto error;
321 }
322 }
323
324 if (swrite(ctl, F("setgroup %llu", args.group)) == ERR)
325 {
326 snprintf(ctx->result, sizeof(ctx->result), "error due to setns failure for '%s' (%s)", args.box,
327 strerror(errno));
328 goto error;
329 }
330
331 if (swrite(ctl, "close 3 -1") == ERR)
332 {
333 snprintf(ctx->result, sizeof(ctx->result), "error due to close failure for '%s' (%s)", args.box,
334 strerror(errno));
335 goto error;
336 }
337
338 fd_t wait = open(F("/proc/%llu/wait", pid));
339 if (wait == ERR)
340 {
341 snprintf(ctx->result, sizeof(ctx->result), "error due to wait open failure for '%s' (%s)", args.box,
342 strerror(errno));
343 goto error;
344 }
345
346 char waitKey[KEY_128BIT];
347 if (share(waitKey, sizeof(waitKey), wait, CLOCKS_PER_SEC) == ERR)
348 {
349 close(wait);
350 snprintf(ctx->result, sizeof(ctx->result), "error due to wait share failure for '%s' (%s)", args.box,
351 strerror(errno));
352 goto error;
353 }
354 close(wait);
355
356 snprintf(ctx->result, sizeof(ctx->result), "foreground %s", waitKey);
357 }
358 else
359 {
360 if (swrite(ctl, "close 0 -1") == ERR)
361 {
362 snprintf(ctx->result, sizeof(ctx->result), "error due to close failure for '%s' (%s)", args.box,
363 strerror(errno));
364 goto error;
365 }
366
367 snprintf(ctx->result, sizeof(ctx->result), "background");
368 }
369
370 if (swrite(ctl, "start") == ERR)
371 {
372 snprintf(ctx->result, sizeof(ctx->result), "error due to start failure for '%s' (%s)", args.box,
373 strerror(errno));
374 goto error;
375 }
376
377 goto cleanup;
378error:
379 if (pid != ERR)
380 {
381 kill(pid);
382 }
383cleanup:
384 for (int i = 0; i < 3; i++)
385 {
386 if (args.stdio[i] != FD_NONE)
387 {
388 close(args.stdio[i]);
389 }
390 }
391 if (args.group != FD_NONE)
392 {
393 close(args.group);
394 }
395 if (args.namespace != FD_NONE)
396 {
397 close(args.namespace);
398 }
399 if (ctl != FD_NONE)
400 {
401 close(ctl);
402 }
403}
404
405int main(void)
406{
407 /// @todo Use nonblocking sockets to avoid hanging on accept or read, or just wait until we have filesystem servers
408 /// and do that instead.
409
410 char* id = sreadfile("/net/local/seqpacket");
411 if (id == NULL)
412 {
413 printf("boxd: failed to open local seqpacket socket (%s)\n", strerror(errno));
414 abort();
415 }
416
417 if (swritefile(F("/net/local/%s/ctl", id), "bind boxspawn && listen") == ERR)
418 {
419 printf("boxd: failed to bind to box (%s)\n", strerror(errno));
420 goto error;
421 }
422
423 printf("boxd: listening for connections...\n");
424 while (1)
425 {
426 fd_t client = open(F("/net/local/%s/accept", id));
427 if (client == ERR)
428 {
429 printf("boxd: failed to accept connection (%s)\n", strerror(errno));
430 goto error;
431 }
432
433 box_spawn_t ctx = {0};
434 if (read(client, ctx.input, sizeof(ctx.input) - 1) == ERR)
435 {
436 printf("boxd: failed to read request (%s)\n", strerror(errno));
437 close(client);
438 continue;
439 }
440
441 box_spawn(&ctx);
442
443 if (swrite(client, ctx.result) == ERR)
444 {
445 printf("boxd: failed to write response (%s)\n", strerror(errno));
446 }
447
448 close(client);
449 }
450
451error:
452 free(id);
453 return EXIT_FAILURE;
454}
int main(void)
Definition main.c:5
int64_t y
Definition main.c:153
#define CLOCKS_PER_SEC
Definition clock_t.h:15
#define errno
Error number variable.
Definition errno.h:27
const char ** argsplit_buf(void *buf, uint64_t size, const char *str, uint64_t maxLen, uint64_t *count)
Standardized argument parsing function using a provided buffer.
Definition argsplit_buf.c:3
#define ARRAY_SIZE(x)
Get the number of elements in a static array.
Definition defs.h:108
#define KEY_128BIT
The size of a buffer needed to hold a 128-bit key.
Definition io.h:509
fd_t open(const char *path)
System call for opening files.
Definition open.c:9
uint64_t close(fd_t fd)
System call for closing files.
Definition close.c:9
#define F(format,...)
Allocates a formatted string on the stack.
Definition io.h:66
size_t read(fd_t fd, void *buffer, size_t count)
System call for reading from files.
Definition read.c:9
size_t swrite(fd_t fd, const char *string)
Wrapper for writing a null-terminated string to a file.
Definition swrite.c:4
size_t swritefile(const char *path, const char *string)
Wrapper for writing a null-terminated string directly to a file using a path.
Definition swritefile.c:4
fd_t claim(const char *key)
System call for claiming a shared file descriptor.
Definition claim.c:6
#define STDOUT_FILENO
Standard output file descriptor.
Definition io.h:35
char * sreadfile(const char *path)
Wrapper for reading an entire file directly into a null-terminated string.
Definition sreadfile.c:3
#define STDERR_FILENO
Standard error file descriptor.
Definition io.h:36
uint64_t share(char *key, uint64_t size, fd_t fd, clock_t timeout)
System call for sharing a file descriptor with another process.
Definition share.c:6
#define STDIN_FILENO
Standard input file descriptor.
Definition io.h:34
spawn_flags_t
Spawn behaviour flags.
Definition proc.h:51
pid_t spawn(const char **argv, spawn_flags_t flags)
System call for spawning new processes.
Definition spawn.c:6
uint64_t kill(pid_t pid)
Helper for sending the "kill" command to a process.
Definition kill.c:4
@ SPAWN_EMPTY_GROUP
Don't inherit the parent's process group, instead create a new group.
Definition proc.h:65
@ SPAWN_EMPTY_NS
Create a new empty namespace, the new namespace will not contain any mountpoints or even a root.
Definition proc.h:67
@ SPAWN_SUSPEND
Definition proc.h:60
@ SPAWN_EMPTY_ENV
Don't inherit the parent's environment variables.
Definition proc.h:63
@ SPAWN_EMPTY_CWD
Don't inherit the parent's current working directory, starts at root (/).
Definition proc.h:64
#define NULL
Pointer error value.
Definition NULL.h:23
#define FD_NONE
No file descriptor constant.
Definition fd_t.h:22
#define ERR
Integer error value.
Definition ERR.h:17
__UINT64_TYPE__ fd_t
A file descriptor.
Definition fd_t.h:12
__UINT64_TYPE__ pid_t
Process Identifier.
Definition pid_t.h:11
uint64_t manifest_parse(manifest_t *manifest, const char *path)
Definition manifest.c:35
char * manifest_get_value(section_t *section, const char *key)
Definition manifest.c:168
void manifest_substitute(manifest_t *manifest, substitution_t *substitutions, uint64_t amount)
Definition manifest.c:124
uint64_t manifest_get_integer(section_t *section, const char *key)
Definition manifest.c:180
@ SECTION_EXEC
Definition manifest.h:83
@ SECTION_NAMESPACE
Definition manifest.h:86
@ SECTION_ENV
Definition manifest.h:85
@ SECTION_SANDBOX
Definition manifest.h:84
const char ** argv
Definition main.c:69
const char * box
Definition main.c:68
#define BUFFER_MAX
Definition main.c:58
fd_t group
Definition main.c:72
char result[BUFFER_MAX]
Definition main.c:63
uint64_t argc
Definition main.c:70
static uint64_t box_args_parse(box_args_t *args, uint64_t argc, const char **argv, box_spawn_t *ctx)
Definition main.c:76
char input[BUFFER_MAX]
Definition main.c:62
static void box_spawn(box_spawn_t *ctx)
Definition main.c:166
fd_t stdio[3]
Definition main.c:71
static const path_flag_t flags[]
Definition path.c:46
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
_PUBLIC int printf(const char *_RESTRICT format,...)
Definition printf.c:5
_PUBLIC int snprintf(char *_RESTRICT s, size_t n, const char *_RESTRICT format,...)
Definition snprintf.c:3
#define EXIT_FAILURE
Definition stdlib.h:47
_PUBLIC _NORETURN void abort(void)
Definition abort.c:9
_PUBLIC void free(void *ptr)
Definition free.c:11
_PUBLIC char * strerror(int errnum)
Definition strerror.c:6
_PUBLIC int strcmp(const char *s1, const char *s2)
Definition strcmp.c:3
_PUBLIC char * strchr(const char *s, int c)
Definition strchr.c:3
char key[MANIFEST_STRING_MAX]
Definition manifest.h:70
char value[MANIFEST_STRING_MAX]
Definition manifest.h:71
uint64_t amount
Definition manifest.h:77
section_entry_t entries[MANIFEST_SECTION_MAX]
Definition manifest.h:76