]> git.ipfire.org Git - people/ms/pakfire.git/commitdiff
parser: Implement executing commands in %(...)
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Mar 2021 15:59:25 +0000 (15:59 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Mar 2021 15:59:25 +0000 (15:59 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/parser.c
tests/data/parser/test-declarations.txt
tests/libpakfire/parser.c

index 6411d8ff8a55df271485b04acb27551ab9bf1a7d..0c4e0bc762f98c980605c0ef01662c0855f509d2 100644 (file)
@@ -27,6 +27,7 @@
 #include <pcre2.h>
 
 #include <pakfire/errno.h>
+#include <pakfire/execute.h>
 #include <pakfire/logging.h>
 #include <pakfire/parser.h>
 #include <pakfire/pakfire.h>
@@ -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;
 }
 
index 115f7dbae11b1b2cc923e9cbf302287b34715974..4ee3b5480986516cf7d92ce1c9fc5a54880c1cd3 100644 (file)
@@ -16,3 +16,6 @@ end
 e = A \
        B \
        C
+
+# Commands
+command = %(echo "ABC")
index 6fc51f11eaf96e404dff99e6ff54c113b2f8a57d..0a93a91b323230d66acf957cbf930bef965ae153 100644 (file)
@@ -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();
 }