Blob Blame History Raw
From 9c93f0540b2afe736a8633c0ac8ac6e4e425726d Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Wed, 30 Jul 2014 09:03:35 +0200
Subject: [PATCH] Added capability to register and deregister collections

- scl register <col_path> creates record for the collection in the scl
  conf dir
- scl deregister <collection> removes the collection record from the
  scl conf dir
---
 scl.1    |  18 +++
 scl.bash |  11 +-
 scl.c    | 427 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 427 insertions(+), 29 deletions(-)

diff --git a/scl.1 b/scl.1
index 9e13778..c9c2abe 100644
--- a/scl.1
+++ b/scl.1
@@ -8,6 +8,10 @@ scl \- Setup and run software from Software Collection environment
 \fBscl\fP \fI<action>\fR \fI<collection1>\fR [\fI<collection2> ...\fR] -- \fI<command>\fR
 .PP
 \fBscl\fP {\fB-l|--list\fP} [\fI<collection1> <collection2> ...\fR]
+.PP
+\fBscl register\fP \fI<path>\f
+.PP
+\fBscl deregister\fP \fI<collection>\fR [\fB--force\fP]
 .SH "DESCRIPTION"
 .PP
 This manual page documents \fBscl\fP, a
@@ -46,6 +50,14 @@ details may be found in the documentation of the particular collection.
 Lists all installed Software Collections on the system.
 .IP "\fB-l, --list\fP \fI<collection1> <collection2> ...\fR"
 If a collection name is specified then list of installed packages belonging to the collection is listed.
+.IP "\fBregister\fP \fI<path>\fR"
+If \fI<path>\fR leads to valid SCL file structure, \fBscl\fP will register that as a SCL.
+\fI<path>\fR directory needs to contain \fBenable\fR  scriptlet and \fBroot\fP directory,
+to be considered valid SCL.
+<path> needs to be an absolute path to the collection location.
+.IP "\fBderegister\fP \fI<collection>\fR [\fB--force\fP]"
+\fI<collection>\fR will no longer be considered SCL.
+If the \fI<collection>\fR was installed locally, then the use of \fB--force\fP is needed.i
 .SH "EXAMPLES"
 .TP
 scl enable example 'less --version'
@@ -63,6 +75,12 @@ list all installed collections
 .TP
 scl -l example
 list all packages within example collection
+scl register /foo/bar
+registers new collection with a name bar
+.TP
+scl deregister bar --force
+forces the deregistration of collection bar
+.TP
 .SH "AUTHOR"
 .PP
 \fBscl\fP was written by Jindrich Novy <jnovy@redhat.com> and Jan Zeleny
diff --git a/scl.bash b/scl.bash
index 7f77233..5fae09a 100644
--- a/scl.bash
+++ b/scl.bash
@@ -14,17 +14,18 @@ _scl()
     return 0
   fi
 
+  local collections=($(find /etc/scl/prefixes -maxdepth 1 -mindepth 1 -type f -exec basename {} \; | sort -u))
+
   # handle scriptlets; the first parameter must be a scriptlet if it is not an option
   if ((COMP_CWORD == 1)); then
     # get array of scriptlets found throughout collections
