]> git.ipfire.org Git - people/ms/pakfire.git/blobdiff - src/_pakfire/pakfire.c
python: Make sure we hold the GIL when jumping into the logging callback
[people/ms/pakfire.git] / src / _pakfire / pakfire.c
index ea25f3ed2fad8ca31c9ee21a83244d2d0a290eec..b82f28a54250ce2aa4593077b253c7062714b938 100644 (file)
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <errno.h>
+#include <syslog.h>
 
 #include <pakfire/archive.h>
 #include <pakfire/build.h>
 #include <pakfire/constants.h>
+#include <pakfire/ctx.h>
 #include <pakfire/dist.h>
 #include <pakfire/jail.h>
 #include <pakfire/logging.h>
 #include <pakfire/key.h>
 #include <pakfire/repo.h>
 #include <pakfire/repolist.h>
-#include <pakfire/request.h>
 #include <pakfire/transaction.h>
 #include <pakfire/util.h>
 
 #include "archive.h"
+#include "ctx.h"
 #include "errors.h"
 #include "key.h"
 #include "pakfire.h"
 static PyObject* Pakfire_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
        PakfireObject* self = (PakfireObject *)type->tp_alloc(type, 0);
        if (self) {
+               self->ctx = NULL;
                self->pakfire = NULL;
-
-               // Callbacks
-               self->callbacks.log = NULL;
-               self->callbacks.confirm = NULL;
        }
 
        return (PyObject *)self;
 }
 
-static void Pakfire_log_callback(void* data, int priority, const char* file, int line,
-               const char* fn, const char* format, va_list args) {
-       PyObject* callback = (PyObject*)data;
-       PyObject* exception = NULL;
-
-       // Do nothing if callback isn't set
-       if (!callback)
-               return;
-
-       // Translate priority to Python logging priorities
-       switch (priority) {
-               case LOG_DEBUG:
-                       priority = 10;
-                       break;
-
-               case LOG_INFO:
-                       priority = 20;
-                       break;
-
-               case LOG_ERR:
-                       priority = 40;
-                       break;
-
-               // Drop messages of an unknown priority
-               default:
-                       return;
-       }
-
-       PyObject* tuple = NULL;
-       PyObject* result = NULL;
-       char* buffer = NULL;
-
-       // Make line
-       int r = vasprintf(&buffer, format, args);
-       if (r < 0)
-               goto ERROR;
-
-       // Build a tuple with the priority and the log message
-       tuple = Py_BuildValue("(is)", priority, buffer);
-       if (!tuple)
-               goto ERROR;
-
-       // Call the callback
-       result = PyObject_CallObject(callback, tuple);
-
-ERROR:
-       /*
-               We cannot really catch any Python errors here, since we cannot send
-               any error codes up the chain.
-
-               So, in order to debug the problem, We will check if an exception has
-               occurred and if so, print it to the console.
-       */
-       exception = PyErr_Occurred();
-       if (exception)
-               PyErr_Print();
-
-       if (buffer)
-               free(buffer);
-       Py_XDECREF(tuple);
-       Py_XDECREF(result);
-}
-
-static int Pakfire_confirm_callback(struct pakfire* pakfire, void* data,
-               const char* message, const char* question) {
-       PyObject* callback = (PyObject*)data;
-       int r = 0;
-
-       // Do nothing if callback isn't set
-       if (!callback)
-               return 0;
-
-       PyObject* args = Py_BuildValue("(ss)", message, question);
-       if (!args)
-               return -1;
-
-       PyObject* result = PyObject_CallObject(callback, args);
-
-       // If the callback raised an exception, we will ignore it and indicate
-       // that an error happened, but we cannot re-raise the exception
-       if (!result) {
-               r = -1;
-               goto ERROR;
-       }
-
-       // Set the return code
-       if (result == Py_True)
-               r = 0;
-       else
-               r = 1;
-
-ERROR:
-       Py_DECREF(args);
-       Py_DECREF(result);
-
-       return r;
-}
-
 static int Pakfire_init(PakfireObject* self, PyObject* args, PyObject* kwds) {
        char* kwlist[] = {
+               "ctx",
                "path",
                "arch",
-               "logger",
-               "offline",
                "conf",
-               "confirm_callback",
                NULL,
        };
+       CtxObject* ctx = NULL;
        const char* path = NULL;
        const char* arch = NULL;
-       const char* conf = NULL;
-       int offline = 0;
+       PyObject* conf = Py_None;
+       int r = 1;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzOpzO", kwlist,
-                       &path, &arch, &self->callbacks.log, &offline, &conf, &self->callbacks.confirm))
-               return -1;
+       FILE* fconf = NULL;
 
-       // Check if log callback is callable
-       if (self->callbacks.log && !PyCallable_Check(self->callbacks.log)) {
-               PyErr_SetString(PyExc_TypeError, "logger must be callable\n");
-               return -1;
-       }
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|zzO", kwlist,
+                       &CtxType, &ctx, &path, &arch, &conf))
+               goto ERROR;
 
