From 4a1f8198d62e5988aaa7f81d0f94201ca16f8b47 Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Mon, 6 Jul 2020 16:06:42 +0100 Subject: [PATCH] utils: Allow selecting library comparators from a string This is a piece of necessary infrastructure for exposing this as a command-line option, or even as per-library metadata. We were originally going to do the equivalent of "versions,name,symbols" unconditionally, but it looks as though that could break more than it fixes, so let's be a bit more cautious. Signed-off-by: Simon McVittie <smcv@collabora.com> --- utils/library-cmp.c | 151 ++++++++++++++++++++++++++++++++++++++++++++ utils/library-cmp.h | 33 ++++++++++ 2 files changed, 184 insertions(+) diff --git a/utils/library-cmp.c b/utils/library-cmp.c index 1e7a82626..fa1ba0b5c 100644 --- a/utils/library-cmp.c +++ b/utils/library-cmp.c @@ -1063,3 +1063,154 @@ out: close_elf( &provider_elf, &provider_fd ); return cmp_result; } + +static int +library_cmp_choose_container( const char *soname, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + DEBUG( DEBUG_TOOL, + "Choosing %s from container \"%s\", ignoring provider \"%s\"", + soname, container_root, provider_root ); + return 1; +} + +static int +library_cmp_choose_provider( const char *soname, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + DEBUG( DEBUG_TOOL, + "Choosing %s from provider \"%s\", ignoring container \"%s\"", + soname, provider_root, container_root ); + return -1; +} + +typedef struct +{ + const char *name; + library_cmp_function comparator; +} named_comparator; + +static const named_comparator named_comparators[] = +{ + { "name", library_cmp_by_name }, + { "symbols", library_cmp_by_symbols }, + { "versions", library_cmp_by_versions }, + { "container", library_cmp_choose_container }, + { "provider", library_cmp_choose_provider }, +}; + +/* + * library_cmp_list_from_string: + * @spec: A string of tokens separated by @delimiters + * @delimiters: Any character from this set separates tokens + * @code: (out) (optional): Set to an errno value on error + * @message: (out) (optional): Set to a string error message on error + * + * Parse a list of comparators into an array of functions. + * + * Returns: (free-function free): An array of comparators + */ +library_cmp_function * +library_cmp_list_from_string( const char *spec, + const char *delimiters, + int *code, + char **message ) +{ + library_cmp_function *ret = NULL; + char *buf = xstrdup( spec ); + char *saveptr = NULL; + char *token; + ptr_list *list; + + static_assert( sizeof( void * ) == sizeof( library_cmp_function ), + "Function pointer assumed to be same size as void *"); + static_assert( alignof( void * ) == alignof( library_cmp_function ), + "Function pointer assumed to be same alignment as void *"); + + list = ptr_list_alloc( N_ELEMENTS( named_comparators ) ); + + for( token = strtok_r( buf, delimiters, &saveptr ); + token != NULL; + token = strtok_r( NULL, delimiters, &saveptr ) ) + { + library_cmp_function comparator = NULL; + size_t i; + + if( token[0] == '\0' ) + continue; + + for( i = 0; i < N_ELEMENTS( named_comparators ); i++ ) + { + if( strcmp( token, named_comparators[i].name ) == 0 ) + { + comparator = named_comparators[i].comparator; + break; + } + } + + if( comparator == NULL ) + { + _capsule_set_error( code, message, + EINVAL, "Unknown library comparison mode \"%s\"", + token ); + goto out; + } + + ptr_list_push_ptr( list, (void *) comparator ); + } + + ret = (library_cmp_function *) ptr_list_free_to_array( _capsule_steal_pointer( &list ), NULL ); + +out: + if( list != NULL ) + ptr_list_free( list ); + + free( buf ); + return ret; +} + +/* + * library_cmp_list_iterate: + * @comparators: (array zero-terminated=1): Functions to compare the libraries + * @soname: The name under which we searched for the library + * @container_path: The path to the library in the container + * @container_root: The path to the top-level directory of the container + * @provider_path: The path to the library in the provider + * @provider_root: The path to the top-level directory of the provider + * + * Iterate through a %NULL-terminated array of comparators, + * highest-precedence first, calling each one in turn until one of them + * returns a nonzero value. If none of them return nonzero, return 0. + * + * Returns: Negative if the container version appears newer, zero if they + * appear the same or we cannot tell, or positive if the provider version + * appears newer. + */ +int +library_cmp_list_iterate( const library_cmp_function *comparators, + const char *soname, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ) +{ + const library_cmp_function *iter; + + for( iter = comparators; iter != NULL && *iter != NULL; iter++ ) + { + int decision = (*iter)( soname, + container_path, container_root, + provider_path, provider_root ); + + if( decision != 0 ) + return decision; + } + + return 0; +} diff --git a/utils/library-cmp.h b/utils/library-cmp.h index 4a43f4cf8..8d9627016 100644 --- a/utils/library-cmp.h +++ b/utils/library-cmp.h @@ -17,6 +17,28 @@ #pragma once +/* + * library_cmp_function: + * @soname: The name under which we searched for the library + * @container_path: The path to the library in the container + * @container_root: The path to the top-level directory of the container + * @provider_path: The path to the library in the provider + * @provider_root: The path to the top-level directory of the provider + * + * Compare two libraries, returning a result with the same convention + * as strcmp(): informally, `cmp(a, b) < 0` if `a < b`, and the same + * for `>`. + * + * Returns: Negative if the container version appears newer, zero if they + * appear the same or we cannot tell, or positive if the provider version + * appears newer. + */ +typedef int ( *library_cmp_function ) ( const char *soname, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ); + int library_cmp_by_name( const char *soname, const char *container_path, const char *container_root, @@ -34,3 +56,14 @@ int library_cmp_by_versions( const char *soname, const char *container_root, const char *provider_path, const char *provider_root ); + +library_cmp_function *library_cmp_list_from_string( const char *spec, + const char *delimiters, + int *code, + char **message ); +int library_cmp_list_iterate( const library_cmp_function *comparators, + const char *soname, + const char *container_path, + const char *container_root, + const char *provider_path, + const char *provider_root ); -- GitLab