From 50a6ca9d248403e8a2a165e18b4974c43d5a827e Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Tue, 18 Mar 2025 09:31:13 +0000 Subject: [PATCH] python: execute: Implement passing data into stdin Signed-off-by: Michael Tremer --- src/python/pakfire.c | 43 ++++++++++++++++++++++++++++++++++++----- tests/python/execute.py | 18 +++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/python/pakfire.c b/src/python/pakfire.c index 9dbcfb7c..8f752bf6 100644 --- a/src/python/pakfire.c +++ b/src/python/pakfire.c @@ -532,6 +532,33 @@ ERROR: * Execute */ +struct Pakfire_execute_input { + const char* data; + size_t length; +}; + +static ssize_t Pakfire_execute_stdin_callback( + struct pakfire_ctx* ctx, void* data, char* buffer, size_t length) { + struct Pakfire_execute_input* input = data; + + // Return zero if we are done writing the input + if (!input->length) + return 0; + + // Cap the memory if we have less input + if (input->length < length) + length = input->length; + + // Copy the data into the output buffer + memcpy(buffer, input->data, length); + + // Move the pointer forward + input->data += length; + input->length -= length; + + return length; +} + static int Pakfire_execute_stdout_callback( struct pakfire_ctx* ctx, void* data, const char* line, const size_t length) { PyObject** output = data; @@ -549,6 +576,7 @@ static int Pakfire_execute_stdout_callback( } static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwargs) { + struct Pakfire_execute_input input = {}; struct pakfire_jail* jail = NULL; struct pakfire_env* env = NULL; const char** argv = NULL; @@ -558,6 +586,8 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* Py_ssize_t p = 0; int r; + pakfire_pty_stdin_callback stdin_callback = NULL; + PyObject* command = NULL; PyObject* environ = NULL; int nice = 0; @@ -565,19 +595,18 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* PyObject* output = NULL; int return_output = 0; - pakfire_pty_stdout_callback stdout_callback = NULL; - const char* kwlist[] = { "command", "environ", "nice", "return_output", + "input", NULL, }; // Parse arguments - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oib", (char**)kwlist, - &command, &environ, &nice, &return_output)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oiby#", (char**)kwlist, + &command, &environ, &nice, &return_output, &input.data, &input.length)) goto ERROR; // Check if command is a list @@ -651,6 +680,10 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* if (!output) goto ERROR; + // Register the input callback + if (input.data) + stdin_callback = Pakfire_execute_stdin_callback; + // Create a new jail r = pakfire_jail_create(&jail, self->pakfire); if (r < 0) { @@ -673,7 +706,7 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* // Execute command r = pakfire_jail_communicate(jail, argv, env, 0, - NULL, NULL, Pakfire_execute_stdout_callback, &output); + stdin_callback, &input, Pakfire_execute_stdout_callback, &output); Py_END_ALLOW_THREADS diff --git a/tests/python/execute.py b/tests/python/execute.py index 991c5adc..55be12e9 100755 --- a/tests/python/execute.py +++ b/tests/python/execute.py @@ -100,6 +100,24 @@ class ExecuteTests(tests.TestCase): output = self.pakfire.execute(["/command", "lines", "100", "40"], return_output=True) self.assertEquals(output.count(b"\n"), 100) + def test_execute_input(self): + def test(input, output=None): + #if output is None: + # output = input + + # Pipe the data through the command + output = self.pakfire.execute(["/command", "pipe"], + input=input, return_output=True) + + # Input and output must match + self.assertEqual(input, output) + + # Test a simple, short string with a line ending + test(b"1234\n") + + # Test a very long string + test(b"A" * 8192 + b"\n") + def test_nice(self): self.pakfire.execute(["/command", "print-nice"], nice=5) -- 2.39.5