From 3935dd9f2ac3aab9027783e45f499401476f52cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Mon, 19 Nov 2018 15:04:42 +0100 Subject: [PATCH 09/15] lib: add authselect_profile_features to list supported features The feature list is automatically obtained from profile's template files. Related to: https://github.com/pbrezina/authselect/issues/107 --- include/authselect.h | 12 +++++ src/lib/authselect_profile.c | 51 ++++++++++++++++++++++ src/lib/util/string_array.c | 36 +++++++++++++++ src/lib/util/string_array.h | 20 +++++++++ src/lib/util/template.c | 85 ++++++++++++++++++++++++++++++++++-- src/lib/util/template.h | 11 +++++ 6 files changed, 211 insertions(+), 4 deletions(-) diff --git a/include/authselect.h b/include/authselect.h index d52d460e38132cf81e8a03b88a530c634a2c8937..462f54291e8283d5778005ac1747dd03258584be 100644 --- a/include/authselect.h +++ b/include/authselect.h @@ -273,6 +273,18 @@ char ** authselect_profile_nsswitch_maps(const struct authselect_profile *profile, const char **features); +/** + * List features supported by the profile. + * + * It is necessary to free the returned pointer with @authselect_array_free. + * + * @param profile Pointer to structure obtained by @authselect_profile. + * + * @return NULL-terminated array of supported features, NULL on error. + */ +char ** +authselect_profile_features(const struct authselect_profile *profile); + /** * Free authconfig_profile structure obtained by @authselect_profile. * diff --git a/src/lib/authselect_profile.c b/src/lib/authselect_profile.c index 8a12a082802fbc4f3c8cb3af7379ad26372dcc0c..d0cf17b8b7a414a41427c88039687725dcf1a560 100644 --- a/src/lib/authselect_profile.c +++ b/src/lib/authselect_profile.c @@ -115,6 +115,57 @@ authselect_profile_nsswitch_maps(const struct authselect_profile *profile, return maps; } +_PUBLIC_ char ** +authselect_profile_features(const struct authselect_profile *profile) +{ + char **features; + char **array; + errno_t ret; + int i; + + if (profile == NULL) { + return NULL; + } + + features = string_array_create(10); + if (features == NULL) { + ERROR("Unable to create array (out of memory)"); + return NULL; + } + + struct authselect_generated files[] = PROFILE_FILES(profile->files); + + for (i = 0; files[i].path != NULL; i++) { + array = template_list_features(files[i].content); + if (array == NULL) { + ERROR("Unable to obtain feature list (out of memory)"); + ret = ENOMEM; + goto done; + } + + features = string_array_concat(features, array, true); + string_array_free(array); + + if (features == NULL) { + ERROR("Unable to obtain feature list (out of memory)"); + ret = ENOMEM; + goto done; + } + } + + string_array_sort(features); + + ret = EOK; + +done: + if (ret != EOK) { + string_array_free(features); + return NULL; + } + + return features; +} + _PUBLIC_ void authselect_profile_free(struct authselect_profile *profile) { diff --git a/src/lib/util/string_array.c b/src/lib/util/string_array.c index a074e23ee504792da7f1a9262e15c549de5747d3..43e30d0008b5709f97da9c43f8f2c28ef2475df5 100644 --- a/src/lib/util/string_array.c +++ b/src/lib/util/string_array.c @@ -159,3 +159,39 @@ string_array_del_value(char **array, const char *value) return array; } + +char ** +string_array_concat(char **to, char **items, bool unique) +{ + int i; + + if (items == NULL) { + return to; + } + + for (i = 0; items[i] != NULL; i++) { + to = string_array_add_value(to, items[i], unique); + if (to == NULL) { + return NULL; + } + } + + return to; +} + +static int +string_array_sort_callback(const void *a, const void *b) +{ + return strcmp(*(char* const*)a, *(char* const*)b); +} + +void +string_array_sort(char **array) +{ + if (array == NULL) { + return; + } + + qsort(array, string_array_count(array), sizeof(char *), + string_array_sort_callback); +} diff --git a/src/lib/util/string_array.h b/src/lib/util/string_array.h index 9545685b2553228a109ab51e7c6a0fd16698fa3e..06aa46c008058163e5557d51e18258fa4e9a1523 100644 --- a/src/lib/util/string_array.h +++ b/src/lib/util/string_array.h @@ -118,4 +118,24 @@ string_array_add_value(char **array, const char *value, bool unique); char ** string_array_del_value(char **array, const char *value); +/** + * Concatenate two array. Array @items values will be appended to arra @to. + * + * @param to NULL-terminated destination array. + * @param items NULL-terminated array to be appended into @to. + * @param unique If true, value will not be added if it is already present. + * + * @return Array or NULL if reallocation fails. + */ +char ** +string_array_concat(char **to, char **items, bool unique); + +/** + * Alphabetically sort a NULL-terminated string array. + * + * @param array NULL-terminated string array. + */ +void +string_array_sort(char **array); + #endif /* _STRING_ARRAY_H_ */ diff --git a/src/lib/util/template.c b/src/lib/util/template.c index e38d1d7f1db2c90f1281e97f3ebbdd9d9d1a935d..fb373c0fd83e04c6c78d5f57d3d8e50e54c0377b 100644 --- a/src/lib/util/template.c +++ b/src/lib/util/template.c @@ -325,10 +325,27 @@ template_process_matches(const char *match_string, return ret; } - *_op = op; - *_feature = feature; - *_if_true = if_true; - *_if_false = if_false; + if (_op != NULL) { + *_op = op; + } + + if (_feature != NULL) { + *_feature = feature; + } else { + free(feature); + } + + if (_if_true != NULL) { + *_if_true = if_true; + } else { + free(if_true); + } + + if (_if_false != NULL) { + *_if_false = if_false; + } else { + free(if_false); + } return EOK; } @@ -478,6 +495,66 @@ template_generate_preamble() return preamble; } +char ** +template_list_features(const char *template) +{ + regmatch_t m[RE_MATCHES]; + const char *match_string; + char **features; + char *feature; + regex_t regex; + errno_t ret; + int reret; + + features = string_array_create(10); + if (features == NULL) { + return NULL; + } + + if (template == NULL) { + return features; + } + + reret = regcomp(®ex, OP_RE, REG_EXTENDED | REG_NEWLINE); + if (reret != REG_NOERROR) { + ERROR("Unable to compile regular expression: regex error %d", reret); + ret = EFAULT; + goto done; + } + + match_string = template; + while ((reret = regexec(®ex, match_string, RE_MATCHES, m, 0)) == REG_NOERROR) { + ret = template_process_matches(match_string, m, NULL, &feature, + NULL, NULL); + if (ret != EOK) { + ERROR("Unable to process match [%d]: %s", ret, strerror(ret)); + goto done; + } + + features = string_array_add_value(features, feature, true); + free(feature); + + match_string += m[0].rm_eo; + } + + if (reret != REG_NOMATCH) { + ERROR("Unable to search string: regex error %d", reret); + ret = EFAULT; + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + string_array_free(features); + return NULL; + } + + regfree(®ex); + return features; +} + errno_t template_write(const char *filepath, const char *content, diff --git a/src/lib/util/template.h b/src/lib/util/template.h index 57e076c257a8cdcbc9c2c8f4f5266b4c21f27941..1c412dcf15a7f0d5a5897b78b8b52ff67b7c3f95 100644 --- a/src/lib/util/template.h +++ b/src/lib/util/template.h @@ -37,6 +37,17 @@ char * template_generate(const char *template, const char **features); +/** + * Find all features available withint the @template and return them in + * NULL-terminated array. + * + * @param template Template. + * + * @return List of features in NULL-terminated array or NULL on error. + */ +char ** +template_list_features(const char *template); + /** * Write generated file preamble together with its content to a file. * If the file does not exist, it is created, otherwise its content -- 2.17.2