-       // Check if confirm callback is callable
-       if (self->callbacks.confirm && !PyCallable_Check(self->callbacks.confirm)) {
-               PyErr_SetString(PyExc_TypeError, "Confirm callback is not callable");
-               return -1;
+       // Map the configuration
+       if (conf != Py_None) {
+               fconf = PyObject_AsFileHandle(conf, "r");
+               if (!fconf)
+                       goto ERROR;
        }
 
        int flags = 0;
 
-       // Enable offline mode
-       if (offline)
-               flags |= PAKFIRE_FLAGS_OFFLINE;
+       Py_BEGIN_ALLOW_THREADS
 
-       // Configure callbacks
-       if (self->callbacks.log)
-               Py_INCREF(self->callbacks.log);
+       // Store a reference to the context
+       self->ctx = pakfire_ctx_ref(ctx->ctx);
 
        // Create a new Pakfire instance
-       int r = pakfire_create(&self->pakfire, path, arch, conf, flags,
-               LOG_DEBUG, Pakfire_log_callback, self->callbacks.log);
-       if (r) {
+       r = pakfire_create(&self->pakfire, self->ctx, path, arch, fconf, flags);
+
+       Py_END_ALLOW_THREADS
+
+       if (r < 0) {
+               errno = -r;
+
                switch (errno) {
                        // Invalid architecture or path
                        case EINVAL:
@@ -212,57 +110,48 @@ static int Pakfire_init(PakfireObject* self, PyObject* args, PyObject* kwds) {
                                PyErr_SetFromErrno(PyExc_OSError);
                }
 
-               return -1;
+               r = -1;
+               goto ERROR;
     }
 
-       // Configure confirm callback
-       if (self->callbacks.confirm) {
-               pakfire_set_confirm_callback(self->pakfire,
-                       Pakfire_confirm_callback, self->callbacks.confirm);
-
-               Py_INCREF(self->callbacks.confirm);
-       }
+ERROR:
+       if (fconf)
+               fclose(fconf);
 
-       return 0;
+       return -r;
 }
 
 static void Pakfire_dealloc(PakfireObject* self) {
        if (self->pakfire) {
-               // Reset log callback
-               if (self->callbacks.log) {
-                       pakfire_set_log_callback(self->pakfire, NULL, NULL);
-                       Py_DECREF(self->callbacks.log);
-               }
-
-               // Reset confirm callback
-               if (self->callbacks.confirm) {
-                       pakfire_set_confirm_callback(self->pakfire, NULL, NULL);
-                       Py_DECREF(self->callbacks.confirm);
-               }
+               Py_BEGIN_ALLOW_THREADS
 
                pakfire_unref(self->pakfire);
+
+               Py_END_ALLOW_THREADS
        }
+       if (self->ctx)
+               pakfire_ctx_unref(self->ctx);
 
        Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static PyObject* Pakfire_repr(PakfireObject* self) {
-    const char* path = pakfire_get_path(self->pakfire);
-    const char* arch = pakfire_get_arch(self->pakfire);
+       const char* path = pakfire_get_path(self->pakfire);
+       const char* arch = pakfire_get_arch(self->pakfire);
 
        return PyUnicode_FromFormat("<_pakfire.Pakfire %s (%s)>", path, arch);
 }
 
 static PyObject* Pakfire_get_path(PakfireObject* self) {
-    const char* path = pakfire_get_path(self->pakfire);
+       const char* path = pakfire_get_path(self->pakfire);
 
-    return PyUnicode_FromString(path);
+       return PyUnicode_FromString(path);
 }
 
 static PyObject* Pakfire_get_arch(PakfireObject* self) {
-    const char* arch = pakfire_get_arch(self->pakfire);
+       const char* arch = pakfire_get_arch(self->pakfire);
 
-    return PyUnicode_FromString(arch);
+       return PyUnicode_FromString(arch);
 }
 
 static PyObject* Pakfire_get_repo(PakfireObject* self, PyObject* args) {
@@ -281,26 +170,6 @@ static PyObject* Pakfire_get_repo(PakfireObject* self, PyObject* args) {
        return obj;
 }
 
-static void Pakfire_status_callback(struct pakfire* pakfire, void* data,
-               int progress, const char* status) {
-       PyObject* callback = (PyObject*)data;
-
-       // Do not attempt to call nothing
-       if (!callback)
-               return;
-
-       // Compile arguments
-       PyObject* args = Py_BuildValue("(is)", progress, status);
-       if (!args)
-               return;
-
-       // Call the callback
-       PyObject* result = PyObject_CallObject(callback, args);
-
-       Py_XDECREF(result);
-       Py_DECREF(args);
-}
-
 static int convert_packages(PyObject* object, void* address) {
        char*** packages = (char***)address;
 
@@ -369,259 +238,20 @@ ERROR:
        return 0;
 }
 
-static PyObject* Pakfire_install(PakfireObject* self, PyObject* args, PyObject* kwargs) {
-       char* kwlist[] = {
-               "packages",
-               "dryrun",
-               "without_recommended",
-               "allow_uninstall",
-               "allow_downgrade",
-               "status_callback",
-               NULL
-       };
-       char** packages = NULL;
-       int dryrun = 0;
-       int without_recommended = 0;
-       int allow_uninstall = 0;
-       int allow_downgrade = 0;
-       int solver_flags = 0;
-       int transaction_flags = 0;
-       PyObject* status_callback = NULL;
-
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$ppppO", kwlist,
-                       convert_packages, &packages, &dryrun, &without_recommended, &allow_uninstall,
-                       &allow_downgrade, &status_callback))
-               return NULL;
-
-       // Check if callback is callable
-       if (status_callback && !PyCallable_Check(status_callback)) {
-               PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
-               return NULL;
-       }
-
-       // Enable dry-run mode
-       if (dryrun)
-               transaction_flags |= PAKFIRE_TRANSACTION_DRY_RUN;
-
-       // Do not install recommended packages
-       if (without_recommended)
-               solver_flags |= PAKFIRE_REQUEST_WITHOUT_RECOMMENDED;
-
-       // Can the solver uninstall packages?
-       if (allow_uninstall)
-               solver_flags |= PAKFIRE_REQUEST_ALLOW_UNINSTALL;
-
-       // Can the solver downgrade packages?
-       if (allow_downgrade)
-               solver_flags |= PAKFIRE_REQUEST_ALLOW_DOWNGRADE;
-
-       // Run pakfire_install
-       int r = pakfire_install(self->pakfire, transaction_flags, solver_flags,
-               (const char**)packages, NULL, 0, NULL, Pakfire_status_callback, status_callback);
-       if (r)
-               PyErr_SetFromErrno(PyExc_OSError);
-
-       if (packages) {
-               for (char** package = packages; *package; package++)
-                       free(*package);
-               free(packages);
-       }
-
-       if (r)
-               return NULL;
-
-       Py_RETURN_NONE;
-}
-
-static PyObject* Pakfire_erase(PakfireObject* self, PyObject* args, PyObject* kwargs) {
-       char* kwlist[] = {
-               "packages",
-               "dryrun",
-               "keep_dependencies",
-               "status_callback",
-               NULL
-       };
-       char** packages = NULL;
-       int dryrun = 0;
-       int keep_dependencies = 0;
-       int transaction_flags = 0;
-       int flags = 0;
-       PyObject* status_callback = NULL;
-
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$ppO", kwlist,
-                       convert_packages, &packages, &dryrun, &keep_dependencies, &status_callback))
-               return NULL;
-
-       // Check if callback is callable
-       if (status_callback && !PyCallable_Check(status_callback)) {
-               PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
-               return NULL;
-       }
-
-       if (dryrun)
-               transaction_flags |= PAKFIRE_TRANSACTION_DRY_RUN;
-
-       if (keep_dependencies)
-               flags |= PAKFIRE_REQUEST_KEEP_DEPS;
-
-       // Run pakfire_erase
-       int r = pakfire_erase(self->pakfire, transaction_flags, 0, (const char**)packages,
-               NULL, flags, NULL, Pakfire_status_callback, status_callback);
-       if (r)
-               PyErr_SetFromErrno(PyExc_OSError);
-
-       if (packages) {
-               for (char** package = packages; *package; package++)
-                       free(*package);
-               free(packages);
-       }
-
-       if (r)
-               return NULL;
-
-       Py_RETURN_NONE;
-}
-
-static PyObject* Pakfire_update(PakfireObject* self, PyObject* args, PyObject* kwargs) {
-       char* kwlist[] = {
-               "packages",
-               "dryrun",
-               "excludes",
-               "allow_uninstall",
-               "allow_downgrade",
-               "status_callback",
-               NULL
-       };
-       char** packages = NULL;
-       char** excludes = NULL;
-       int dryrun = 0;
-       int allow_uninstall = 0;
-       int allow_downgrade = 0;
-       int solver_flags = 0;
-       int transaction_flags = 0;
-       PyObject* status_callback = NULL;
-
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$pO&ppO", kwlist,
-                       convert_packages, &packages, &dryrun, convert_packages, &excludes,
-                       &allow_uninstall, &allow_downgrade, &status_callback))
-               return NULL;
-
-       // Check if callback is callable
-       if (status_callback && !PyCallable_Check(status_callback)) {
-               PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
-               return NULL;
-       }
-
-       if (dryrun)
-               transaction_flags = PAKFIRE_TRANSACTION_DRY_RUN;
-
-       // Can the solver uninstall packages?
-       if (allow_uninstall)
-               solver_flags |= PAKFIRE_REQUEST_ALLOW_UNINSTALL;
-
-       // Can the solver downgrade packages?
-       if (allow_downgrade)
-               solver_flags |= PAKFIRE_REQUEST_ALLOW_DOWNGRADE;
-
-       // Run pakfire_update
-       int r = pakfire_update(self->pakfire, transaction_flags, solver_flags,
-               (const char**)packages, (const char**)excludes, 0, NULL,
-               Pakfire_status_callback, status_callback);
-       if (r)
-               PyErr_SetFromErrno(PyExc_OSError);
-
-       if (packages) {
-               for (char** package = packages; *package; package++)
-                       free(*package);
-               free(packages);
-       }
-
-       if (excludes) {
-               for (char** exclude = excludes; *exclude; exclude++)
-                       free(*exclude);
-               free(excludes);
-       }
-
-       if (r)
-               return NULL;
-
-       Py_RETURN_NONE;
-}
-
-static PyObject* Pakfire_keys_to_list(struct pakfire_key** keys) {
-       PyObject* list = PyList_New(0);
-
-       // Empty input?
-       if (!keys)
-               return list;
-
-       // Push all keys onto the list
-       for (struct pakfire_key** key = keys; *key; key++) {
-               PyObject* object = new_key(&KeyType, *key);
-               if (!object)
-                       goto ERROR;
-
-               PyList_Append(list, object);
-               Py_DECREF(object);
-       }
-
-       return list;
-
-ERROR:
-       Py_DECREF(list);
-       return NULL;
-}
-
-static PyObject* Pakfire_get_keys(PakfireObject* self) {
-       struct pakfire_key** keys = NULL;
-
-       int r = pakfire_list_keys(self->pakfire, &keys);
-       if (r) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
-
-       // Convert keys to list
-       PyObject* list = Pakfire_keys_to_list(keys);
-
-       // Free keys
-       if (keys) {
-               for (struct pakfire_key** key = keys; *key; key++)
-                       pakfire_key_unref(*key);
-               free(keys);
-       }
-
-       return list;
-}
-
-static PyObject* Pakfire_get_key(PakfireObject* self, PyObject* args) {
-       const char* pattern = NULL;
-
-       if (!PyArg_ParseTuple(args, "s", &pattern))
-               return NULL;
-
-       // Try finding the key
-       struct pakfire_key* key = pakfire_key_get(self->pakfire, pattern);
-       if (!key)
-               Py_RETURN_NONE;
-
-       PyObject* object = new_key(&KeyType, key);
-       pakfire_key_unref(key);
-
-       return object;
-}
-
+/*
+       XXX This could be moved out of here as this no longer depends on Pakfire
+*/
 static PyObject* Pakfire_generate_key(PakfireObject* self, PyObject* args, PyObject* kwds) {
-       char* kwlist[] = { "userid", "algorithm", NULL };
+       char* kwlist[] = { "algorithm", "comment", NULL };
        struct pakfire_key* key = NULL;
-       const char* userid = NULL;
-       const char* algo = NULL;
+       pakfire_key_algo_t algo = PAKFIRE_KEY_ALGO_NULL;
+       const char* comment = NULL;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|$z", kwlist, &userid, &algo))
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, &algo, &comment))
                return NULL;
 
        // Generate a new key
