aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2020-02-28 01:06:35 -0500
committerJoseph Hunkeler <jhunkeler@gmail.com>2020-02-28 01:06:35 -0500
commit511deab4f22a6c25f33cce1e206b656d520ab7e6 (patch)
tree669591ef38f61f0dc842e96b86aa1533f274269f /src
parentb6587f1d905e308cab713b1d5545e4667c80d6e4 (diff)
downloadspmc-511deab4f22a6c25f33cce1e206b656d520ab7e6.tar.gz
Improvements:
* Refactored a few function names * Can read package metadata * Can delete packages * Can download! and install packages at the same time * Can prompt the user before proceeding
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/config_global.c6
-rw-r--r--src/install.c105
-rw-r--r--src/internal_cmd.c34
-rw-r--r--src/metadata.c40
-rw-r--r--src/purge.c93
-rw-r--r--src/spm.c37
-rw-r--r--src/user_input.c29
8 files changed, 261 insertions, 84 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 06065a8..bac2769 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -30,6 +30,7 @@ add_executable(spm
shlib.c
user_input.c
metadata.c
+ purge.c
)
target_link_libraries(spm crypto ssl curl)
diff --git a/src/config_global.c b/src/config_global.c
index fb97a61..ee42935 100644
--- a/src/config_global.c
+++ b/src/config_global.c
@@ -169,6 +169,9 @@ SPM_Hierarchy *spm_hierarchy_init(char *basepath) {
fs->localstatedir = join((char *[]) {fs->rootdir, "var", NULL}, DIRSEPS);
fs->sysconfdir = join((char *[]) {fs->rootdir, "etc", NULL}, DIRSEPS);
fs->mandir = join((char *[]) {fs->datadir, "man", NULL}, DIRSEPS);
+ fs->tmpdir = join((char *[]) {fs->rootdir, "tmp", NULL}, DIRSEPS);
+ fs->dbdir = join((char *[]) {fs->localstatedir, "db", NULL}, DIRSEPS);
+ fs->dbrecdir = join((char *[]) {fs->dbdir, "records", NULL}, DIRSEPS);
return fs;
}
@@ -186,6 +189,9 @@ void spm_hierarchy_free(SPM_Hierarchy *fs) {
free(fs->localstatedir);
free(fs->sysconfdir);
free(fs->mandir);
+ free(fs->tmpdir);
+ free(fs->dbdir);
+ free(fs->dbrecdir);
free(fs);
}
diff --git a/src/install.c b/src/install.c
index 2655626..93d07b0 100644
--- a/src/install.c
+++ b/src/install.c
@@ -1,39 +1,10 @@
/**
* @file install.c
*/
+#include <url.h>
#include "spm.h"
-extern const char *METADATA_FILES[];
-
-/**
- * SPM packages contain metadata files that are not useful post-install and would amount to a lot of clutter.
- * This function removes these data files from a directory tree
- * @param _path
- * @return success=0, error=-1
- */
-int metadata_remove(const char *_path) {
- if (exists(_path) != 0) {
- perror(_path);
- fprintf(SYSERROR);
- return -1;
- }
-
- for (int i = 0; METADATA_FILES[i] != NULL; i++) {
- char path[PATH_MAX];
- sprintf(path, "%s%c%s", _path, DIRSEP, METADATA_FILES[i]);
- if (exists(path) != 0) {
- continue;
- }
- if (unlink(path) < 0) {
- perror(path);
- fprintf(SYSERROR);
- return -1;
- }
- }
- return 0;
-}
-
-void install_show_package(ManifestPackage *package) {
+void spm_install_show_package(ManifestPackage *package) {
if (package == NULL) {
fprintf(stderr, "ERROR: package was NULL\n");
return;
@@ -48,7 +19,7 @@ void install_show_package(ManifestPackage *package) {
* @param _package name of archive to install (not a path)
* @return success=0, exists=1, error=-1 (general), -2 (unable to create `destroot`)
*/
-int install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) {
+int spm_install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) {
char *package = strdup(_package);
if (!package) {
@@ -71,6 +42,35 @@ int install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) {
printf("Extracting archive: %s\n", package);
}
+ if (strstr(package, "://") != NULL) {
+ if (exists(fs->tmpdir) != 0) {
+ mkdirs(fs->tmpdir, 0755);
+ }
+ long response = 0;
+ char *url = strdup(package);
+ char *tmp_package = join_ex(DIRSEPS, fs->tmpdir, basename(package), NULL);
+ size_t tmp_package_len = strlen(tmp_package);
+
+ if (tmp_package_len > strlen(package)) {
+ char *tmp = realloc(package, (strlen(package) + 1) * sizeof(char));
+ if (tmp == NULL) {
+ perror("cannot realloc package path");
+ return -1;
+ }
+ package = tmp;
+ }
+ strcpy(package, tmp_package);
+
+ if (exists(tmp_package) != 0) {
+ if ((response = fetch(url, package)) >= 400) {
+ fprintf(stderr, "HTTP(%ld): %s\n", response, http_response_str(response));
+ return -1;
+ }
+ }
+ free(url);
+ free(tmp_package);
+ }
+
if (tar_extract_archive(package, tmpdir) != 0) {
fprintf(stderr, "%s: %s\n", package, strerror(errno));
free(package);
@@ -81,7 +81,7 @@ int install(SPM_Hierarchy *fs, const char *tmpdir, const char *_package) {
return 0;
}
-int install_package_record(SPM_Hierarchy *fs, char *tmpdir, char *package_name) {
+int spm_install_package_record(SPM_Hierarchy *fs, char *tmpdir, char *package_name) {
RuntimeEnv *rt = runtime_copy(__environ);
char *records_topdir = join((char *[]) {fs->localstatedir, "db", "records", NULL}, DIRSEPS);
char *records_pkgdir = join((char *[]) {records_topdir, package_name, NULL}, DIRSEPS);
@@ -122,14 +122,15 @@ int install_package_record(SPM_Hierarchy *fs, char *tmpdir, char *package_name)
return 0;
}
-int is_installed(SPM_Hierarchy *fs, char *package_name) {
+int spm_check_installed(SPM_Hierarchy *fs, char *package_name) {
char *records_topdir = join((char *[]) {fs->localstatedir, "db", "records", NULL}, DIRSEPS);
char *records_pkgdir = join((char *[]) {records_topdir, package_name, NULL}, DIRSEPS);
+
char *descriptor = join((char *[]) {records_pkgdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS);
char *filelist = join((char *[]) {records_pkgdir, SPM_META_FILELIST, NULL}, DIRSEPS);
char **data = NULL;
- if (exists(records_pkgdir) != 0) {
+ if ((exists(records_pkgdir) || exists(descriptor) || exists(descriptor)) != 0) {
free(records_topdir);
free(records_pkgdir);
free(descriptor);
@@ -137,7 +138,7 @@ int is_installed(SPM_Hierarchy *fs, char *package_name) {
return 0; // does not exist
}
- data = metadata_read(filelist, SPM_METADATA_VERIFY);
+ data = spm_metadata_read(filelist, SPM_METADATA_VERIFY);
if (data == NULL) {
free(records_topdir);
free(records_pkgdir);
@@ -147,7 +148,6 @@ int is_installed(SPM_Hierarchy *fs, char *package_name) {
}
for (size_t i = 0; data[i] != NULL; i++) {
- printf("%zu: %s\n", i, data[i]);
free(data[i]);
}
free(data);
@@ -166,7 +166,7 @@ int is_installed(SPM_Hierarchy *fs, char *package_name) {
* @param packages
* @return 0=success, -1=failed to create storage, -2=denied by user
*/
-int do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
+int spm_do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
size_t num_requirements = 0;
ManifestPackage **requirements = NULL;
char source[PATH_MAX];
@@ -202,24 +202,13 @@ int do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
// Install packages
printf("Requested package(s):\n");
for (size_t i = 0; requirements !=NULL && requirements[i] != NULL; i++) {
- install_show_package(requirements[i]);
+ spm_install_show_package(requirements[i]);
}
if (SPM_GLOBAL.prompt_user) {
- int user_choice;
- int status_choice;
- printf("\nProceed with installation? [Y/n] ");
- while ((user_choice = getchar())) {
- status_choice = spm_user_yesno(user_choice, 1);
- if (status_choice == 0) { // No
- exit(-2);
- } else if (status_choice == 1) { // Yes
- break;
- } else { // Only triggers when spm_user_yesno's second argument is zero
- puts("Please answer 'y' or 'n'...");
- }
+ if (spm_prompt_user("Proceed with installation?", 1) == 0) {
+ exit(-2);
}
- puts("");
}
printf("Installing package(s):\n");
@@ -227,15 +216,15 @@ int do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
for (size_t i = 0; requirements != NULL && requirements[i] != NULL; i++) {
char *package_path = join((char *[]) {requirements[i]->origin, SPM_GLOBAL.repo_target, requirements[i]->archive, NULL}, DIRSEPS);
- if (is_installed(fs, requirements[i]->name)) {
+ if (spm_check_installed(fs, requirements[i]->name)) {
printf(" -> %s is already installed\n", requirements[i]->name);
free(package_path);
continue;
}
- install_show_package(requirements[i]);
- install(fs, tmpdir, package_path);
- install_package_record(fs, tmpdir, requirements[i]->name);
+ spm_install_show_package(requirements[i]);
+ spm_install(fs, tmpdir, package_path);
+ spm_install_package_record(fs, tmpdir, requirements[i]->name);
num_installed++;
free(package_path);
}
@@ -256,7 +245,7 @@ int do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
if (SPM_GLOBAL.verbose) {
printf("Removing metadata\n");
}
- metadata_remove(source);
+ spm_metadata_remove(source);
// Copy temporary directory to destination
if (SPM_GLOBAL.verbose) {
@@ -273,4 +262,4 @@ int do_install(SPM_Hierarchy *fs, ManifestList *mf, StrList *packages) {
}
rmdirs(tmpdir);
return 0;
-} \ No newline at end of file
+}
diff --git a/src/internal_cmd.c b/src/internal_cmd.c
index 08bc158..47a0532 100644
--- a/src/internal_cmd.c
+++ b/src/internal_cmd.c
@@ -141,7 +141,7 @@ int mkmanifest_interface(int argc, char **argv) {
*
*/
void mkruntime_interface_usage(void) {
- printf("usage: mkruntime {root_dir}");
+ printf("usage: mkruntime {root_dir}\n");
}
/**
@@ -162,20 +162,15 @@ int mkruntime_interface(int argc, char **argv) {
}
char *root = argv[1];
- char *spm_bindir = join((char *[]) {root, "bin", NULL}, DIRSEPS);
- char *spm_includedir = join((char *[]) {root, "include", NULL}, DIRSEPS);
- char *spm_libdir = join((char *[]) {root, "lib", NULL}, DIRSEPS);
- char *spm_datadir = join((char *[]) {root, "share", NULL}, DIRSEPS);
- char *spm_mandir = join((char *[]) {spm_datadir, "man", NULL}, DIRSEPS);
- char *spm_localstatedir = join((char *[]) {root, "var", NULL}, DIRSEPS);
- char *spm_pkgconfigdir = join((char *[]) {spm_libdir, "pkgconfig", NULL}, DIRSEPS);
-
- runtime_set(rt, "SPM_BIN", spm_bindir);
- runtime_set(rt, "SPM_INCLUDE", spm_includedir);
- runtime_set(rt, "SPM_LIB", spm_libdir);
- runtime_set(rt, "SPM_DATA", spm_datadir);
- runtime_set(rt, "SPM_MAN", spm_mandir);
- runtime_set(rt, "SPM_LOCALSTATE", spm_localstatedir);
+ SPM_Hierarchy *fs = spm_hierarchy_init(root);
+ char *spm_pkgconfigdir = join((char *[]) {fs->libdir, "pkgconfig", NULL}, DIRSEPS);
+
+ runtime_set(rt, "SPM_BIN", fs->bindir);
+ runtime_set(rt, "SPM_INCLUDE", fs->includedir);
+ runtime_set(rt, "SPM_LIB", fs->libdir);
+ runtime_set(rt, "SPM_DATA", fs->datadir);
+ runtime_set(rt, "SPM_MAN", fs->mandir);
+ runtime_set(rt, "SPM_LOCALSTATE", fs->localstatedir);
runtime_set(rt, "SPM_PKGCONFIG", spm_pkgconfigdir);
runtime_set(rt, "SPM_META_DEPENDS", SPM_META_DEPENDS);
runtime_set(rt, "SPM_META_PREFIX_BIN", SPM_META_PREFIX_BIN);
@@ -188,7 +183,7 @@ int mkruntime_interface(int argc, char **argv) {
runtime_set(rt, "MANPATH", "$SPM_MAN:$MANPATH");
runtime_set(rt, "PKG_CONFIG_PATH", "$SPM_PKGCONFIG:$PKG_CONFIG_PATH");
- char *spm_ccpath = join((char *[]) {spm_bindir, "gcc"}, DIRSEPS);
+ char *spm_ccpath = join((char *[]) {fs->bindir, "gcc"}, DIRSEPS);
if (exists(spm_ccpath) == 0) {
runtime_set(rt, "CC", "$SPM_BIN/gcc");
}
@@ -198,14 +193,9 @@ int mkruntime_interface(int argc, char **argv) {
runtime_export(rt, NULL);
runtime_free(rt);
- free(spm_bindir);
- free(spm_includedir);
- free(spm_libdir);
- free(spm_datadir);
- free(spm_mandir);
- free(spm_localstatedir);
free(spm_pkgconfigdir);
free(spm_ccpath);
+ spm_hierarchy_free(fs);
return 0;
}
diff --git a/src/metadata.c b/src/metadata.c
index d2bb078..e4dcfc6 100644
--- a/src/metadata.c
+++ b/src/metadata.c
@@ -1,5 +1,7 @@
#include "spm.h"
+extern const char *METADATA_FILES[];
+
static int verify_filelist(size_t lineno, char **a) {
if (lineno == 0) {
if (strncmp((*a), SPM_PACKAGE_HEADER_FILELIST, strlen(SPM_PACKAGE_HEADER_FILELIST)) != 0) {
@@ -54,11 +56,11 @@ static int reader_metadata(size_t lineno, char **line) {
/**
* Detect the type of metadata based on file name and execute the appropriate function against each line
* in the file. Verification can be disabled by passing a non-zero value as the second argument.
- * @param _filename
+ * @param filename
* @param no_verify SPM_METADATA_VERIFY or SPM_METADATA_NO_VERIFY
* @return array of strings (line endings removed)
*/
-char **metadata_read(const char *_filename, int no_verify) {
+char **spm_metadata_read(const char *_filename, int no_verify) {
char *filename = strdup(_filename);
char **data = NULL;
char **result = NULL;
@@ -126,3 +128,37 @@ char **metadata_read(const char *_filename, int no_verify) {
return result;
}
+
+/**
+ * SPM packages contain metadata files that are not useful post-install and would amount to a lot of clutter.
+ * This function removes these data files from a directory tree
+ * @param _path
+ * @return success=0, error=-1
+ */
+int spm_metadata_remove(const char *_path) {
+ if (exists(_path) != 0) {
+ perror(_path);
+ fprintf(SYSERROR);
+ return -1;
+ }
+
+ for (int i = 0; METADATA_FILES[i] != NULL; i++) {
+ char path[PATH_MAX];
+ sprintf(path, "%s%c%s", _path, DIRSEP, METADATA_FILES[i]);
+ if (exists(path) != 0) {
+ continue;
+ }
+ if (unlink(path) < 0) {
+ perror(path);
+ fprintf(SYSERROR);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+ConfigItem **spm_descriptor_read(const char *filename) {
+ ConfigItem **result = config_read(filename);
+ return result;
+}
+
diff --git a/src/purge.c b/src/purge.c
new file mode 100644
index 0000000..997df51
--- /dev/null
+++ b/src/purge.c
@@ -0,0 +1,93 @@
+#include "spm.h"
+
+/**
+ * Remove a package
+ * @param fs `SPM_Hierarchy`
+ * @param _package_name
+ * @return
+ */
+int spm_purge(SPM_Hierarchy *fs, const char *_package_name) {
+ size_t files_count = 0;
+ char **_files_orig = NULL;
+ char *path = NULL;
+ char *package_name = strdup(_package_name);
+ char *package_topdir = join((char *[]) {fs->dbrecdir, package_name, NULL}, DIRSEPS);
+ char *descriptor = join((char *[]) {package_topdir, SPM_META_DESCRIPTOR, NULL}, DIRSEPS);
+ char *filelist = join((char *[]) {package_topdir, SPM_META_FILELIST, NULL}, DIRSEPS);
+
+ if (spm_check_installed(fs, package_name) == 0) {
+ // package is not installed in this root
+ free(package_name);
+ free(package_topdir);
+ free(descriptor);
+ free(filelist);
+ return 1;
+ }
+
+ ConfigItem **desc = spm_descriptor_read(descriptor);
+ char *name = config_get(desc, "name")->value;
+ char *version = config_get(desc, "version")->value;
+ char *revision = config_get(desc, "revision")->value;
+
+ printf("Removing package: %s-%s-%s\n", name, version, revision);
+ _files_orig = spm_metadata_read(filelist, SPM_METADATA_VERIFY);
+ for (size_t i = 0; _files_orig[i] != NULL; i++) {
+ files_count++;
+ }
+
+ for (size_t i = 0; _files_orig[i] != NULL; i++) {
+ path = calloc(PATH_MAX, sizeof(char));
+ path = join((char *[]) {fs->rootdir, _files_orig[i], NULL}, DIRSEPS);
+ if (SPM_GLOBAL.verbose) {
+ printf(" -> %s\n", path);
+ }
+ if (exists(path) != 0) {
+ printf("%s does not exist\n", path);
+ } else {
+ remove(path);
+ }
+ free(path);
+ }
+ rmdirs(package_topdir);
+
+ free(package_name);
+ free(package_topdir);
+ free(descriptor);
+ free(filelist);
+ config_free(desc);
+ return 0;
+}
+
+/**
+ * Remove packages
+ * @param fs `SPM_Hierarchy`
+ * @param packages `StrList`
+ * @return
+ */
+int spm_do_purge(SPM_Hierarchy *fs, StrList *packages) {
+ int status_remove = 0;
+
+ printf("Removing package(s):\n");
+ for (size_t i = 0; i < strlist_count(packages); i++) {
+ char *package = strlist_item(packages, i);
+ if (spm_check_installed(fs, package) == 0) {
+ printf("%s is not installed\n", package);
+ return -1;
+ }
+ printf("-> %s\n", package);
+ }
+
+ if (SPM_GLOBAL.prompt_user) {
+ if (spm_prompt_user("Proceed with removal?", 1) == 0) {
+ return -2;
+ }
+ }
+
+ for (size_t i = 0; i < strlist_count(packages); i++) {
+ if ((status_remove = spm_purge(fs, strlist_item(packages, i))) != 0) {
+ printf("Failed");
+ break;
+ }
+ }
+ return status_remove;
+} \ No newline at end of file
diff --git a/src/spm.c b/src/spm.c
index 8444990..7b81280 100644
--- a/src/spm.c
+++ b/src/spm.c
@@ -5,6 +5,7 @@
#include <errno.h>
#include "spm.h"
+int RUNTIME_REMOVE = 0;
int RUNTIME_INSTALL = 0;
int RUNTIME_ROOTDIR = 0;
int RUNTIME_LIST = 0;
@@ -131,6 +132,22 @@ int main(int argc, char *argv[], char *arge[]) {
strcpy(rootdir, arg_next);
i++;
}
+ else if (strcmp(arg, "-R") == 0 || strcmp(arg, "--remove") == 0) {
+ RUNTIME_REMOVE = 1;
+ for (int p = 0; i < argc; p++) {
+ i++;
+ if (startswith(argv[i], "-") == 0 || startswith(argv[i], "--") == 0) {
+ i--;
+ break;
+ }
+ if ((argc - i) == 0) {
+ fprintf(stderr, "-R|--remove requires at least one package\n");
+ usage(program_name);
+ exit(1);
+ }
+ strlist_append(packages, argv[i]);
+ }
+ }
else if (strcmp(arg, "-I") == 0 || strcmp(arg, "--install") == 0) {
RUNTIME_INSTALL = 1;
for (int p = 0; i < argc; p++) {
@@ -165,10 +182,14 @@ int main(int argc, char *argv[], char *arge[]) {
show_global_config();
}
- if (RUNTIME_ROOTDIR && !RUNTIME_INSTALL) {
+ if (!RUNTIME_ROOTDIR && RUNTIME_INSTALL) {
fprintf(stderr, "-r|--root requires -I|--install\n");
usage(program_name);
exit(1);
+ } else if (!RUNTIME_ROOTDIR && RUNTIME_REMOVE) {
+ fprintf(stderr, "-r|--root requires -R|--remove\n");
+ usage(program_name);
+ exit(1);
}
if (isempty(rootdir)) {
@@ -190,9 +211,21 @@ int main(int argc, char *argv[], char *arge[]) {
runtime_set(rt, "SPM_LOCALSTATE", rootfs->localstatedir);
runtime_apply(rt);
+ if (RUNTIME_REMOVE) {
+ int status_remove = 0;
+ if ((status_remove = spm_do_purge(rootfs, packages)) == -1) {
+ exit(1);
+ }
+ else if (status_remove == -2) {
+ // user said no when asked to proceed
+ exit(2);
+ }
+
+ }
+
if (RUNTIME_INSTALL) {
int status_install = 0;
- if ((status_install = do_install(rootfs, mf, packages)) == -1) {
+ if ((status_install = spm_do_install(rootfs, mf, packages)) == -1) {
// failed to create temporary destination root
exit(1);
}
diff --git a/src/user_input.c b/src/user_input.c
index 061fb2e..3f358fa 100644
--- a/src/user_input.c
+++ b/src/user_input.c
@@ -25,6 +25,35 @@ int spm_user_yesno(int answer, int empty_input_is_yes) {
return result;
}
+int spm_prompt_user(const char *msg, int empty_input_is_yes) {
+ int user_choice = 0;
+ int status_choice = 0;
+ char ch_yes = 'y';
+ char ch_no = 'n';
+
+ if (empty_input_is_yes) {
+ ch_yes = 'Y';
+ } else {
+ ch_no = 'N';
+ }
+
+ printf("\n%s [%c/%c] ", msg, ch_yes, ch_no);
+ while ((user_choice = getchar())) {
+ status_choice = spm_user_yesno(user_choice, 1);
+ if (status_choice == 0) { // No
+ break;
+ } else if (status_choice == 1) { // Yes
+ break;
+ } else { // Only triggers when spm_user_yesno's second argument is zero
+ puts("Please answer 'y' or 'n'...");
+ }
+ }
+ puts("");
+
+ return status_choice;
+}
+
+
void spm_user_yesno_test() {
int choice;
int status;