From 9c93f0540b2afe736a8633c0ac8ac6e4e425726d Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Wed, 30 Jul 2014 09:03:35 +0200 Subject: [PATCH] Added capability to register and deregister collections - scl register creates record for the collection in the scl conf dir - scl deregister 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\fR \fI\fR [\fI ...\fR] -- \fI\fR .PP \fBscl\fP {\fB-l|--list\fP} [\fI ...\fR] +.PP +\fBscl register\fP \fI\f +.PP +\fBscl deregister\fP \fI\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 ...\fR" If a collection name is specified then list of installed packages belonging to the collection is listed. +.IP "\fBregister\fP \fI\fR" +If \fI\fR leads to valid SCL file structure, \fBscl\fP will register that as a SCL. +\fI\fR directory needs to contain \fBenable\fR scriptlet and \fBroot\fP directory, +to be considered valid SCL. + needs to be an absolute path to the collection location. +.IP "\fBderegister\fP \fI\fR [\fB--force\fP]" +\fI\fR will no longer be considered SCL. +If the \fI\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 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 #include #include +#include #include #include +#include #include #include #include @@ -56,6 +58,8 @@ static void write_script( int tfd, char *s ) { static void print_usage( const char *name ) { fprintf(stderr, "usage: %s [...] \n", name); fprintf(stderr, " or: %s -l|--list [...]\n", name); + fprintf(stderr, " or: %s register \n", name); + fprintf(stderr, " or: %s deregister [--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 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; id_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; id_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 '\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