-       int r = pakfire_key_generate(&key, self->pakfire, algo, userid);
+       int r = pakfire_key_generate(&key, self->ctx, algo, comment);
        if (r) {
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
@@ -632,108 +262,114 @@ static PyObject* Pakfire_generate_key(PakfireObject* self, PyObject* args, PyObj
 
        return object;
 }
-
+/*
+       XXX This could be moved out of here as this no longer depends on Pakfire
+*/
 static PyObject* Pakfire_import_key(PakfireObject* self, PyObject* args) {
+       struct pakfire_key* key = NULL;
        PyObject* object = NULL;
+       char* data = NULL;
+       Py_ssize_t data_length = 0;
+       int r;
 
-       if (!PyArg_ParseTuple(args, "O", &object))
+       // Parse arguments
+       if (!PyArg_ParseTuple(args, "s#", &data, &data_length))
                return NULL;
 
-       // Get a file descriptor from object
-       int fd = PyObject_AsFileDescriptor(object);
-       if (fd < 0)
+       // Map the object
+       FILE* f = fmemopen(data, data_length, "r");
+       if (!f)
                return NULL;
 
-       // Convert to FILE*
-       FILE* f = fdopen(fd, "r");
-       if (!f) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
-
-       struct pakfire_key** keys = NULL;
-
-       // Import keys from f
-       int r = pakfire_key_import(self->pakfire, f, &keys);
+       // Import the key
+       r = pakfire_key_import(&key, self->ctx, f);
        if (r) {
                PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
+               goto ERROR;
        }
 
-       // Convert keys to list
-       PyObject* list = Pakfire_keys_to_list(keys);
-
-       // Free keys
-       for (struct pakfire_key** key = keys; *key; key++)
-               pakfire_key_unref(*key);
-       free(keys);
-
-       return list;
-}
-
-static PyObject* Pakfire_fetch_key(PakfireObject* self, PyObject* args, PyObject* kwds) {
-       char* kwlist[] = { "userid", "fingerprint", NULL };
-       struct pakfire_key* key = NULL;
-       const char* userid = NULL;
-       const char* fingerprint = NULL;
-
-       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$zz", kwlist, &userid, &fingerprint))
-               return NULL;
-
-       // Fetch the key
-       int r = pakfire_key_fetch(&key, self->pakfire, userid, fingerprint);
-       if (r) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
+       // Convert the key into a Key object
+       object = new_key(&KeyType, key);
+       if (!object)
+               goto ERROR;
 
-       // Return the result
-       if (key) {
-               PyObject* object = new_key(&KeyType, key);
+ERROR:
+       if (key)
                pakfire_key_unref(key);
+       if (f)
+               fclose(f);
 
-               return object;
-       }
-
-       Py_RETURN_NONE;
+       return object;
 }
 
 static PyObject* Pakfire_whatprovides(PakfireObject* self, PyObject* args) {
        const char* provides = NULL;
        struct pakfire_packagelist* list = NULL;
+       PyObject* ret = NULL;
+       int r;
 
        if (!PyArg_ParseTuple(args, "s", &provides))
                return NULL;
 
-       int r = pakfire_whatprovides(self->pakfire, provides, 0, &list);
+       // Create a new list
+       r = pakfire_packagelist_create(&list, self->ctx);
        if (r) {
                PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
+               goto ERROR;
        }
 
-       PyObject* obj = PyList_FromPackageList(list);
-       pakfire_packagelist_unref(list);
+       r = pakfire_whatprovides(self->pakfire, provides, 0, list);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
 
-       return obj;
+       // Create a Python list from the package list
+       ret = PyList_FromPackageList(list);
+
+ERROR:
+       if (list)
+               pakfire_packagelist_unref(list);
+
+       return ret;
 }
 
 static PyObject* Pakfire_whatrequires(PakfireObject* self, PyObject* args) {
        const char* requires = NULL;
        struct pakfire_packagelist* list = NULL;
+       PyObject* ret = NULL;
+       int r;
 
        if (!PyArg_ParseTuple(args, "s", &requires))
                return NULL;
 
-       int r = pakfire_whatrequires(self->pakfire, requires, 0, &list);
+       Py_BEGIN_ALLOW_THREADS
+
+       // Create a new list
+       r = pakfire_packagelist_create(&list, self->ctx);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
+               goto ERROR;
+       }
+
+       r = pakfire_whatrequires(self->pakfire, requires, 0, list);
+       if (r) {
+               Py_BLOCK_THREADS
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
        }
 
-       PyObject* obj = PyList_FromPackageList(list);
-       pakfire_packagelist_unref(list);
+       Py_END_ALLOW_THREADS
 
-       return obj;
+       // Create a Python list from the package list
+       ret = PyList_FromPackageList(list);
+
+ERROR:
+       if (list)
+               pakfire_packagelist_unref(list);
+
+       return ret;
 }
 
 static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* kwds) {
@@ -742,6 +378,8 @@ static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* k
        const char* pattern = NULL;
        int name_only = 0;
        int flags = 0;
+       PyObject* ret = NULL;
+       int r;
 
        if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|$p", kwlist, &pattern, &name_only))
                return NULL;
@@ -750,16 +388,25 @@ static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* k
        if (name_only)
                flags |= PAKFIRE_SEARCH_NAME_ONLY;
 
-       int r = pakfire_search(self->pakfire, pattern, flags, &list);
+       r = pakfire_packagelist_create(&list, self->ctx);
        if (r) {
                PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
+               goto ERROR;
        }
 
-       PyObject* obj = PyList_FromPackageList(list);
-       pakfire_packagelist_unref(list);
+       r = pakfire_search(self->pakfire, pattern, flags, list);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
 
-       return obj;
+       ret = PyList_FromPackageList(list);
+
+ERROR:
+       if (list)
+               pakfire_packagelist_unref(list);
+
+       return ret;
 }
 
 static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
