From: Michael Tremer Date: Wed, 3 Mar 2021 15:59:25 +0000 (+0000) Subject: parser: Implement executing commands in %(...) X-Git-Tag: 0.9.28~1285^2~650 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d25dd74f7124376e95a30008b5c12ae108236e7e;p=pakfire.git parser: Implement executing commands in %(...) Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/parser.c b/src/libpakfire/parser.c index 6411d8ff8..0c4e0bc76 100644 --- a/src/libpakfire/parser.c +++ b/src/libpakfire/parser.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,7 @@ struct _PakfireParser { size_t num_declarations; // Regular expressions + pcre2_code* regex_command; pcre2_code* regex_variable; }; @@ -53,6 +55,29 @@ static int pakfire_parser_compile_regex(PakfireParser parser) { size_t pcre2_offset; PCRE2_UCHAR errmsg[256]; + // Commands + if (!parser->regex_command && parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS) { + parser->regex_command = pcre2_compile((PCRE2_SPTR)"%\\((.*)\\)", + PCRE2_ZERO_TERMINATED, 0, &pcre2_errno, &pcre2_offset, NULL); + + if (!parser->regex_command) { + pcre2_get_error_message(pcre2_errno, errmsg, sizeof(errmsg)); + ERROR(parser->pakfire, "PCRE2 compilation failed at offset %zu: %s\n", + pcre2_offset, errmsg); + + return 1; + } + + // Enable JIT + pcre2_errno = pcre2_jit_compile(parser->regex_command, PCRE2_JIT_COMPLETE); + if (pcre2_errno) { + pcre2_get_error_message(pcre2_errno, errmsg, sizeof(errmsg)); + ERROR(parser->pakfire, "Enabling JIT on commands failed: %s\n", errmsg); + return 1; + } + } + + // Variables if (!parser->regex_variable) { parser->regex_variable = pcre2_compile((PCRE2_SPTR)"%\\{([A-Za-z0-9_\\-]+)\\}", PCRE2_ZERO_TERMINATED, 0, &pcre2_errno, &pcre2_offset, NULL); @@ -357,20 +382,97 @@ static struct pakfire_parser_declaration* pakfire_parser_find_declaration( return d; } +static int pakfire_parser_expand_commands(PakfireParser parser, char** buffer) { + int r = 0; + PCRE2_UCHAR* command = NULL; + PCRE2_SIZE command_length; + PCRE2_UCHAR* pattern = NULL; + PCRE2_SIZE pattern_length; + + DEBUG(parser->pakfire, "Searching for commands in:\n%s\n", *buffer); + + // Allocate memory for results + pcre2_match_data* match = pcre2_match_data_create_from_pattern( + parser->regex_command, NULL); + + // Arguments passed to pakfire_execute + const char* argv[4] = { + "/bin/sh", "-c", NULL /* will be replaced by command later */, NULL, + }; + + while (1) { + // Perform matching + int r = pcre2_jit_match(parser->regex_command, + (PCRE2_UCHAR*)*buffer, strlen(*buffer), 0, 0, match, NULL); + + // End loop when we have expanded all variables + if (r == PCRE2_ERROR_NOMATCH) + break; + + // Extract the command + r = pcre2_substring_get_bynumber(match, 1, &command, &command_length); + if (r) + goto ERROR; + + DEBUG(parser->pakfire, "Expanding command: %s\n", command); + + // Update argv + argv[2] = (const char*)command; + + // The output of the command + const char* output = "XXX"; + + // Execute the command inside the Pakfire environment + r = pakfire_execute(parser->pakfire, argv, NULL, 0, NULL); + if (r) { + // Just log this and continue + DEBUG(parser->pakfire, "Command '%s' failed with return code %d\n", command, r); + } + + // Find the entire matched pattern + r = pcre2_substring_get_bynumber(match, 0, &pattern, &pattern_length); + if (r) + goto ERROR; + + // Replace all occurrences + char* tmp = pakfire_string_replace(*buffer, (const char*)pattern, output); + if (!tmp) + goto ERROR; + + // Replace buffer + free(*buffer); + *buffer = tmp; + + // Free resources + pcre2_substring_free(command); + command = NULL; + + pcre2_substring_free(pattern); + pattern = NULL; + } + +ERROR: + pcre2_match_data_free(match); + + if (command) + pcre2_substring_free(command); + if (pattern) + pcre2_substring_free(pattern); + + return r; +} + static int pakfire_parser_expand_variables(PakfireParser parser, const char* namespace, char** buffer) { + int r = 0; PCRE2_UCHAR* variable = NULL; PCRE2_SIZE variable_length; PCRE2_UCHAR* pattern = NULL; PCRE2_SIZE pattern_length; - // Compile all regular expressions - int r = pakfire_parser_compile_regex(parser); - if (r) - goto ERROR; - // Allocate memory for results - pcre2_match_data* match = pcre2_match_data_create_from_pattern(parser->regex_variable, NULL); + pcre2_match_data* match = pcre2_match_data_create_from_pattern( + parser->regex_variable, NULL); // Search for any variables while (1) { @@ -455,8 +557,15 @@ PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser, if (!pos) return buffer; + // Compile all regular expressions + int r = pakfire_parser_compile_regex(parser); + if (r) { + free(buffer); + return NULL; + } + // Expand all variables - int r = pakfire_parser_expand_variables(parser, namespace, &buffer); + r = pakfire_parser_expand_variables(parser, namespace, &buffer); if (r) { if (buffer) free(buffer); @@ -464,6 +573,17 @@ PAKFIRE_EXPORT char* pakfire_parser_expand(PakfireParser parser, return NULL; } + // Expand all commands + if (parser->flags & PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS) { + r = pakfire_parser_expand_commands(parser, &buffer); + if (r) { + if (buffer) + free(buffer); + + return NULL; + } + } + return buffer; } diff --git a/tests/data/parser/test-declarations.txt b/tests/data/parser/test-declarations.txt index 115f7dbae..4ee3b5480 100644 --- a/tests/data/parser/test-declarations.txt +++ b/tests/data/parser/test-declarations.txt @@ -16,3 +16,6 @@ end e = A \ B \ C + +# Commands +command = %(echo "ABC") diff --git a/tests/libpakfire/parser.c b/tests/libpakfire/parser.c index 6fc51f11e..0a93a91b3 100644 --- a/tests/libpakfire/parser.c +++ b/tests/libpakfire/parser.c @@ -140,9 +140,26 @@ static int test_parser_files(const struct test* t) { return EXIT_SUCCESS; } +static int test_parser_command(const struct test* t) { + const char* command = "%(echo \"ABC\")"; + + PakfireParser parser = pakfire_parser_create(t->pakfire, NULL, NULL, + PAKFIRE_PARSER_FLAGS_EXPAND_COMMANDS); + ASSERT(parser); + + ASSERT(pakfire_parser_set(parser, NULL, "command", command) == 0); + + // Retrieve the expanded value + char* value = pakfire_parser_get(parser, NULL, "command"); + ASSERT_STRING_EQUALS(value, "ABC"); + + return EXIT_SUCCESS; +} + int main(int argc, char** argv) { testsuite_add_test(test_parser); testsuite_add_test(test_parser_files); + testsuite_add_test(test_parser_command); return testsuite_run(); }