diff --git a/services/herdd/CMakeLists.txt b/services/herdd/CMakeLists.txt index 9a70d25..796eba1 100644 --- a/services/herdd/CMakeLists.txt +++ b/services/herdd/CMakeLists.txt @@ -1,6 +1,9 @@ file(GLOB sources *.c) 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( NAME herdd diff --git a/services/herdd/log.c b/services/herdd/log.c new file mode 100644 index 0000000..0f2a9c8 --- /dev/null +++ b/services/herdd/log.c @@ -0,0 +1,31 @@ +#include + +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); +} diff --git a/services/herdd/log.h b/services/herdd/log.h new file mode 100644 index 0000000..1d780e1 --- /dev/null +++ b/services/herdd/log.h @@ -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 diff --git a/services/herdd/main.c b/services/herdd/main.c index dc6bf0f..e12015c 100644 --- a/services/herdd/main.c +++ b/services/herdd/main.c @@ -1,5 +1,14 @@ +#include "log.h" +#include "queue.h" +#include "runlevel.h" +#include "service.h" + +#include #include +#include +#include #include +#include #include #include #include @@ -7,61 +16,117 @@ #include #include +#ifdef TRACE +#define tracef(...) printf(__VA_ARGS__) +#else +#define tracef(...) +#endif + static void *thread_func(void *arg) { - kern_logf("started thread with arg %p", arg); + tracef("started thread with arg %p\n", arg); errno = 100; + tracef("thread done"); 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[]) { sys_remote_set(SYS_REMOTE_NSD, 0, 0); - kern_logf("herdd"); - kern_logf("args:"); + struct queue *runlevels = global_runlevels(); + struct queue *services = global_services(); - for (int i = 0; i < argc; i++) { - kern_logf("[%d]: %s", i, argv[i]); + int err = load_runlevels("/etc/herdd/runlevels", runlevels); + 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_logf("[%d]: %s", i, envp[i]); - } + kern_handle_t new_task = 123, new_space = 456; + 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()); - errno = 200; - -#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); + if (new_task == KERN_HANDLE_INVALID) { + kern_log("this is the child"); } else { - kern_logf("open() failed: %s", strerror(errno)); + kern_log("this is the parent"); } -#endif - pthread_t thread; - 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); + printf("hello\n"); return 0; } diff --git a/services/herdd/queue.c b/services/herdd/queue.c new file mode 100644 index 0000000..5c6060e --- /dev/null +++ b/services/herdd/queue.c @@ -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; +} diff --git a/services/herdd/queue.h b/services/herdd/queue.h new file mode 100644 index 0000000..787fb99 --- /dev/null +++ b/services/herdd/queue.h @@ -0,0 +1,100 @@ +#ifndef QUEUE_H_ +#define QUEUE_H_ + +#include +#include + +#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 diff --git a/services/herdd/runlevel.c b/services/herdd/runlevel.c index e69de29..3ec8415 100644 --- a/services/herdd/runlevel.c +++ b/services/herdd/runlevel.c @@ -0,0 +1,154 @@ +#include "runlevel.h" + +#include "log.h" +#include "service.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/services/herdd/runlevel.h b/services/herdd/runlevel.h index 0bd61ad..5667352 100644 --- a/services/herdd/runlevel.h +++ b/services/herdd/runlevel.h @@ -1,14 +1,22 @@ #ifndef RUNLEVEL_H_ #define RUNLEVEL_H_ +#include "queue.h" + #include -#define RUNLEVEL_DESCRIPTION_MAX 64 - struct runlevel { - char rl_description[RUNLEVEL_DESCRIPTION_MAX]; + char *rl_name; + char *rl_description; char **rl_requires; 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 diff --git a/services/herdd/service.c b/services/herdd/service.c index e69de29..3ee666c 100644 --- a/services/herdd/service.c +++ b/services/herdd/service.c @@ -0,0 +1,154 @@ +#include "service.h" + +#include "log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/services/herdd/service.h b/services/herdd/service.h index a05dba0..a021001 100644 --- a/services/herdd/service.h +++ b/services/herdd/service.h @@ -1,17 +1,24 @@ #ifndef SERVICE_H_ #define SERVICE_H_ -#define SVC_DESCRIPTION_MAX 64 - +#include "queue.h" enum service_role { SVC_ROLE_NONE = 0x00u, SVC_ROLE_NAMESPACE_PROVIDER = 0x01u, }; struct service { - char s_description[SVC_DESCRIPTION_MAX]; - enum service_role s_roles; + char *s_name; + char *s_description; 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