@@ -774,43 +421,42 @@ static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
        return PyLong_FromLong(cmp);
 }
 
-static int Pakfire_execute_output_callback(struct pakfire* pakfire, void* data,
-               int priority, const char* line, size_t length) {
+static int Pakfire_execute_output_callback(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
+               void* data, const char* line, size_t length) {
        PyObject* callback = (PyObject*)data;
-       int r = 0;
+       PyObject* args = NULL;
+       PyObject* result = NULL;
+       int r = 1;
 
        // Do nothing if callback isn't set
        if (!callback)
                return 0;
 
-       // Translate priority to Python logging priorities
-       switch (priority) {
-               case LOG_INFO:
-                       priority = 20;
-                       break;
-
-               case LOG_ERR:
-                       priority = 40;
-                       break;
-       }
-
        // Remove the trailing newline
-       if (line && line[length - 1] == '\n')
+       if (line && length && line[length - 1] == '\n')
                length--;
 
+       // Get the GIL
+       PyGILState_STATE state = PyGILState_Ensure();
+
        // Create tuple with arguments for the callback function
-       PyObject* args = Py_BuildValue("(is#)", priority, line, (Py_ssize_t)length);
+       args = Py_BuildValue("(is#)", LOG_INFO, line, (Py_ssize_t)length);
        if (!args)
-               return 1;
+               goto ERROR;
 
-       PyObject* result = PyObject_CallObject(callback, args);
+       // Call the callback method
+       result = PyObject_CallObject(callback, args);
     if (result && PyLong_Check(result)) {
         r = PyLong_AsLong(result);
     }
 
+ERROR:
        Py_XDECREF(args);
        Py_XDECREF(result);
 
+       // Release the GIL
+       PyGILState_Release(state);
+
        return r;
 }
 
@@ -826,7 +472,6 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
 
        struct pakfire_jail* jail = NULL;
        const char** argv = NULL;
-       int flags = 0;
        int r;
        PyObject* ret = NULL;
 
@@ -865,7 +510,7 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
 
                if (!PyUnicode_Check(item)) {
                        PyErr_Format(PyExc_TypeError, "Item %u in command is not a string", i);
-                       return NULL;
+                       goto ERROR;
                }
 
                // Copy to argv
@@ -885,18 +530,12 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
        }
 
        // Create jail
