aboutsummaryrefslogtreecommitdiff
path: root/snag/src/package_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'snag/src/package_db.c')
-rw-r--r--snag/src/package_db.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/snag/src/package_db.c b/snag/src/package_db.c
new file mode 100644
index 0000000..0bae257
--- /dev/null
+++ b/snag/src/package_db.c
@@ -0,0 +1,161 @@
+#include "package_db.h"
+#include "util.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#define PKG_INFO_CMD "./scripts/get_pkg_attrs.sh"
+#define PKG_INST_CMD "./scripts/run_pkg_install.sh"
+
+// note: fixed location of package database for now
+const char pkg_db[] = "../packages";
+const size_t pkg_db_len = sizeof(pkg_db) - 1;
+
+bool load_package_info(char* pkgid, package_info_t* info) {
+
+ memset(info, 0, sizeof(*info));
+ size_t pkgid_len = strlen(pkgid);
+
+ // construct package description directory path and ensure it exists
+ // pkg_dir = "pkg_db/pkgid"
+ size_t pkg_dir_len = pkg_db_len + pkgid_len + 1;
+ char* pkg_dir = emalloc(pkg_dir_len + 1, "pkg_dir");
+ {
+ memcpy(pkg_dir, pkg_db, pkg_db_len);
+ pkg_dir[pkg_db_len] = '/';
+ memcpy(pkg_dir + pkg_db_len + 1, pkgid, pkgid_len);
+ pkg_dir[pkg_dir_len] = '\0';
+ if (access(pkg_dir, F_OK) != 0) {
+ print("Package '%s' not found\n", pkgid);
+ free(pkg_dir);
+ return false;
+ }
+ }
+
+ // construct package description script path and ensure it exists
+ // script_path = "pkg_dir/pkgid.sh"
+ size_t script_path_len = pkg_dir_len + pkgid_len + 4;
+ info->script_path = emalloc(script_path_len + 1, "info->script_path");
+ {
+ memcpy(info->script_path, pkg_dir, pkg_dir_len);
+ info->script_path[pkg_dir_len] = '/';
+ memcpy(&info->script_path[pkg_dir_len + 1], pkgid, pkgid_len);
+ memcpy(&info->script_path[script_path_len - 3], ".sh", 3);
+ info->script_path[script_path_len] = '\0';
+ free(pkg_dir);
+ if (access(info->script_path, F_OK) != 0) {
+ print("Package '%s' broken, doesn't have description script\n", pkgid);
+ free(info->script_path);
+ return false;
+ }
+ }
+
+ // PIPE and FORK to run pkg attrs script
+ int pipefds[2] = {0};
+ if (pipe(pipefds) < 0) die("failed to pipe: %s", strerror(errno));
+ pid_t pid = fork();
+ if (pid < 0) die("failed to fork: %s", strerror(errno));
+
+ if (pid == 0) {
+ // child proc
+ // connect my stdout to the pipe
+ close(pipefds[0]);
+ dup2(pipefds[1], STDOUT_FILENO);
+ close(pipefds[1]);
+ // exec into get attr script
+ char* argv[] = { PKG_INFO_CMD, info->script_path, NULL };
+ char* envp[] = { NULL };
+ execve(PKG_INFO_CMD, argv, envp);
+ // what are you still doing here?
+ die("CHILD - execve to %s failed: %s", PKG_INFO_CMD, strerror(errno));
+ } else {
+ // parent proc, close write end of pipe
+ close(pipefds[1]);
+ }
+
+ // wait for child to finish
+ int wstatus;
+ if (waitpid(pid, &wstatus, 0) < 0) die("waitpid failed: %s", strerror(errno));
+ if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute get_pkg_attr script");
+
+ // read all bytes from pipe into attr buffer
+ ssize_t bytes_read = read(pipefds[0], info->attrs.buffer, sizeof(info->attrs.buffer));
+ if (bytes_read < 0) die("failed to read from pipe: %s", strerror(errno));
+ close(pipefds[0]);
+
+ // read attributes from attr buffer
+ // attribute format: type (char), data value (null terminated str)
+ // todo(jqj): switch over to dynamically allocated buffer
+ for (int i = 0; i < bytes_read;) {
+ char atype = info->attrs.buffer[i];
+ i++;
+ switch (atype) {
+ case 'i':
+ info->attrs.id = &info->attrs.buffer[i]; break;
+ case 'n':
+ info->attrs.name = &info->attrs.buffer[i]; break;
+ case 'd':
+ info->attrs.desc = &info->attrs.buffer[i]; break;
+ case 'w':
+ info->attrs.webpage = &info->attrs.buffer[i]; break;
+ case 'l':
+ info->attrs.license = &info->attrs.buffer[i]; break;
+ case 'v':
+ info->attrs.version = &info->attrs.buffer[i]; break;
+ case 'u':
+ info->attrs.source_url = &info->attrs.buffer[i]; break;
+ case 'h':
+ info->attrs.source_hash = &info->attrs.buffer[i]; break;
+ }
+ for(;info->attrs.buffer[i] && i < bytes_read; i++);
+ i++;
+ }
+
+ return true;
+}
+
+void run_package_install(const package_info_t* info) {
+
+ // PIPE and FORK to run install script
+ int pipefds[2] = {0};
+ if (pipe(pipefds) < 0) die("failed to pipe: %s", strerror(errno));
+ pid_t pid = fork();
+ if (pid < 0) die("failed to fork: %s", strerror(errno));
+
+ if (pid == 0) {
+ // child proc
+ // connect my stdout and stderr to the pipe
+ close(pipefds[0]);
+ dup2(pipefds[1], STDOUT_FILENO);
+ dup2(pipefds[1], STDERR_FILENO);
+ close(pipefds[1]);
+ // exec into install script
+ char* argv[] = { PKG_INST_CMD, info->script_path, NULL };
+ char* envp[] = { NULL };
+ execve(PKG_INST_CMD, argv, envp);
+ // what are you still doing here?
+ die("CHILD - execve to %s failed: %s", PKG_INST_CMD, strerror(errno));
+ } else {
+ // parent proc, close write end of pipe
+ close(pipefds[1]);
+ }
+
+ // read from the pipe until EOF
+ char buf[4096];
+ ssize_t bytes_read = 0;
+ while ((bytes_read = read(pipefds[0], buf, sizeof(buf))) > 0) {
+ // print build out, can log and stuff in future
+ printf("%.*s", (int)bytes_read, buf);
+ }
+ if (bytes_read < 0) die("failed to read from pipe: %s", strerror(errno));
+ close(pipefds[0]);
+
+ // reap the child to finish
+ int wstatus;
+ if (waitpid(pid, &wstatus, 0) < 0) die("waitpid failed: %s", strerror(errno));
+ if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute install script");
+}