From 8dfff0655a2f93cc540bbaf7b88fb820431ecd02 Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Fri, 7 Jun 2024 22:05:55 +0530 Subject: src/jobs: init implement data structures and json unmarshalling --- flake.nix | 2 + include/jobs.h | 22 +++++++ include/util.h | 9 +++ meson.build | 1 + src/jobs.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/meson.build | 6 +- src/util.c | 77 ++++++++++++++++++++++++ 7 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 include/jobs.h create mode 100644 src/jobs.c create mode 100644 src/util.c diff --git a/flake.nix b/flake.nix index 7d5416c..33f3798 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ name = "dev"; buildInputs = with pkgs; [ + jq cjson nix-eval-jobs @@ -51,6 +52,7 @@ ]; buildInputs = with pkgs; [ cjson + nix-eval-jobs ]; postInstall = '' diff --git a/include/jobs.h b/include/jobs.h new file mode 100644 index 0000000..571be0d --- /dev/null +++ b/include/jobs.h @@ -0,0 +1,22 @@ +#include +#include + +LIST_HEAD(output_dlist, output); +struct output { + char *name, *store_path; + LIST_ENTRY(output) dlist; +}; + +LIST_HEAD(job_dlist, job); +struct job { + char *drv_path, *name; + struct output_dlist outputs; + struct job_dlist deps; + + LIST_ENTRY(job) dlist; +}; + +int jobs_init(FILE **stream); +int job_new(struct job **j, char *name, char *drv_path); +void job_free(struct job *j); +int jobs_read(FILE *stream, struct job *jobs); diff --git a/include/util.h b/include/util.h index e69de29..c5aa0ab 100644 --- a/include/util.h +++ b/include/util.h @@ -0,0 +1,9 @@ +#include + +#include + +#define print_err(fmt, ...) \ + fprintf(stderr, "[%s:%d] " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) + +int json_streaming_read(FILE *stream, cJSON **json); +int vpopen(FILE **stream, const char *file, char *const argv[]); diff --git a/meson.build b/meson.build index d5f4f82..cac287a 100644 --- a/meson.build +++ b/meson.build @@ -11,6 +11,7 @@ project( add_project_arguments( [ + '-D_POSIX_C_SOURCE=200809L', '-Wvla', ], language: 'c', diff --git a/src/jobs.c b/src/jobs.c new file mode 100644 index 0000000..7893632 --- /dev/null +++ b/src/jobs.c @@ -0,0 +1,178 @@ +#include +#include +#include +#include + +#include + +#include "jobs.h" +#include "util.h" + +static int job_output_insert(struct job *j, char *name, char *store_path); + +int job_read(FILE *stream, struct job **job) +{ + int ret; + cJSON *root, *temp, *input_drvs, *array; + struct job *dep_job; + char *name = NULL; + char *out_name = NULL; + char *drv_path = NULL; + + ret = json_streaming_read(stream, &root); + if (ret < 0) + return ret; + + temp = cJSON_GetObjectItemCaseSensitive(root, "name"); + if (!cJSON_IsString(temp)) { + ret = -EPERM; + goto out_free; + } + name = strdup(temp->valuestring); + if (name == NULL) { + ret = -EPERM; + goto out_free; + } + + temp = cJSON_GetObjectItemCaseSensitive(root, "drvPath"); + if (!cJSON_IsString(temp)) { + ret = -EPERM; + goto out_free; + } + drv_path = strdup(temp->valuestring); + if (drv_path == NULL) { + ret = -EPERM; + goto out_free; + } + + ret = job_new(job, name, drv_path); + if (ret < 0) + goto out_free; + + input_drvs = cJSON_GetObjectItemCaseSensitive(root, "inputDrvs"); + for (temp = input_drvs; temp != NULL; temp = temp->next) { + array = cJSON_GetObjectItemCaseSensitive(temp, temp->string); + if (!cJSON_IsArray(array)) { + ret = -EPERM; + job_free(*job); + goto out_free; + } + + drv_path = strdup(temp->string); + if (drv_path == NULL) { + ret = -EPERM; + job_free(*job); + goto out_free; + } + + ret = job_new(&dep_job, NULL, drv_path); + if (ret < 0) { + ret = -EPERM; + job_free(*job); + goto out_free; + } + + for (; array != NULL; array = array->next) { + out_name = strdup(array->string); + ret = job_output_insert(dep_job, out_name, NULL); + if (ret < 0) { + job_free(*job); + job_free(dep_job); + goto out_free; + } + } + + LIST_INSERT_HEAD(&(*job)->deps, dep_job, dlist); + drv_path = NULL; + } + +out_free: + cJSON_Delete(root); + + if (ret < 0) { + print_err("%s", "Invalid JSON"); + free(name); + free(drv_path); + } + return ret; +} + +void output_free(struct output *output) +{ + if (output == NULL) + return; + + free(output->name); + free(output->store_path); + + free(output); +} + +void job_free(struct job *job) +{ + struct job *j; + struct output *o; + + if (job == NULL) + return; + + free(job->name); + free(job->drv_path); + free(job); + LIST_FOREACH (o, &job->outputs, dlist) + output_free(o); + + LIST_FOREACH (j, &job->deps, dlist) + job_free(j); +} + +static int job_output_insert(struct job *j, char *name, char *store_path) +{ + struct output *o; + + o = malloc(sizeof(*o)); + if (o == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + o->name = name; + o->store_path = store_path; + LIST_INSERT_HEAD(&j->outputs, o, dlist); + + return 0; +} + +int job_new(struct job **j, char *name, char *drv_path) +{ + struct job *job; + + job = malloc(sizeof(*job)); + if (*j == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + job->name = name; + job->drv_path = drv_path; + LIST_INIT(&job->deps); + LIST_INIT(&job->outputs); + + *j = job; + return 0; +} + +int jobs_init(FILE **stream) +{ + int ret; + + /* TODO: proproperly handle args */ + char *const args[] = { + "--flake", + "github:sinanmohd/evanix#packages.x86_64-linux.evanix", + }; + + /* the package is wrapProgram-ed with nix-eval-jobs */ + ret = vpopen(stream, "nix-eval-jobs", args); + return ret; +} diff --git a/src/meson.build b/src/meson.build index d34718a..5fe292f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,10 @@ e = executable( 'evanix', - 'evanix.c', + [ + 'evanix.c', + 'jobs.c', + 'util.c', + ], include_directories: evanix_inc, dependencies: cjson_dep, diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..001180e --- /dev/null +++ b/src/util.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include + +#include + +#include "util.h" + +int json_streaming_read(FILE *stream, cJSON **json) +{ + int ret; + size_t n; + char *line = NULL; + + ret = getline(&line, &n, stream); + if (ret < 0) { + if (errno != 0) { + print_err("%s", strerror(errno)); + return -errno; + } + + return -EOF; + } + + *json = cJSON_Parse(line); + free(line); + if (cJSON_IsInvalid(*json)) { + print_err("%s", "Invalid JSON"); + return -EPERM; + } + + return 0; +} + +int vpopen(FILE **stream, const char *file, char *const argv[]) +{ + int fd[2]; + int ret; + + ret = pipe(fd); + if (ret < 0) { + print_err("%s", strerror(errno)); + return -errno; + } + + ret = fork(); + if (ret < 0) { + print_err("%s", strerror(errno)); + + close(fd[0]); + close(fd[1]); + return -errno; + } else if (ret > 0) { + close(fd[1]); + *stream = fdopen(fd[0], "r"); + if (*stream == NULL) { + print_err("%s", strerror(errno)); + return -errno; + } + + return 0; + } + + close(fd[0]); + ret = dup2(fd[1], STDOUT_FILENO); + if (ret < 0) + goto out_err; + + execvp(file, argv); + +out_err: + close(fd[1]); + print_err("%s", strerror(errno)); + exit(EXIT_FAILURE); +} -- cgit v1.2.3