-       r = pakfire_jail_create(&jail, self->pakfire, flags);
+       r = pakfire_jail_create(&jail, self->pakfire);
        if (r) {
                PyErr_SetFromErrno(PyExc_OSError);
                goto ERROR;
        }
 
-       // Check callback
-       if (callback && !PyCallable_Check(callback)) {
-               PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
-               goto ERROR;
-       }
-
        // Set nice
        if (nice) {
                r = pakfire_jail_nice(jail, nice);
@@ -934,37 +573,45 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject*
                }
        }
 
-       const Py_ssize_t num_bind = PySequence_Length(bind);
-
        // Bind
-       for (unsigned int i = 0; i < num_bind; i++) {
-               PyObject* b = PySequence_ITEM(bind, i);
-               if (!b)
-                       goto ERROR;
+       if (bind && PySequence_Check(bind)) {
+               const Py_ssize_t num_bind = PySequence_Length(bind);
 
-               // Check if this is a Unicode object
-               if (!PyUnicode_Check(b)) {
-                       PyErr_SetString(PyExc_ValueError, "bind contains a non-Unicode object");
-                       Py_DECREF(b);
-                       goto ERROR;
-               }
+               for (unsigned int i = 0; i < num_bind; i++) {
+                       PyObject* b = PySequence_ITEM(bind, i);
+                       if (!b)
+                               goto ERROR;
 
-               const char* path = PyUnicode_AsUTF8(b);
+                       // Check if this is a Unicode object
+                       if (!PyUnicode_Check(b)) {
+                               PyErr_SetString(PyExc_ValueError, "bind contains a non-Unicode object");
+                               Py_DECREF(b);
+                               goto ERROR;
+                       }
+
+                       const char* path = PyUnicode_AsUTF8(b);
+
+                       // Perform bind
+                       r = pakfire_jail_bind(jail, path, path, 0);
+                       if (r) {
+                               PyErr_SetFromErrno(PyExc_OSError);
+                               Py_DECREF(b);
+                               goto ERROR;
+                       }
 
-               // Perform bind
-               r = pakfire_jail_bind(jail, path, path, 0);
-               if (r) {
-                       PyErr_SetFromErrno(PyExc_OSError);
                        Py_DECREF(b);
-                       goto ERROR;
                }
-
-               Py_DECREF(b);
        }
 
+       Py_BEGIN_ALLOW_THREADS
+
+       // Set callback
+       pakfire_jail_set_stdout_callback(jail, Pakfire_execute_output_callback, callback);
+
        // Execute command
-       r = pakfire_jail_exec_communicate(jail, argv,
-               NULL, Pakfire_execute_output_callback, callback);
+       r = pakfire_jail_exec(jail, argv, 0);
+
+       Py_END_ALLOW_THREADS
 
        // If the return code was negative, we had some internal error
        if (r < 0) {
@@ -1001,54 +648,28 @@ static PyObject* Pakfire_dist(PakfireObject* self, PyObject* args) {
        const char* path = NULL;
        const char* target = NULL;
        char* result = NULL;
+       int r;
 
        if (!PyArg_ParseTuple(args, "s|z", &path, &target))
                return NULL;
 
-       int r = pakfire_dist(self->pakfire, path, target, &result);
+       Py_BEGIN_ALLOW_THREADS
+
+       r = pakfire_dist(self->pakfire, path, target, &result);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
        }
 
+       Py_END_ALLOW_THREADS
+
        PyObject* ret = PyUnicode_FromString(result);
        free(result);
 
        return ret;
 }
 
-static PyObject* Pakfire_copy_in(PakfireObject* self, PyObject* args) {
-       const char* src = NULL;
-       const char* dst = NULL;
-
-       if (!PyArg_ParseTuple(args, "ss", &src, &dst))
-               return NULL;
-
-       int r = pakfire_copy_in(self->pakfire, src, dst);
-       if (r) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
-
-       Py_RETURN_NONE;
-}
-
-static PyObject* Pakfire_copy_out(PakfireObject* self, PyObject* args) {
-       const char* src = NULL;
-       const char* dst = NULL;
-
-       if (!PyArg_ParseTuple(args, "ss", &src, &dst))
-               return NULL;
-
-       int r = pakfire_copy_out(self->pakfire, src, dst);
-       if (r) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
-
-       Py_RETURN_NONE;
-}
-
 static PyObject* Pakfire_get_repos(PakfireObject* self) {
        struct pakfire_repolist* repos = pakfire_get_repos(self->pakfire);
        if (!repos) {
@@ -1105,23 +726,27 @@ static PyObject* Pakfire_build(PakfireObject* self, PyObject* args, PyObject* kw
                "path",
                "target",
                "build_id",
+               "ccache_path",
                "interactive",
                "disable_snapshot",
                "disable_ccache",
                "disable_tests",
                NULL,
        };
-
+       struct pakfire_build* build = NULL;
        const char* path = NULL;
        const char* target = NULL;
        const char* build_id = NULL;
+       const char* ccache_path = NULL;
        int interactive = 0;
        int disable_snapshot = 0;
        int disable_ccache = 0;
        int disable_tests = 0;
+       int r;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzpppp", kwlist, &path, &target,
-                       &build_id, &interactive, &disable_snapshot, &disable_ccache, &disable_tests))
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzzpppp", kwlist,
+                       &path, &target, &build_id, &ccache_path, &interactive,
+                       &disable_snapshot, &disable_ccache, &disable_tests))
                return NULL;
 
        int flags = 0;
