]> git.ipfire.org Git - people/ms/pakfire.git/commitdiff
python: execute: Implement passing data into stdin
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 18 Mar 2025 09:31:13 +0000 (09:31 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 18 Mar 2025 09:31:13 +0000 (09:31 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/python/pakfire.c
tests/python/execute.py

index 9dbcfb7cd3725558eb301704426dd182172ee73f..8f752bf6226b0d9223cded1eea35bf6136386fae 100644 (file)
@@ -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
 
index 991c5adc0d9889bbc469db1c1f95b72a1a95057b..55be12e93ae8ca884d471f25a687387c57baf338 100755 (executable)
@@ -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)