]> git.ipfire.org Git - pakfire.git/commitdiff
python: Rework Pakfire.execute() using jail
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Aug 2022 14:22:43 +0000 (14:22 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 3 Aug 2022 14:22:43 +0000 (14:22 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/_pakfire/pakfire.c

index 4ed49ffe247f658b76b362c898c75a0553aee066..d3323527d8461f02d2b78899566785745dfdd721 100644 (file)
@@ -26,7 +26,7 @@
 #include <pakfire/build.h>
 #include <pakfire/constants.h>
 #include <pakfire/dist.h>
-#include <pakfire/execute.h>
+#include <pakfire/jail.h>
 #include <pakfire/logging.h>
 #include <pakfire/mount.h>
 #include <pakfire/packagelist.h>
@@ -775,14 +775,13 @@ static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
        return PyLong_FromLong(cmp);
 }
 
-static PyObject* Pakfire_execute_logging_callback = NULL;
-
-static int __Pakfire_execute_logging_callback(struct pakfire* pakfire, void* data,
+static int __Pakfire_logging_callback(struct pakfire* pakfire, void* data,
                int priority, const char* line, size_t length) {
+       PyObject* callback = (PyObject*)data;
        int r = 0;
 
        // Do nothing if callback isn't set
-       if (!Pakfire_execute_logging_callback)
+       if (!callback)
                return 0;
 
        // Translate priority to Python logging priorities
@@ -805,7 +804,7 @@ static int __Pakfire_execute_logging_callback(struct pakfire* pakfire, void* dat
        if (!args)
                return 1;
 
-       PyObject* result = PyObject_CallObject(Pakfire_execute_logging_callback, args);
+       PyObject* result = PyObject_CallObject(callback, args);
     if (result && PyLong_Check(result)) {
         r = PyLong_AsLong(result);
     }
@@ -817,8 +816,20 @@ static int __Pakfire_execute_logging_callback(struct pakfire* pakfire, void* dat
 }
 
 static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwds) {
-       char* kwlist[] = {"command", "environ", "enable_network", "interactive",
-               "logging_callback", NULL};
+       char* kwlist[] = {
+               "command",
+               "environ",
+               "enable_network",
+               "interactive",
+               "logging_callback",
+               NULL
+       };
+
+       struct pakfire_jail* jail = NULL;
+       const char** argv = NULL;
+       int flags = 0;
+       int r;
+       PyObject* ret = NULL;
 
        PyObject* command = NULL;
        PyObject* environ = NULL;
@@ -833,17 +844,22 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
        // Check if command is a list
        if (!PyList_Check(command)) {
                PyErr_SetString(PyExc_TypeError, "command must be a list");
-               return NULL;
+               goto ERROR;
        }
 
-       ssize_t command_length = PyList_Size(command);
+       const ssize_t command_length = PyList_Size(command);
 
        // Check if command is not empty
        if (command_length == 0) {
                PyErr_SetString(PyExc_ValueError, "command is empty");
-               return NULL;
+               goto ERROR;
        }
 
+       // Allocate argv
+       argv = calloc(command_length + 1, sizeof(*argv));
+       if (!argv)
+               goto ERROR;
+
        // All arguments in command must be strings
        for (unsigned int i = 0; i < command_length; i++) {
                PyObject* item = PyList_GET_ITEM(command, i);
@@ -852,111 +868,104 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
                        PyErr_Format(PyExc_TypeError, "Item %u in command is not a string", i);
                        return NULL;
                }
+
+               // Copy to argv
+               argv[i] = PyUnicode_AsUTF8(item);
        }
 
-       // Check if logging_callback is
-       if (logging_callback && !PyCallable_Check(logging_callback)) {
-               PyErr_SetString(PyExc_TypeError, "logging_callback must be callable\n");
-               return NULL;
+#if 0
+       // Enable network?
+       if (enable_network)
+               flags |= PAKFIRE_JAIL_ENABLE_NETWORK;
+#endif
+
+       // Interactive?
+       if (interactive)
+               flags |= PAKFIRE_JAIL_INTERACTIVE;
+
+       // Create jail
+       r = pakfire_jail_create(&jail, self->pakfire, flags);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
+
+       // Set logging callback
+       if (logging_callback) {
+               if (!PyCallable_Check(logging_callback)) {
+                       PyErr_SetString(PyExc_TypeError, "logging_callback must be callable\n");
+                       goto ERROR;
+               }
+
+               pakfire_jail_set_log_callback(jail, __Pakfire_logging_callback, logging_callback);
        }
 
-       ssize_t environ_length = 0;
-       PyObject* key;
-       PyObject* value;
+       PyObject* key = NULL;
+       PyObject* value = NULL;
        Py_ssize_t p = 0;
 
+       // Parse the environment
        if (environ) {
                // Check if environ is a dictionary
                if (!PyDict_Check(environ)) {
                        PyErr_SetString(PyExc_TypeError, "environ must be a dictionary");
-                       return NULL;
+                       goto ERROR;
                }
 
                // All keys and values must be strings
                while (PyDict_Next(environ, &p, &key, &value)) {
                        if (!PyUnicode_Check(key) || !PyUnicode_Check(value)) {
                                PyErr_SetString(PyExc_TypeError, "Environment contains a non-string object");
-                               return NULL;
+                               goto ERROR;
                        }
-               }
-
-               environ_length = PyDict_Size(environ);
-       }
-
-       // All inputs look fine
 
-       const char* argv[command_length + 1];
-       char* envp[environ_length + 1];
-       int flags = 0;
-
-       // Parse arguments
-       for (unsigned int i = 0; i < command_length; i++) {
-               PyObject* item = PyList_GET_ITEM(command, i);
-               argv[i] = PyUnicode_AsUTF8(item);
-       }
-
-       // Parse environ
-       if (environ) {
-               unsigned int i = 0;
-               p = 0;
-
-               while (PyDict_Next(environ, &p, &key, &value)) {
-                       int r = asprintf(&envp[i++], "%s=%s",
-                               PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));
-
-                       // Handle errors
-                       if (r < 0) {
-                               // Cleanup
-                               for (i = 0; envp[i]; i++)
-                                       free(envp[i]);
-
-                               return PyErr_NoMemory();
+                       // Set environment value
+                       r = pakfire_jail_set_env(jail, PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));
+                       if (r) {
+                               PyErr_SetFromErrno(PyExc_OSError);
+                               goto ERROR;
                        }
                }
        }
 
-       // Terminate argv and envp
-       argv[command_length] = NULL;
-       envp[environ_length] = NULL;
-
-       // Enable network?
-       if (enable_network)
-               flags |= PAKFIRE_EXECUTE_ENABLE_NETWORK;
-
-       // Interactive?
-       if (interactive)
-               flags |= PAKFIRE_EXECUTE_INTERACTIVE;
-
-       // Set logging callback
-       Pakfire_execute_logging_callback = logging_callback;
-
        // Execute command
-       int r = pakfire_execute(self->pakfire, argv, envp, flags,
-               (logging_callback) ? __Pakfire_execute_logging_callback : NULL, NULL);
-
-       // Cleanup
-       for (unsigned int i = 0; envp[i]; i++)
-               free(envp[i]);
+       r = pakfire_jail_exec(jail, argv, NULL);
 
-       // Raise an OS error if r < 0
+       // If the return code was negative, we had some internal error
        if (r < 0) {
-               errno = -r;
-
                PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
+               goto ERROR;
 
-       // Raise exception when the command failed
+       // Otherwise the executed command returned some error code
        } else if (r > 0) {
                PyObject* code = PyLong_FromLong(r);
 
+               // Raise CommandExecutionError
                PyErr_SetObject(PyExc_CommandExecutionError, code);
                Py_DECREF(code);
 
-               return NULL;
+               goto ERROR;
        }
 
-       // Return nothing
-       Py_RETURN_NONE;
+       // Return None if everything was successful
+       ret = Py_None;
+       Py_INCREF(ret);
+
+ERROR:
+       if (argv)
+               free(argv);
+
+       if (jail) {
+               // Dereference the logging callback
+               // It might happen that the jail is not being freed because something else is
+               // holding a reference to it. We will however lose the reference to the logging
+               // function here which is why we reset it.
+               pakfire_jail_set_log_callback(jail, NULL, NULL);
+
+               pakfire_jail_unref(jail);
+       }
+
+       return ret;
 }
 
 static PyObject* Pakfire_dist(PakfireObject* self, PyObject* args) {