services: herdd: implement scanning and parsing of runlevel and service config files
This commit is contained in:
@@ -1,6 +1,9 @@
|
|||||||
file(GLOB sources *.c)
|
file(GLOB sources *.c)
|
||||||
add_executable(herdd ${sources})
|
add_executable(herdd ${sources})
|
||||||
target_link_libraries(herdd libc libc-runtime libmango libpthread)
|
target_link_libraries(herdd
|
||||||
|
libc libc-runtime
|
||||||
|
libmango libpthread liblaunch librosetta
|
||||||
|
fx-core fx-ds fx-serial)
|
||||||
|
|
||||||
sysroot_add_program(
|
sysroot_add_program(
|
||||||
NAME herdd
|
NAME herdd
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void log(const char *format, ...)
|
||||||
|
{
|
||||||
|
fputs(" ", stdout);
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_ok(const char *format, ...)
|
||||||
|
{
|
||||||
|
fputs("[ OK ] ", stdout);
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_fail(const char *format, ...)
|
||||||
|
{
|
||||||
|
fputs("[ FAIL ] ", stdout);
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
vprintf(format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
fputc('\n', stdout);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef LOG_H_
|
||||||
|
#define LOG_H_
|
||||||
|
|
||||||
|
extern void log(const char *format, ...);
|
||||||
|
extern void log_ok(const char *format, ...);
|
||||||
|
extern void log_fail(const char *format, ...);
|
||||||
|
|
||||||
|
#endif
|
||||||
+102
-37
@@ -1,5 +1,14 @@
|
|||||||
|
#include "log.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "runlevel.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <fx/ds/string.h>
|
||||||
#include <mango/log.h>
|
#include <mango/log.h>
|
||||||
|
#include <mango/task.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -7,61 +16,117 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef TRACE
|
||||||
|
#define tracef(...) printf(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define tracef(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
static void *thread_func(void *arg)
|
static void *thread_func(void *arg)
|
||||||
{
|
{
|
||||||
kern_logf("started thread with arg %p", arg);
|
tracef("started thread with arg %p\n", arg);
|
||||||
errno = 100;
|
errno = 100;
|
||||||
|
tracef("thread done");
|
||||||
return (void *)0xdeadbeef;
|
return (void *)0xdeadbeef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int load_services(const char *dir, struct queue *out)
|
||||||
|
{
|
||||||
|
DIR *d = opendir(dir);
|
||||||
|
if (!d) {
|
||||||
|
printf("cannot open '%s': %s\n", dir, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *dent = NULL;
|
||||||
|
|
||||||
|
while ((dent = readdir(d))) {
|
||||||
|
char filepath[4096];
|
||||||
|
snprintf(filepath, sizeof filepath, "%s/%s", dir, dent->d_name);
|
||||||
|
tracef(" - %s\n", dent->d_name);
|
||||||
|
struct service *s = NULL;
|
||||||
|
int err = service_load(filepath, &s);
|
||||||
|
if (err != SUCCESS) {
|
||||||
|
printf("failed to load %s (%s)\n",
|
||||||
|
filepath,
|
||||||
|
strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_push_back(out, &s->s_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int load_runlevels(const char *dir, struct queue *out)
|
||||||
|
{
|
||||||
|
DIR *d = opendir(dir);
|
||||||
|
if (!d) {
|
||||||
|
printf("cannot open '%s': %s\n", dir, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *dent = NULL;
|
||||||
|
|
||||||
|
while ((dent = readdir(d))) {
|
||||||
|
char filepath[4096];
|
||||||
|
snprintf(filepath, sizeof filepath, "%s/%s", dir, dent->d_name);
|
||||||
|
tracef(" - %s\n", dent->d_name);
|
||||||
|
struct runlevel *rl = NULL;
|
||||||
|
int err = runlevel_load(filepath, &rl);
|
||||||
|
if (err != SUCCESS) {
|
||||||
|
printf("failed to load %s (%s)\n",
|
||||||
|
filepath,
|
||||||
|
strerror(errno));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracef("runlevel: %s\n", rl->rl_description);
|
||||||
|
for (size_t i = 0; i < rl->rl_requires_count; i++) {
|
||||||
|
tracef(" requires: %s\n", rl->rl_requires[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_push_back(out, &rl->rl_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(d);
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[], const char *envp[])
|
int main(int argc, const char *argv[], const char *envp[])
|
||||||
{
|
{
|
||||||
sys_remote_set(SYS_REMOTE_NSD, 0, 0);
|
sys_remote_set(SYS_REMOTE_NSD, 0, 0);
|
||||||
|
|
||||||
kern_logf("herdd");
|
struct queue *runlevels = global_runlevels();
|
||||||
kern_logf("args:");
|
struct queue *services = global_services();
|
||||||
|
|
||||||
for (int i = 0; i < argc; i++) {
|
int err = load_runlevels("/etc/herdd/runlevels", runlevels);
|
||||||
kern_logf("[%d]: %s", i, argv[i]);
|
err = load_services("/etc/herdd/services", services);
|
||||||
|
|
||||||
|
struct runlevel *rl = runlevel_find("single-user");
|
||||||
|
if (!rl) {
|
||||||
|
log_fail("Cannot find runlevel single-user.");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
kern_logf("env:");
|
runlevel_activate(rl);
|
||||||
|
|
||||||
for (int i = 0; envp[i]; i++) {
|
kern_handle_t new_task = 123, new_space = 456;
|
||||||
kern_logf("[%d]: %s", i, envp[i]);
|
kern_status_t status = task_duplicate(&new_task, &new_space);
|
||||||
}
|
kern_logf(
|
||||||
|
" task_duplicate returned %d (%x/%x)",
|
||||||
|
status,
|
||||||
|
new_task,
|
||||||
|
new_space);
|
||||||
|
|
||||||
kern_logf("self = %p", pthread_self());
|
if (new_task == KERN_HANDLE_INVALID) {
|
||||||
errno = 200;
|
kern_log("this is the child");
|
||||||
|
|
||||||
#if 1
|
|
||||||
int dir = open("/", 0);
|
|
||||||
if (dir >= 0) {
|
|
||||||
kern_logf("opened '/'");
|
|
||||||
char buf[4096] = {0};
|
|
||||||
struct dentry *dent = (struct dentry *)buf;
|
|
||||||
long len = getdents(dir, dent, sizeof buf);
|
|
||||||
if (len < 0) {
|
|
||||||
kern_logf("getdents failed (%s)", strerror(errno));
|
|
||||||
} else {
|
|
||||||
for (long i = 0; i < len;) {
|
|
||||||
dent = (struct dentry *)(buf + i);
|
|
||||||
kern_logf(" - %s", dent->d_name);
|
|
||||||
i += dent->d_reclen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(dir);
|
|
||||||
} else {
|
} else {
|
||||||
kern_logf("open() failed: %s", strerror(errno));
|
kern_log("this is the parent");
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
pthread_t thread;
|
printf("hello\n");
|
||||||
pthread_create(&thread, NULL, thread_func, (void *)0xdeafcafe);
|
|
||||||
kern_logf("started thread %p", thread);
|
|
||||||
void *ret = NULL;
|
|
||||||
pthread_join(thread, &ret);
|
|
||||||
kern_logf("thread returned %p", ret);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
#include "queue.h"
|
||||||
|
|
||||||
|
size_t queue_length(struct queue *q)
|
||||||
|
{
|
||||||
|
size_t i = 0;
|
||||||
|
struct queue_entry *x = q->q_first;
|
||||||
|
while (x) {
|
||||||
|
i++;
|
||||||
|
x = x->qe_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_insert_before(
|
||||||
|
struct queue *q,
|
||||||
|
struct queue_entry *entry,
|
||||||
|
struct queue_entry *before)
|
||||||
|
{
|
||||||
|
struct queue_entry *x = before->qe_prev;
|
||||||
|
if (x) {
|
||||||
|
x->qe_next = entry;
|
||||||
|
} else {
|
||||||
|
q->q_first = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->qe_prev = x;
|
||||||
|
|
||||||
|
before->qe_prev = entry;
|
||||||
|
entry->qe_next = before;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_insert_after(
|
||||||
|
struct queue *q,
|
||||||
|
struct queue_entry *entry,
|
||||||
|
struct queue_entry *after)
|
||||||
|
{
|
||||||
|
struct queue_entry *x = after->qe_next;
|
||||||
|
if (x) {
|
||||||
|
x->qe_prev = entry;
|
||||||
|
} else {
|
||||||
|
q->q_last = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->qe_prev = x;
|
||||||
|
|
||||||
|
after->qe_next = entry;
|
||||||
|
entry->qe_prev = after;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_push_front(struct queue *q, struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
if (q->q_first) {
|
||||||
|
q->q_first->qe_prev = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->qe_next = q->q_first;
|
||||||
|
entry->qe_prev = NULL;
|
||||||
|
|
||||||
|
q->q_first = entry;
|
||||||
|
|
||||||
|
if (!q->q_last) {
|
||||||
|
q->q_last = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_push_back(struct queue *q, struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
if (q->q_last) {
|
||||||
|
q->q_last->qe_next = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->qe_prev = q->q_last;
|
||||||
|
entry->qe_next = NULL;
|
||||||
|
|
||||||
|
q->q_last = entry;
|
||||||
|
|
||||||
|
if (!q->q_first) {
|
||||||
|
q->q_first = entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct queue_entry *queue_pop_front(struct queue *q)
|
||||||
|
{
|
||||||
|
struct queue_entry *x = q->q_first;
|
||||||
|
if (x) {
|
||||||
|
queue_delete(q, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct queue_entry *queue_pop_back(struct queue *q)
|
||||||
|
{
|
||||||
|
struct queue_entry *x = q->q_last;
|
||||||
|
if (x) {
|
||||||
|
queue_delete(q, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_delete(struct queue *q, struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == q->q_first) {
|
||||||
|
q->q_first = q->q_first->qe_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == q->q_last) {
|
||||||
|
q->q_last = q->q_last->qe_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->qe_next) {
|
||||||
|
entry->qe_next->qe_prev = entry->qe_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->qe_prev) {
|
||||||
|
entry->qe_prev->qe_next = entry->qe_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->qe_next = entry->qe_prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void queue_delete_all(struct queue *q)
|
||||||
|
{
|
||||||
|
struct queue_entry *x = q->q_first;
|
||||||
|
while (x) {
|
||||||
|
struct queue_entry *next = x->qe_next;
|
||||||
|
x->qe_next = x->qe_prev = NULL;
|
||||||
|
x = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
q->q_first = q->q_last = NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
#ifndef QUEUE_H_
|
||||||
|
#define QUEUE_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define QUEUE_CONTAINER(t, m, v) \
|
||||||
|
((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0))
|
||||||
|
|
||||||
|
#define QUEUE_INIT ((struct queue) {.q_first = NULL, .q_last = NULL})
|
||||||
|
#define QUEUE_ENTRY_INIT \
|
||||||
|
((struct queue_entry) {.qe_next = NULL, .qe_prev = NULL})
|
||||||
|
|
||||||
|
#define queue_foreach(iter_type, iter_name, queue_name, node_member) \
|
||||||
|
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||||
|
iter_type, \
|
||||||
|
node_member, \
|
||||||
|
queue_first(queue_name)); \
|
||||||
|
iter_name; \
|
||||||
|
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||||
|
iter_type, \
|
||||||
|
node_member, \
|
||||||
|
queue_next(&((iter_name)->node_member))))
|
||||||
|
|
||||||
|
#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \
|
||||||
|
for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||||
|
iter_type, \
|
||||||
|
node_member, \
|
||||||
|
queue_last(queue_name)); \
|
||||||
|
iter_name; \
|
||||||
|
iter_name = (iter_type *)QUEUE_CONTAINER( \
|
||||||
|
iter_type, \
|
||||||
|
node_member, \
|
||||||
|
queue_prev(&((iter_name)->node_member))))
|
||||||
|
|
||||||
|
struct queue_entry {
|
||||||
|
struct queue_entry *qe_next;
|
||||||
|
struct queue_entry *qe_prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct queue {
|
||||||
|
struct queue_entry *q_first;
|
||||||
|
struct queue_entry *q_last;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void queue_init(struct queue *q)
|
||||||
|
{
|
||||||
|
memset(q, 0x00, sizeof *q);
|
||||||
|
}
|
||||||
|
static inline bool queue_empty(struct queue *q)
|
||||||
|
{
|
||||||
|
return q->q_first == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct queue_entry *queue_first(struct queue *q)
|
||||||
|
{
|
||||||
|
return q->q_first;
|
||||||
|
}
|
||||||
|
static inline struct queue_entry *queue_last(struct queue *q)
|
||||||
|
{
|
||||||
|
return q->q_last;
|
||||||
|
}
|
||||||
|
static inline struct queue_entry *queue_next(struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
return entry->qe_next;
|
||||||
|
}
|
||||||
|
static inline struct queue_entry *queue_prev(struct queue_entry *entry)
|
||||||
|
{
|
||||||
|
return entry->qe_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern size_t queue_length(struct queue *q);
|
||||||
|
|
||||||
|
extern void queue_insert_before(
|
||||||
|
struct queue *q,
|
||||||
|
struct queue_entry *entry,
|
||||||
|
struct queue_entry *before);
|
||||||
|
extern void queue_insert_after(
|
||||||
|
struct queue *q,
|
||||||
|
struct queue_entry *entry,
|
||||||
|
struct queue_entry *after);
|
||||||
|
|
||||||
|
extern void queue_push_front(struct queue *q, struct queue_entry *entry);
|
||||||
|
extern void queue_push_back(struct queue *q, struct queue_entry *entry);
|
||||||
|
|
||||||
|
extern struct queue_entry *queue_pop_front(struct queue *q);
|
||||||
|
extern struct queue_entry *queue_pop_back(struct queue *q);
|
||||||
|
|
||||||
|
extern void queue_delete(struct queue *q, struct queue_entry *entry);
|
||||||
|
extern void queue_delete_all(struct queue *q);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#include "runlevel.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fx/core/stringstream.h>
|
||||||
|
#include <fx/ds/array.h>
|
||||||
|
#include <fx/ds/dict.h>
|
||||||
|
#include <fx/serial.h>
|
||||||
|
#include <mango/log.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static struct queue runlevels = QUEUE_INIT;
|
||||||
|
|
||||||
|
struct queue *global_runlevels(void)
|
||||||
|
{
|
||||||
|
return &runlevels;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_runlevel_data(fx_object *in, struct runlevel *out)
|
||||||
|
{
|
||||||
|
if (!fx_object_is_type(in, FX_TYPE_DICT)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_dict *in_runlevel = fx_dict_at(in, "runlevel");
|
||||||
|
if (!in_runlevel || !fx_object_is_type(in_runlevel, FX_TYPE_DICT)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_string *name = fx_dict_at(in_runlevel, "name");
|
||||||
|
if (!name || !fx_object_is_type(name, FX_TYPE_STRING)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_string *description = fx_dict_at(in_runlevel, "description");
|
||||||
|
if (!description || !fx_object_is_type(description, FX_TYPE_STRING)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_array *deps = fx_dict_at(in_runlevel, "requires");
|
||||||
|
if (deps && !fx_object_is_type(deps, FX_TYPE_ARRAY)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
size_t dep_count = fx_array_size(deps);
|
||||||
|
fx_iterator *it = fx_iterator_begin(deps);
|
||||||
|
fx_foreach_ptr(fx_string, dep, it)
|
||||||
|
{
|
||||||
|
if (!fx_object_is_type(dep, FX_TYPE_STRING)) {
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fx_iterator_unref(it);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->rl_requires_count = dep_count;
|
||||||
|
out->rl_requires = calloc(dep_count, sizeof(char *));
|
||||||
|
it = fx_iterator_begin(deps);
|
||||||
|
size_t i = 0;
|
||||||
|
fx_foreach_ptr(fx_string, dep, it)
|
||||||
|
{
|
||||||
|
out->rl_requires[i++] = fx_string_steal(dep);
|
||||||
|
}
|
||||||
|
fx_iterator_unref(it);
|
||||||
|
|
||||||
|
out->rl_name = fx_string_steal(name);
|
||||||
|
out->rl_description = fx_string_steal(description);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runlevel_load(const char *path, struct runlevel **out)
|
||||||
|
{
|
||||||
|
struct runlevel *rl = malloc(sizeof *rl);
|
||||||
|
if (!rl) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
if (!fp) {
|
||||||
|
free(rl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_stream *in = fx_stream_open_fp(fp);
|
||||||
|
fx_serial_ctx *ctx = fx_toml_serial_ctx_create();
|
||||||
|
fx_object *data;
|
||||||
|
fx_status status = fx_serial_ctx_deserialise(ctx, in, &data, 0);
|
||||||
|
if (!FX_OK(status)) {
|
||||||
|
free(rl);
|
||||||
|
errno = EFTYPE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parse_runlevel_data(data, rl) != 0) {
|
||||||
|
free(rl);
|
||||||
|
errno = EFTYPE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_stream_unref(in);
|
||||||
|
fx_object_unref(data);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
*out = rl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct runlevel *runlevel_find(const char *name)
|
||||||
|
{
|
||||||
|
struct queue_entry *cur = queue_first(&runlevels);
|
||||||
|
while (cur) {
|
||||||
|
struct runlevel *rl
|
||||||
|
= QUEUE_CONTAINER(struct runlevel, rl_entry, cur);
|
||||||
|
if (!strcmp(rl->rl_name, name)) {
|
||||||
|
return rl;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = queue_next(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int runlevel_activate(struct runlevel *rl)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < rl->rl_requires_count; i++) {
|
||||||
|
const char *svc_name = rl->rl_requires[i];
|
||||||
|
struct service *svc = service_find(svc_name);
|
||||||
|
if (!svc) {
|
||||||
|
log_fail("Cannot find service %s.", svc_name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = service_start(svc);
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_ok("Reached runlevel %s.", rl->rl_description);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
#ifndef RUNLEVEL_H_
|
#ifndef RUNLEVEL_H_
|
||||||
#define RUNLEVEL_H_
|
#define RUNLEVEL_H_
|
||||||
|
|
||||||
|
#include "queue.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define RUNLEVEL_DESCRIPTION_MAX 64
|
|
||||||
|
|
||||||
struct runlevel {
|
struct runlevel {
|
||||||
char rl_description[RUNLEVEL_DESCRIPTION_MAX];
|
char *rl_name;
|
||||||
|
char *rl_description;
|
||||||
char **rl_requires;
|
char **rl_requires;
|
||||||
size_t rl_requires_count;
|
size_t rl_requires_count;
|
||||||
|
struct queue_entry rl_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern struct queue *global_runlevels(void);
|
||||||
|
|
||||||
|
extern int runlevel_load(const char *path, struct runlevel **out);
|
||||||
|
extern struct runlevel *runlevel_find(const char *name);
|
||||||
|
extern int runlevel_activate(struct runlevel *rl);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fx/core/stringstream.h>
|
||||||
|
#include <fx/ds/array.h>
|
||||||
|
#include <fx/ds/dict.h>
|
||||||
|
#include <fx/serial.h>
|
||||||
|
#include <launch.h>
|
||||||
|
#include <mango/log.h>
|
||||||
|
#include <rosetta/bootstrap.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static struct queue services = QUEUE_INIT;
|
||||||
|
|
||||||
|
struct queue *global_services(void)
|
||||||
|
{
|
||||||
|
return &services;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_service_data(fx_object *in, struct service *out)
|
||||||
|
{
|
||||||
|
if (!fx_object_is_type(in, FX_TYPE_DICT)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_dict *in_service = fx_dict_at(in, "service");
|
||||||
|
if (!in_service || !fx_object_is_type(in_service, FX_TYPE_DICT)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_dict *in_unit = fx_dict_at(in, "unit");
|
||||||
|
if (!in_unit || !fx_object_is_type(in_unit, FX_TYPE_DICT)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_string *name = fx_dict_at(in_unit, "name");
|
||||||
|
if (!name || !fx_object_is_type(name, FX_TYPE_STRING)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_string *description = fx_dict_at(in_unit, "description");
|
||||||
|
if (!description || !fx_object_is_type(description, FX_TYPE_STRING)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_string *exec = fx_dict_at(in_service, "exec");
|
||||||
|
if (!description || !fx_object_is_type(description, FX_TYPE_STRING)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_array *roles = fx_dict_at(in_service, "roles");
|
||||||
|
if (roles && !fx_object_is_type(roles, FX_TYPE_ARRAY)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
fx_iterator *it = fx_iterator_begin(roles);
|
||||||
|
fx_foreach_ptr(fx_string, role, it)
|
||||||
|
{
|
||||||
|
if (!fx_object_is_type(role, FX_TYPE_STRING)) {
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fx_iterator_unref(it);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = fx_iterator_begin(roles);
|
||||||
|
size_t i = 0;
|
||||||
|
fx_foreach_ptr(fx_string, role, it)
|
||||||
|
{
|
||||||
|
const char *role_cstr = fx_string_ptr(role);
|
||||||
|
if (!strcmp(role_cstr, "NamespaceProvider")) {
|
||||||
|
out->s_roles |= SVC_ROLE_NAMESPACE_PROVIDER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fx_iterator_unref(it);
|
||||||
|
|
||||||
|
out->s_name = fx_string_steal(name);
|
||||||
|
out->s_description = fx_string_steal(description);
|
||||||
|
out->s_exec = fx_string_steal(exec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int service_load(const char *path, struct service **out)
|
||||||
|
{
|
||||||
|
struct service *rl = malloc(sizeof *rl);
|
||||||
|
if (!rl) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
if (!fp) {
|
||||||
|
free(rl);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_stream *in = fx_stream_open_fp(fp);
|
||||||
|
fx_serial_ctx *ctx = fx_toml_serial_ctx_create();
|
||||||
|
fx_object *data;
|
||||||
|
fx_status status = fx_serial_ctx_deserialise(ctx, in, &data, 0);
|
||||||
|
if (!FX_OK(status)) {
|
||||||
|
free(rl);
|
||||||
|
errno = EFTYPE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parse_service_data(data, rl) != 0) {
|
||||||
|
free(rl);
|
||||||
|
errno = EFTYPE;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fx_stream_unref(in);
|
||||||
|
fx_object_unref(data);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
*out = rl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct service *service_find(const char *name)
|
||||||
|
{
|
||||||
|
struct queue_entry *cur = queue_first(&services);
|
||||||
|
while (cur) {
|
||||||
|
struct service *s
|
||||||
|
= QUEUE_CONTAINER(struct service, s_entry, cur);
|
||||||
|
if (!strcmp(s->s_name, name)) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = queue_next(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int service_start(struct service *svc)
|
||||||
|
{
|
||||||
|
log("Starting %s...", svc->s_description);
|
||||||
|
|
||||||
|
log_ok("Started %s.", svc->s_description);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
#ifndef SERVICE_H_
|
#ifndef SERVICE_H_
|
||||||
#define SERVICE_H_
|
#define SERVICE_H_
|
||||||
|
|
||||||
#define SVC_DESCRIPTION_MAX 64
|
#include "queue.h"
|
||||||
|
|
||||||
enum service_role {
|
enum service_role {
|
||||||
SVC_ROLE_NONE = 0x00u,
|
SVC_ROLE_NONE = 0x00u,
|
||||||
SVC_ROLE_NAMESPACE_PROVIDER = 0x01u,
|
SVC_ROLE_NAMESPACE_PROVIDER = 0x01u,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct service {
|
struct service {
|
||||||
char s_description[SVC_DESCRIPTION_MAX];
|
char *s_name;
|
||||||
enum service_role s_roles;
|
char *s_description;
|
||||||
char *s_exec;
|
char *s_exec;
|
||||||
|
enum service_role s_roles;
|
||||||
|
struct queue_entry s_entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern struct queue *global_services(void);
|
||||||
|
|
||||||
|
extern int service_load(const char *path, struct service **out);
|
||||||
|
extern struct service *service_find(const char *name);
|
||||||
|
extern int service_start(struct service *svc);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user