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