@@ -1141,10 +766,62 @@ static PyObject* Pakfire_build(PakfireObject* self, PyObject* args, PyObject* kw
        if (disable_tests)
                flags |= PAKFIRE_BUILD_DISABLE_TESTS;
 
+       // Create a new build environment
+       r = pakfire_build_create(&build, self->pakfire, build_id, flags);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
+
+       // Set target
+       if (target) {
+               r = pakfire_build_set_target(build, target);
+               if (r) {
+                       PyErr_SetFromErrno(PyExc_OSError);
+                       goto ERROR;
+               }
+       }
+
+       // Set ccache path
+       if (ccache_path) {
+               r = pakfire_build_set_ccache_path(build, ccache_path);
+               if (r) {
+                       PyErr_SetFromErrno(PyExc_OSError);
+                       goto ERROR;
+               }
+       }
+
+       Py_BEGIN_ALLOW_THREADS
+
        // Run build
-       int r = pakfire_build(self->pakfire, path, target, build_id, flags);
+       r = pakfire_build_exec(build, path);
+       if (r) {
+               Py_BLOCK_THREADS;
 
-       return execute_return_value(r);
+               if (r < 0) {
+                       PyErr_SetFromErrno(PyExc_OSError);
+
+               // Raise a command execution error
+               } else {
+                       PyObject* code = PyLong_FromLong(r);
+
+                       PyErr_SetObject(PyExc_CommandExecutionError, code);
+                       Py_DECREF(code);
+               }
+
+               goto ERROR;
+       }
+
+       Py_END_ALLOW_THREADS
+
+ERROR:
+       if (build)
+               pakfire_build_unref(build);
+
+       if (r)
+               return NULL;
+
+       Py_RETURN_NONE;
 }
 
 static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kwargs) {
@@ -1155,6 +832,7 @@ static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kw
        };
        char** packages = NULL;
        int disable_snapshot = 0;