-    local collections=($(find /etc/scl/prefixes -maxdepth 1 -mindepth 1 -type f -exec basename {} \; | sort -u))
     local scriptlets=()
     for col in ${collections[@]}; do
         local prefix=`cat /etc/scl/prefixes/$col`
         scriptlets+=($(find $prefix/$col/* -maxdepth 1 -type f -exec basename {} \; | sort -u))
     done
     scriptlets_str=`echo ${scriptlets[@]} | sed 's/ /\n/g'| sort -u`
-    COMPREPLY=( $(compgen -W "$scriptlets_str" -- ${cur}) )
+    COMPREPLY=( $(compgen -W "$scriptlets_str register deregister" -- ${cur}) )
     return 0
   fi
 
@@ -35,7 +36,11 @@ _scl()
   fi
 
   # handle collections; if it is not an option or a command, it must be a collection
-  local collections=($(find /etc/scl/prefixes -maxdepth 1 -mindepth 1 -type f -exec basename {} \; | sort -u))
+  if [ $prev == "register" ]; then
+    compopt -o nospace
+    COMPREPLY=( $(compgen -A directory ${cur}) )
+    return 0
+  fi
   COMPREPLY=( $(compgen -W "${collections[*]}" -- ${cur}) )
   return 0
 }
diff --git a/scl.c b/scl.c
index fcdebcb..f8e2ed4 100644
--- a/scl.c
+++ b/scl.c
@@ -22,8 +22,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <getopt.h>
+#include <ctype.h>
 #include <string.h>
 #include <dirent.h>
 #include <sys/types.h>
@@ -56,6 +58,8 @@ static void write_script( int tfd, char *s ) {
 static void print_usage( const char *name ) {
 	fprintf(stderr, "usage: %s <action> [<collection>...] <command>\n", name);
 	fprintf(stderr, "   or: %s -l|--list [<collection>...]\n", name);
+	fprintf(stderr, "   or: %s register <path>\n", name);
+	fprintf(stderr, "   or: %s deregister <collection> [--force]\n", name);
 
 	fprintf(stderr, "\nOptions:\n"
 				 "    -l, --list            list installed Software Collections or packages\n"
@@ -68,32 +72,121 @@ static void print_usage( const char *name ) {
 				 "\nUse '-' as <command> to read the command from standard input.\n");
 }
 
+static int check_directory(const char *dir_name, struct stat *sb, int *count, struct dirent ***nl) {
+    if (stat(dir_name, sb) == -1) {
+        fprintf(stderr, "%s does not exist\n", dir_name);
+        return EXIT_FAILURE;
+    }
+
+    if (!S_ISDIR(sb->st_mode)) {
+        fprintf(stderr, "%s is not a directory\n", dir_name);
+        return EXIT_FAILURE;
+    }
+
+    if ((*count = scandir(dir_name, nl, 0, alphasort)) < 0) {
+        perror("scandir");
+        fprintf(stderr, "%s\n", dir_name);
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+static int get_collection_dir_path(char *col_name, char **_col_dir) {
+    int i;
+    int fd = -1;
+    char *file_path = NULL;
+    char *col_dir = NULL;
+    struct stat st;
+    int ret = EXIT_FAILURE;
+    int col_name_len = strlen(col_name);
+    int col_dir_len;
+
+    file_path = (char *)malloc(sizeof(SCL_CONF_DIR) + col_name_len + 1);
+    if (file_path == NULL) {
+        fprintf(stderr, "Can't allocate memory.\n");
+        return EXIT_FAILURE;
+    }
+    sprintf(file_path, "%s%s", SCL_CONF_DIR, col_name);
+
+    if (stat(file_path, &st) != 0) {
+        perror("Unable to get file status");
+        fprintf(stderr, "%s\n", file_path);
+        goto done;
+    }
+
+    fd = open(file_path, O_RDONLY);
+    if (fd < 0) {
+        perror("Unable to open file");
+        fprintf(stderr, "%s\n", file_path);
+        goto done;
+    }
+
+	/* One for slash, one for terminating zero*/
+    col_dir = (char *)calloc(st.st_size + col_name_len + 2, 1);
+    if (col_dir == NULL) {
+        fprintf(stderr, "Can't allocate memory.\n");
+        goto done;
+    }
+    if ((col_dir_len = read(fd, col_dir, st.st_size)) < 0) {
+        fprintf(stderr, "Unable to read from file.\n");
+        goto done;
+    }
+    for (i = col_dir_len-1; i > 0; i--) {
+        if (isspace(col_dir[i]) || col_dir[i] == '/') {
+            col_dir[i] = '\0';
+        } else {
+            break;
+        }
+    }
+    col_dir[i+1] = '/';
+    memcpy(col_dir + i + 2, col_name, col_name_len + 1);
+
+    *_col_dir = col_dir;
+
+    ret = EXIT_SUCCESS;
+done:
+    if (fd > 0) {
+        close(fd);
+    }
+    if (ret != EXIT_SUCCESS) {
+        free(col_dir);
+    }
+    free(file_path);
+    return ret;
+}
+
+static int col_available(char *col_name) {
+    char *col_dir = NULL;
+    int ret = 0;
+
+    if (get_collection_dir_path(col_name, &col_dir)) {
+        return EXIT_FAILURE;
+    }
+    ret = access(col_dir, F_OK);
+    free(col_dir);
+    return ret;
+}
+
 static void list_collections() {
 	struct stat sb;
 	struct dirent **nl;
 	int n, i;
 
-	if (stat(SCL_CONF_DIR, &sb) == -1) {
-		fprintf(stderr, "%s does not exist\n", SCL_CONF_DIR);
-		exit(EXIT_FAILURE);
-	}
-
-	if (!S_ISDIR(sb.st_mode)) {
-		fprintf(stderr, "%s is not a directory\n", SCL_CONF_DIR);
-		exit(EXIT_FAILURE);
-	}
-
-	if ((n = scandir(SCL_CONF_DIR, &nl, 0, alphasort)) < 0) {
-		perror("scandir");
+	if (check_directory(SCL_CONF_DIR, &sb, &n, &nl)) {
 		exit(EXIT_FAILURE);
 	}
 
 	for (i=0; i<n; i++) {
 		if (*nl[i]->d_name != '.') {
-			printf("%s\n", nl[i]->d_name);
+			if (col_available(nl[i]->d_name) == 0) {
+				printf("%s\n", nl[i]->d_name);
+			}
 		}
 	}
 
+	for (i = 0; i < n; i++) {
+		free(nl[i]);
+	}
 	free(nl);
 }
 
@@ -157,7 +250,7 @@ static char **read_script_output( char *ori_cmd ) {
 	return lines;
 }
 
-static int list_packages_in_collection( const char *colname) {
+static int list_packages_in_collection(const char *colname) {
 	struct stat sb;
 	struct dirent **nl;
 	int i, n, found, smax, ss;
@@ -165,18 +258,7 @@ static int list_packages_in_collection( const char *colname) {
 	char **srpms = NULL;
 	size_t cns;
 
-	if (stat(SCL_CONF_DIR, &sb) == -1) {
-		fprintf(stderr, "%s does not exist\n", SCL_CONF_DIR);
-		exit(EXIT_FAILURE);
-	}
-
-	if (!S_ISDIR(sb.st_mode)) {
-		fprintf(stderr, "%s is not a directory\n", SCL_CONF_DIR);
-		exit(EXIT_FAILURE);
-	}
-
-	if ((n = scandir(SCL_CONF_DIR, &nl, 0, alphasort)) < 0) {
-		perror("scandir");
+	if (check_directory(SCL_CONF_DIR, &sb, &n, &nl)) {
 		exit(EXIT_FAILURE);
 	}
 
@@ -246,6 +328,260 @@ static int list_packages_in_collection( const char *colname) {
 	return 0;
 }
 
+static int split_path(char *col_path, char **_col, char **_fname) {
+    char *name_start = NULL;
+    char *name_end = NULL;
+    char *col = NULL;
+    int col_path_len = strlen(col_path);
+
+    col = (char *)malloc(strlen(col_path) + 1);
+    if (col == NULL) {
+        fprintf(stderr, "Can't allocate memory.\n");
+        return EXIT_FAILURE;
+    }
+    memcpy(col, col_path, col_path_len + 1);
+
+    name_end = col + col_path_len - 1;
+    while (name_end > col && *name_end == '/') {
+        *name_end = '\0';
+        name_end--;
+    }
+
+    name_start = strrchr(col, '/');
+    if (name_start == NULL) {
+        free(col);
+        return EXIT_FAILURE;
+    } else {
+        *name_start = '\0';
+        name_start++;
+    }
+
+    *_fname = name_start;
+    *_col = col;
+    return EXIT_SUCCESS;
+}
+
+static int get_collection_conf_path(char *col_name, char **_col_path) {
+    char *col_path = (char *)malloc(sizeof(SCL_CONF_DIR) + strlen(col_name) + 1);
+    if (col_path == NULL) {
+        fprintf(stderr, "Can't allocate memory.\n");
+        return EXIT_FAILURE;
+    }
+    sprintf(col_path, "%s%s", SCL_CONF_DIR, col_name);
+    *_col_path = col_path;
+    return EXIT_SUCCESS;
+}
+
+static int check_valid_collection(char *col_dir) {
+    struct stat sb;
+    struct dirent **nl;
+    int n, i;
+    bool missing_root = true;
+    bool missing_enable = true;
+
+    if (check_directory(col_dir, &sb, &n, &nl)) {
+        exit(EXIT_FAILURE);
+    }
+
+    for (i=0; i<n; i++) {
+        if (*nl[i]->d_name != '.') {
+            if (!strcmp(nl[i]->d_name, "root")) {
+                missing_root = false;
+            } else if (!strcmp(nl[i]->d_name, "enable")) {
+                missing_enable = false;
+            }
+        }
+        free(nl[i]);
+    }
+    free(nl);
+
+    return missing_root || missing_enable;
+}
+
+static int run_script(char *script_path, char *script_name) {
+    char *script = NULL;
+    char *cmd = NULL;
+    int status;
+    int ret = EXIT_FAILURE;
+
+    if (script_path[strlen(script_path) - 1] == '/') {
+        check_asprintf(&script, "%s%s", script_path, script_name);
+    } else {
+        check_asprintf(&script, "%s/%s", script_path, script_name);
+    }
+
+    if (!access(script, F_OK)) {
+        check_asprintf(&cmd, "/bin/bash %s", script);
+        status = system(cmd);
+        if (status == -1) {
+            perror("Unable to execute script\n");
+            fprintf(stderr, "%s\n", script);
+            goto done;
+        }
+        if (!WIFEXITED(status)) {
+            fprintf(stderr, "Script %s didn't terminate normally\n", script);
+            goto done;
+        }
+        if (WEXITSTATUS(status)) {
+            fprintf(stderr, "Script %s returned nonzero return code\n", script);
+            goto done;
+        }
+    }
+
+    ret = EXIT_SUCCESS;
+
+done:
+    free(script);
+    free(cmd);
+    return ret;
+}
+
+static int register_collection(char *col_path) {
+    FILE *f;
+    char *col = NULL;
+    char *name = NULL;
+    char *new_file = NULL;
+
+    if (col_path == NULL || col_path[0] != '/') {
+        fprintf(stderr, "Collection must be specified as an absolute path!\n");
+        return EXIT_FAILURE;
+    }
+
+    if (access(col_path, F_OK)) {
+        perror("Directory doesn't exist");
+        fprintf(stderr, "%s\n", col_path);
+        return EXIT_FAILURE;
+    }
+
+    if (check_valid_collection(col_path)) {
+        fprintf(stderr, "Unable to register collection: %s is not a valid collection\n", col_path);
+        return EXIT_FAILURE;
+    }
+
+    if (split_path(col_path, &col, &name)) {
+        return EXIT_FAILURE;
+    }
+
+    if (get_collection_conf_path(name, &new_file)) {
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    if (access(new_file, F_OK) == 0) {
+        fprintf(stderr, "Unable to register collection: Collection with the same name is already registered\n");
+        free(new_file);
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    f = fopen(new_file, "w+");
+    if (f == NULL) {
+        perror("Unable to open file");
+        fprintf(stderr, "%s\n", new_file);
+        free(col);
+        free(new_file);
+        return EXIT_FAILURE;
+    }
+
+    fprintf(f, "%s\n", col);
+    fclose(f);
+
+    if (run_script(col_path, "register")) {
+        fprintf(stderr, "Execution of register script failed\n");
+        if (unlink(new_file)) {
+            perror("Unable to remove file: ");
+            fprintf(stderr, "%s\n", new_file);
+            fprintf(stderr, "Remove this file manually before a new try to register collection!\n");
+        }
+        free(new_file);
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    printf("Collection succesfully registered.\n"
+           "The collection can now be enabled using 'scl enable %s <command>'\n", name);
+    free(new_file);
+    free(col);
+
+    return EXIT_SUCCESS;
+}
+
+static int check_package(char *file_path, int *_status) {
+    char *cmd = NULL;
+    int path_len = strlen(file_path);
+    char rpm_query[] = "rpm -qf %s > /dev/null 2> /dev/null";
+
+    cmd  = (char *)malloc(path_len + sizeof(rpm_query) - 1);
+    if (cmd == NULL) {
+        fprintf(stderr, "Can't allocate memory.\n");
+        return EXIT_FAILURE;
+    }
+    sprintf(cmd, rpm_query, file_path);
+    *_status = system(cmd);
+    free(cmd);
+
+    return EXIT_SUCCESS;
+}
+
+static int deregister_collection(char *col_path, bool force) {
+    char *col = NULL;
+    char *col_name = NULL;
+	char *col_dir = NULL;
+
+    if (get_collection_conf_path(col_path, &col_name)) {
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    if (!force) {
+        int status;
+        if (check_package(col_name, &status)) {
+            free(col_name);
+            free(col);
+            return EXIT_FAILURE;
+        }
+
+        if (status == 0) {
+            fprintf(stderr, "Unable to deregister collection: "
+                    "Collection was installed as a package, please use --force to deregister it.\n");
+            free(col);
+            free(col_name);
+            return EXIT_FAILURE;
+        }
+    }
+
+    if (get_collection_dir_path(col_path, &col_dir)) {
+        free(col_name);
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    if (run_script(col_dir, "deregister")) {
+        fprintf(stderr, "Execution of deregister script failed\n");
+        free(col_dir);
+        free(col_name);
+        free(col);
+        return EXIT_FAILURE;
+    }
+
+    if (remove(col_name)) {
+        perror("Unable to delete file");
+        fprintf(stderr, "%s\n", col_name);
+        free(col_dir);
+        free(col_name);
+        free(col);
+        return EXIT_FAILURE;
+    }
+    printf("Collection successfully deregistered.\n");
+	free(col_dir);
+    free(col_name);
+    free(col);
+    return EXIT_SUCCESS;
+}
+
+
+
+
 int main(int argc, char **argv) {
 	struct stat st;
 	char *path, *enablepath;
@@ -254,6 +590,7 @@ int main(int argc, char **argv) {
 	int i, tfd, ffd;
 	int separator_pos = 0;
 	char *command = NULL;
+	int failed = 0;
 
 	if (argc == 2 && (!strcmp(argv[1],"--help") || !strcmp(argv[1],"-h"))) {
 		print_usage(argv[0]);
@@ -270,6 +607,44 @@ int main(int argc, char **argv) {
 		exit(EXIT_SUCCESS);
 	}
 
+	if (argc > 2 && (!strcmp(argv[1], "register"))) {
+		failed = 0;
+		for (i = 2; i < argc; i++) {
+			if (register_collection(argv[i]) != 0) {
+				failed++;
+			}
+		}
+		if (failed > 0) {
+			fprintf(stderr, "Registration of %d collections failed!\n", failed);
+			exit(EXIT_FAILURE);
+		} else {
+			exit(EXIT_SUCCESS);
+		}
+	}
+	if (argc > 2 && (!(strcmp(argv[1], "deregister")))) {
+		bool force = false;
+		for (i = 2; i < argc; i++) {
+			if (!strcmp(argv[i], "--force")) {
+				force = true;
+				break;
+			}
+		}
+		for (i = 2; i < argc; i++) {
+			if (strcmp(argv[i], "--force") != 0) {
+				failed = 0;
+				if (deregister_collection(argv[i], force) != 0) {
+					failed++;
+				}
+			}
+		}
+		if (failed > 0) {
+			fprintf(stderr, "Deregistration of %d collections failed!\n", failed);
+			exit(EXIT_FAILURE);
+		} else {
+			exit(EXIT_SUCCESS);
+		}
+	}
+
 	for (i = 0; i < argc; i++) {
 		if (strcmp(argv[i], "--") == 0) {
 			break;
-- 
1.9.3