+       int r;
 
        // Parse everything
        if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&p", kwlist,
@@ -1166,103 +844,50 @@ static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kw
        if (disable_snapshot)
                flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
 
-       int r = pakfire_shell(self->pakfire, (const char**)packages, flags);
+       Py_BEGIN_ALLOW_THREADS
 
-       return execute_return_value(r);
-}
+       r = pakfire_shell(self->pakfire, (const char**)packages, flags);
 
-static PyObject* Pakfire_clean(PakfireObject* self) {
-       int r = pakfire_clean(self->pakfire, 0);
-       if (r) {
-               PyErr_SetFromErrno(PyExc_OSError);
-               return NULL;
-       }
+       Py_END_ALLOW_THREADS
 
-       Py_RETURN_NONE;
+       return execute_return_value(r);
 }
 
-static PyObject* Pakfire_refresh(PakfireObject* self, PyObject* args) {
-       int force = 0;
+static PyObject* Pakfire_clean(PakfireObject* self) {
+       int r;
 
-       if (!PyArg_ParseTuple(args, "|p", &force))
-               return NULL;
+       Py_BEGIN_ALLOW_THREADS
 
-       int r = pakfire_refresh(self->pakfire, force);
+       r = pakfire_clean(self->pakfire, 0);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
        }
 
-       Py_RETURN_NONE;
-}
-
-static PyObject* Pakfire_check(PakfireObject* self) {
-       struct pakfire_filelist* errors = NULL;
-       int r;
-
-       // Allocate a filelist for errors
-       r = pakfire_filelist_create(&errors, self->pakfire);
-       if (r)
-               goto ERROR;
-
-       // Perform check
-       r = pakfire_check(self->pakfire, errors);
-       if (r)
-               goto ERROR;
+       Py_END_ALLOW_THREADS
 
-       const size_t num_errors = pakfire_filelist_size(errors);
-
-       // Did we find any errors?
-       if (num_errors) {
-               PyObject* _errors = PyList_FromFileList(errors);
-               if (!_errors)
-                       goto ERROR;
-
-               pakfire_filelist_unref(errors);
-
-               // Raise CheckFileVerificationError
-               PyErr_SetObject(PyExc_CheckFileVerificationError, _errors);
-               Py_DECREF(_errors);
-               return NULL;
-       }
-
-       pakfire_filelist_unref(errors);
        Py_RETURN_NONE;
-
-ERROR:
-       // Cleanup
-       if (errors)
-               pakfire_filelist_unref(errors);
-
-       // Raise exception from errno
-       PyErr_SetFromErrno(PyExc_OSError);
-       return NULL;
 }
 
-static PyObject* Pakfire_sync(PakfireObject* self, PyObject* args, PyObject* kwargs) {
-       char* kwlist[] = {
-               "keep_orphaned",
-               "status_callback",
-               NULL,
-       };
-       int keep_orphaned = 0;
-       int flags = 0;
-       PyObject* status_callback = NULL;
+static PyObject* Pakfire_refresh(PakfireObject* self, PyObject* args) {
+       int force = 0;
+       int r;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$pO", kwlist,
-                       &keep_orphaned, &status_callback))
+       if (!PyArg_ParseTuple(args, "|p", &force))
                return NULL;
 
-       if (keep_orphaned)
-               flags |= PAKFIRE_REQUEST_KEEP_ORPHANED;
+       Py_BEGIN_ALLOW_THREADS
 
-       int r = pakfire_sync(self->pakfire, 0, flags, NULL,
-                       Pakfire_status_callback, status_callback);
+       r = pakfire_refresh(self->pakfire, force);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
        }
 
+       Py_END_ALLOW_THREADS
+
        Py_RETURN_NONE;
 }
 
@@ -1273,12 +898,17 @@ static PyObject* Pakfire_open(PakfireObject* self, PyObject* args) {
        if (!PyArg_ParseTuple(args, "s", &path))
                return NULL;
 
+       Py_BEGIN_ALLOW_THREADS
+
        int r = pakfire_archive_open(&archive, self->pakfire, path);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
        }
 
+       Py_END_ALLOW_THREADS
+
        // Create Python object
        PyObject* object = new_archive(&ArchiveType, archive);
        pakfire_archive_unref(archive);
@@ -1287,13 +917,14 @@ static PyObject* Pakfire_open(PakfireObject* self, PyObject* args) {
 }
 
 static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObject* kwargs) {
-       char* kwlist[] = { "path", "files", NULL };
+       char* kwlist[] = { "path", "files", "key", NULL };
        const char* path = NULL;
        PyObject* list = NULL;
+       KeyObject* key = NULL;
 
        PyObject* ret = NULL;
 
-       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO", kwlist, &path, &list))
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!", kwlist, &path, &list, &KeyType, &key))
                return NULL;
 
        // List must be a sequence
@@ -1302,8 +933,6 @@ static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObj
                return NULL;
        }
 
-       const int flags = 0;
-
        // How many new files do we have?
        ssize_t num_files = PySequence_Length(list);
        if (num_files < 0)
@@ -1335,12 +964,17 @@ static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObj
                Py_DECREF(file);
        }
 
-       int r = pakfire_repo_compose(self->pakfire, path, flags, files);
+       Py_BEGIN_ALLOW_THREADS
+
+       int r = pakfire_repo_compose(self->pakfire, path, (key) ? key->key : NULL, files);
        if (r) {
+               Py_BLOCK_THREADS
                PyErr_SetFromErrno(PyExc_OSError);
                return NULL;
        }
 
+       Py_END_ALLOW_THREADS
+
        // Return None on success
        ret = Py_None;
        Py_INCREF(ret);
@@ -1352,6 +986,59 @@ ERROR:
        return ret;
 }
 
+static PyObject* Pakfire_mkimage(PakfireObject* self, PyObject* args, PyObject* kwargs) {
+       char* kwlist[] = {
+               "type",
+               "path",
+               NULL,
+       };
+       struct pakfire_build* build = NULL;
+       const char* type = NULL;
+       PyObject* file = NULL;
+       FILE* f = NULL;
+       int r;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO", kwlist, &type, &file))
+               return NULL;
+
+       // Make a file handle out of file
+       f = PyObject_AsFileHandle(file, "w");
+       if (!f)
+               return NULL;
+
+       // Create a new build environment
+       r = pakfire_build_create(&build, self->pakfire, NULL, 0);
+       if (r) {
+               PyErr_SetFromErrno(PyExc_OSError);
+               goto ERROR;
+       }
+
+       Py_BEGIN_ALLOW_THREADS
+
+       // Run mkimage
+       r = pakfire_build_mkimage(build, type, f);
+       if (r) {
+               Py_BLOCK_THREADS;
+
+               if (r < 0)
+                       PyErr_SetFromErrno(PyExc_OSError);
+
+               goto ERROR;
+       }
+
+       Py_END_ALLOW_THREADS
+
+ERROR:
+       if (build)
+               pakfire_build_unref(build);
+       if (f)
+               fclose(f);
+       if (r)
+               return NULL;
+
+       Py_RETURN_NONE;
+}
+
 static struct PyMethodDef Pakfire_methods[] = {
        {
                "build",
@@ -1359,66 +1046,30 @@ static struct PyMethodDef Pakfire_methods[] = {
                METH_VARARGS|METH_KEYWORDS,
                NULL
        },
-       {
-               "check",
-               (PyCFunction)Pakfire_check,
-               METH_NOARGS,
-               NULL,
-       },
        {
                "clean",
                (PyCFunction)Pakfire_clean,
                METH_NOARGS,
                NULL,
        },
-       {
-               "copy_in",
-               (PyCFunction)Pakfire_copy_in,
-               METH_VARARGS,
-               NULL,
-       },
-       {
-               "copy_out",
-               (PyCFunction)Pakfire_copy_out,
-               METH_VARARGS,
-               NULL,
-       },
        {
                "dist",
                (PyCFunction)Pakfire_dist,
                METH_VARARGS,
                NULL
        },
-       {
-               "erase",
-               (PyCFunction)Pakfire_erase,
-               METH_VARARGS|METH_KEYWORDS,
-               NULL
-       },
        {
                "execute",
                (PyCFunction)Pakfire_execute,
                METH_VARARGS|METH_KEYWORDS,
                NULL
        },
-       {
-               "fetch_key",
-               (PyCFunction)Pakfire_fetch_key,
-               METH_VARARGS|METH_KEYWORDS,
-               NULL
-       },
        {
                "generate_key",
                (PyCFunction)Pakfire_generate_key,
                METH_VARARGS|METH_KEYWORDS,
                NULL
        },
-       {
-               "get_key",
-               (PyCFunction)Pakfire_get_key,
-               METH_VARARGS,
-               NULL
-       },
        {
                "get_repo",
                (PyCFunction)Pakfire_get_repo,
@@ -1429,13 +1080,13 @@ static struct PyMethodDef Pakfire_methods[] = {
                "import_key",
                (PyCFunction)Pakfire_import_key,
                METH_VARARGS,
-               NULL
+               NULL,
        },
        {
-               "install",
-               (PyCFunction)Pakfire_install,
+               "mkimage",
+               (PyCFunction)Pakfire_mkimage,
                METH_VARARGS|METH_KEYWORDS,
-               NULL,
+               NULL
        },
        {
                "open",
@@ -1467,18 +1118,6 @@ static struct PyMethodDef Pakfire_methods[] = {
                METH_VARARGS|METH_KEYWORDS,
                NULL,
        },
-       {
-               "sync",
-               (PyCFunction)Pakfire_sync,
-               METH_VARARGS|METH_KEYWORDS,
-               NULL,
-       },
-       {
-               "update",
-               (PyCFunction)Pakfire_update,
-               METH_VARARGS|METH_KEYWORDS,
-               NULL
-       },
        {
                "version_compare",
                (PyCFunction)Pakfire_version_compare,
@@ -1508,13 +1147,6 @@ static struct PyGetSetDef Pakfire_getsetters[] = {
                NULL,
                NULL
        },
-       {
-               "keys",
-               (getter)Pakfire_get_keys,
-               NULL,
-               NULL,
-               NULL
-       },
     {
                "path",
                (getter)Pakfire_get_path,