1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2017 Pakfire development team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
21 #define PY_SSIZE_T_CLEAN
26 #include <pakfire/archive.h>
27 #include <pakfire/build.h>
28 #include <pakfire/constants.h>
29 #include <pakfire/ctx.h>
30 #include <pakfire/dist.h>
31 #include <pakfire/jail.h>
32 #include <pakfire/logging.h>
33 #include <pakfire/mount.h>
34 #include <pakfire/packagelist.h>
35 #include <pakfire/pakfire.h>
36 #include <pakfire/key.h>
37 #include <pakfire/repo.h>
38 #include <pakfire/repolist.h>
39 #include <pakfire/transaction.h>
40 #include <pakfire/util.h>
49 extern struct pakfire_ctx
* pakfire_ctx
;
51 static PyObject
* Pakfire_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
52 PakfireObject
* self
= (PakfireObject
*)type
->tp_alloc(type
, 0);
56 return (PyObject
*)self
;
59 static int Pakfire_init(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
68 const char* path
= NULL
;
69 const char* arch
= NULL
;
70 PyObject
* conf
= Py_None
;
76 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "|zzpO", kwlist
,
77 &path
, &arch
, &offline
, &conf
))
80 // Map the configuration
81 if (conf
!= Py_None
) {
82 fconf
= PyObject_AsFileHandle(conf
, "r");
87 int flags
= PAKFIRE_FLAGS_DEBUG
;
89 // Enable offline mode
91 flags
|= PAKFIRE_FLAGS_OFFLINE
;
93 Py_BEGIN_ALLOW_THREADS
95 // Create a new Pakfire instance
96 r
= pakfire_create(&self
->pakfire
, pakfire_ctx
, path
, arch
, fconf
, flags
);
104 // Invalid architecture or path
106 PyErr_SetString(PyExc_ValueError
, "Invalid architecture or path");
111 PyErr_SetFromErrno(PyExc_OSError
);
125 static void Pakfire_dealloc(PakfireObject
* self
) {
127 Py_BEGIN_ALLOW_THREADS
129 pakfire_unref(self
->pakfire
);
134 Py_TYPE(self
)->tp_free((PyObject
*)self
);
137 static PyObject
* Pakfire_repr(PakfireObject
* self
) {
138 const char* path
= pakfire_get_path(self
->pakfire
);
139 const char* arch
= pakfire_get_arch(self
->pakfire
);
141 return PyUnicode_FromFormat("<_pakfire.Pakfire %s (%s)>", path
, arch
);
144 static PyObject
* Pakfire_get_path(PakfireObject
* self
) {
145 const char* path
= pakfire_get_path(self
->pakfire
);
147 return PyUnicode_FromString(path
);
150 static PyObject
* Pakfire_get_arch(PakfireObject
* self
) {
151 const char* arch
= pakfire_get_arch(self
->pakfire
);
153 return PyUnicode_FromString(arch
);
156 static PyObject
* Pakfire_get_repo(PakfireObject
* self
, PyObject
* args
) {
157 const char* name
= NULL
;
159 if (!PyArg_ParseTuple(args
, "s", &name
))
162 struct pakfire_repo
* repo
= pakfire_get_repo(self
->pakfire
, name
);
166 PyObject
* obj
= new_repo(&RepoType
, repo
);
167 pakfire_repo_unref(repo
);
172 static int convert_packages(PyObject
* object
, void* address
) {
173 char*** packages
= (char***)address
;
175 // Called for cleanup
179 // Nothing to do when object is None
180 if (object
== Py_None
)
181 return Py_CLEANUP_SUPPORTED
;
183 if (!PySequence_Check(object
)) {
184 PyErr_SetString(PyExc_ValueError
, "Packages must be a sequence");
188 const unsigned int length
= PySequence_Length(object
);
190 return Py_CLEANUP_SUPPORTED
;
193 *packages
= calloc(length
+ 1, sizeof(*packages
));
195 PyErr_SetFromErrno(PyExc_OSError
);
199 for (unsigned int i
= 0; i
< length
; i
++) {
200 PyObject
* item
= PySequence_GetItem(object
, i
);
202 // Check if input is a string
203 if (!PyUnicode_Check(item
)) {
206 PyErr_SetString(PyExc_AttributeError
, "Expected a string");
211 const char* package
= PyUnicode_AsUTF8(item
);
217 // Add package to array
218 (*packages
)[i
] = strdup(package
);
219 if (!(*packages
)[i
]) {
228 return Py_CLEANUP_SUPPORTED
;
232 for (char** package
= *packages
; *package
; package
++)
240 static PyObject
* Pakfire_generate_key(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
241 char* kwlist
[] = { "algorithm", "comment", NULL
};
242 struct pakfire_key
* key
= NULL
;
243 pakfire_key_algo_t algo
= PAKFIRE_KEY_ALGO_NULL
;
244 const char* comment
= NULL
;
246 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "is", kwlist
, &algo
, &comment
))
249 // Generate a new key
250 int r
= pakfire_key_generate(&key
, self
->pakfire
, algo
, comment
);
252 PyErr_SetFromErrno(PyExc_OSError
);
256 PyObject
* object
= new_key(&KeyType
, key
);
257 pakfire_key_unref(key
);
262 static PyObject
* Pakfire_import_key(PakfireObject
* self
, PyObject
* args
) {
263 struct pakfire_key
* key
= NULL
;
264 PyObject
* object
= NULL
;
266 Py_ssize_t data_length
= 0;
270 if (!PyArg_ParseTuple(args
, "s#", &data
, &data_length
))
274 FILE* f
= fmemopen(data
, data_length
, "r");
279 r
= pakfire_key_import(&key
, self
->pakfire
, f
);
281 PyErr_SetFromErrno(PyExc_OSError
);
285 // Convert the key into a Key object
286 object
= new_key(&KeyType
, key
);
292 pakfire_key_unref(key
);
299 static PyObject
* Pakfire_whatprovides(PakfireObject
* self
, PyObject
* args
) {
300 const char* provides
= NULL
;
301 struct pakfire_packagelist
* list
= NULL
;
302 PyObject
* ret
= NULL
;
305 if (!PyArg_ParseTuple(args
, "s", &provides
))
309 r
= pakfire_packagelist_create(&list
, self
->pakfire
);
311 PyErr_SetFromErrno(PyExc_OSError
);
315 r
= pakfire_whatprovides(self
->pakfire
, provides
, 0, list
);
317 PyErr_SetFromErrno(PyExc_OSError
);
321 // Create a Python list from the package list
322 ret
= PyList_FromPackageList(list
);
326 pakfire_packagelist_unref(list
);
331 static PyObject
* Pakfire_whatrequires(PakfireObject
* self
, PyObject
* args
) {
332 const char* requires
= NULL
;
333 struct pakfire_packagelist
* list
= NULL
;
334 PyObject
* ret
= NULL
;
337 if (!PyArg_ParseTuple(args
, "s", &requires
))
340 Py_BEGIN_ALLOW_THREADS
343 r
= pakfire_packagelist_create(&list
, self
->pakfire
);
346 PyErr_SetFromErrno(PyExc_OSError
);
350 r
= pakfire_whatrequires(self
->pakfire
, requires
, 0, list
);
353 PyErr_SetFromErrno(PyExc_OSError
);
359 // Create a Python list from the package list
360 ret
= PyList_FromPackageList(list
);
364 pakfire_packagelist_unref(list
);
369 static PyObject
* Pakfire_search(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
370 char* kwlist
[] = { "pattern", "name_only", NULL
};
371 struct pakfire_packagelist
* list
= NULL
;
372 const char* pattern
= NULL
;
375 PyObject
* ret
= NULL
;
378 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|$p", kwlist
, &pattern
, &name_only
))
381 // Search for package names only
383 flags
|= PAKFIRE_SEARCH_NAME_ONLY
;
385 r
= pakfire_packagelist_create(&list
, self
->pakfire
);
387 PyErr_SetFromErrno(PyExc_OSError
);
391 r
= pakfire_search(self
->pakfire
, pattern
, flags
, list
);
393 PyErr_SetFromErrno(PyExc_OSError
);
397 ret
= PyList_FromPackageList(list
);
401 pakfire_packagelist_unref(list
);
406 static PyObject
* Pakfire_version_compare(PakfireObject
* self
, PyObject
* args
) {
407 const char* evr1
= NULL
;
408 const char* evr2
= NULL
;
410 if (!PyArg_ParseTuple(args
, "ss", &evr1
, &evr2
))
413 int cmp
= pakfire_version_compare(self
->pakfire
, evr1
, evr2
);
415 return PyLong_FromLong(cmp
);
418 static int Pakfire_execute_output_callback(struct pakfire
* pakfire
, void* data
,
419 int priority
, const char* line
, size_t length
) {
420 PyObject
* callback
= (PyObject
*)data
;
423 // Do nothing if callback isn't set
427 // Translate priority to Python logging priorities
438 // Remove the trailing newline
439 if (line
&& line
[length
- 1] == '\n')
442 // Create tuple with arguments for the callback function
443 PyObject
* args
= Py_BuildValue("(is#)", priority
, line
, (Py_ssize_t
)length
);
447 PyObject
* result
= PyObject_CallObject(callback
, args
);
448 if (result
&& PyLong_Check(result
)) {
449 r
= PyLong_AsLong(result
);
458 static PyObject
* Pakfire_execute(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
468 struct pakfire_jail
* jail
= NULL
;
469 const char** argv
= NULL
;
471 PyObject
* ret
= NULL
;
473 PyObject
* command
= NULL
;
474 PyObject
* environ
= NULL
;
475 PyObject
* bind
= NULL
;
476 PyObject
* callback
= NULL
;
479 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "O|OOOi", kwlist
, &command
, &environ
,
480 &bind
, &callback
, &nice
))
483 // Check if command is a list
484 if (!PyList_Check(command
)) {
485 PyErr_SetString(PyExc_TypeError
, "command must be a list");
489 const ssize_t command_length
= PyList_Size(command
);
491 // Check if command is not empty
492 if (command_length
== 0) {
493 PyErr_SetString(PyExc_ValueError
, "command is empty");
498 argv
= calloc(command_length
+ 1, sizeof(*argv
));
502 // All arguments in command must be strings
503 for (unsigned int i
= 0; i
< command_length
; i
++) {
504 PyObject
* item
= PyList_GET_ITEM(command
, i
);
506 if (!PyUnicode_Check(item
)) {
507 PyErr_Format(PyExc_TypeError
, "Item %u in command is not a string", i
);
512 argv
[i
] = PyUnicode_AsUTF8(item
);
515 // Check if bind is a sequence
516 if (bind
&& !PySequence_Check(bind
)) {
517 PyErr_SetString(PyExc_ValueError
, "bind is not a sequence");
522 if (callback
&& !PyCallable_Check(callback
)) {
523 PyErr_SetString(PyExc_TypeError
, "callback must be callable\n");
528 r
= pakfire_jail_create(&jail
, self
->pakfire
);
530 PyErr_SetFromErrno(PyExc_OSError
);
535 if (callback
&& !PyCallable_Check(callback
)) {
536 PyErr_SetString(PyExc_TypeError
, "callback must be callable\n");
542 r
= pakfire_jail_nice(jail
, nice
);
544 PyErr_SetFromErrno(PyExc_OSError
);
549 PyObject
* key
= NULL
;
550 PyObject
* value
= NULL
;
553 // Parse the environment
555 // Check if environ is a dictionary
556 if (!PyDict_Check(environ
)) {
557 PyErr_SetString(PyExc_TypeError
, "environ must be a dictionary");
561 // All keys and values must be strings
562 while (PyDict_Next(environ
, &p
, &key
, &value
)) {
563 if (!PyUnicode_Check(key
) || !PyUnicode_Check(value
)) {
564 PyErr_SetString(PyExc_TypeError
, "Environment contains a non-string object");
568 // Set environment value
569 r
= pakfire_jail_set_env(jail
, PyUnicode_AsUTF8(key
), PyUnicode_AsUTF8(value
));
571 PyErr_SetFromErrno(PyExc_OSError
);
577 const Py_ssize_t num_bind
= PySequence_Length(bind
);
580 for (unsigned int i
= 0; i
< num_bind
; i
++) {
581 PyObject
* b
= PySequence_ITEM(bind
, i
);
585 // Check if this is a Unicode object
586 if (!PyUnicode_Check(b
)) {
587 PyErr_SetString(PyExc_ValueError
, "bind contains a non-Unicode object");
592 const char* path
= PyUnicode_AsUTF8(b
);
595 r
= pakfire_jail_bind(jail
, path
, path
, 0);
597 PyErr_SetFromErrno(PyExc_OSError
);
605 Py_BEGIN_ALLOW_THREADS
608 r
= pakfire_jail_exec(jail
, argv
,
609 NULL
, Pakfire_execute_output_callback
, callback
, 0);
613 // If the return code was negative, we had some internal error
615 PyErr_SetFromErrno(PyExc_OSError
);
618 // Otherwise the executed command returned some error code
620 PyObject
* code
= PyLong_FromLong(r
);
622 // Raise CommandExecutionError
623 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
629 // The process has exited successfully
639 pakfire_jail_unref(jail
);
644 static PyObject
* Pakfire_dist(PakfireObject
* self
, PyObject
* args
) {
645 const char* path
= NULL
;
646 const char* target
= NULL
;
650 if (!PyArg_ParseTuple(args
, "s|z", &path
, &target
))
653 Py_BEGIN_ALLOW_THREADS
655 r
= pakfire_dist(self
->pakfire
, path
, target
, &result
);
658 PyErr_SetFromErrno(PyExc_OSError
);
664 PyObject
* ret
= PyUnicode_FromString(result
);
670 static PyObject
* Pakfire_get_repos(PakfireObject
* self
) {
671 struct pakfire_repolist
* repos
= pakfire_get_repos(self
->pakfire
);
673 PyErr_SetFromErrno(PyExc_OSError
);
677 const size_t l
= pakfire_repolist_size(repos
);
679 PyObject
* list
= PyList_New(l
);
683 for (unsigned int i
= 0; i
< l
; i
++) {
684 struct pakfire_repo
* repo
= pakfire_repolist_get(repos
, i
);
688 PyObject
* obj
= new_repo(&RepoType
, repo
);
689 PyList_SET_ITEM(list
, i
, obj
);
691 pakfire_repo_unref(repo
);
695 pakfire_repolist_unref(repos
);
700 static PyObject
* execute_return_value(int r
) {
701 // Raise an OS error if r < 0
705 PyErr_SetFromErrno(PyExc_OSError
);
708 // Raise exception when the command failed
710 PyObject
* code
= PyLong_FromLong(r
);
712 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
721 static PyObject
* Pakfire_build(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
733 struct pakfire_build
* build
= NULL
;
734 const char* path
= NULL
;
735 const char* target
= NULL
;
736 const char* build_id
= NULL
;
737 const char* ccache_path
= NULL
;
739 int disable_snapshot
= 0;
740 int disable_ccache
= 0;
741 int disable_tests
= 0;
744 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "s|zzzpppp", kwlist
,
745 &path
, &target
, &build_id
, &ccache_path
, &interactive
,
746 &disable_snapshot
, &disable_ccache
, &disable_tests
))
752 flags
|= PAKFIRE_BUILD_INTERACTIVE
;
754 // Disable snapshot if requested
755 if (disable_snapshot
)
756 flags
|= PAKFIRE_BUILD_DISABLE_SNAPSHOT
;
758 // Disable ccache if requested
760 flags
|= PAKFIRE_BUILD_DISABLE_CCACHE
;
762 // Disable tests if requested
764 flags
|= PAKFIRE_BUILD_DISABLE_TESTS
;
766 // Create a new build environment
767 r
= pakfire_build_create(&build
, self
->pakfire
, build_id
, flags
);
769 PyErr_SetFromErrno(PyExc_OSError
);
775 r
= pakfire_build_set_target(build
, target
);
777 PyErr_SetFromErrno(PyExc_OSError
);
784 r
= pakfire_build_set_ccache_path(build
, ccache_path
);
786 PyErr_SetFromErrno(PyExc_OSError
);
791 Py_BEGIN_ALLOW_THREADS
794 r
= pakfire_build_exec(build
, path
);
799 PyErr_SetFromErrno(PyExc_OSError
);
801 // Raise a command execution error
803 PyObject
* code
= PyLong_FromLong(r
);
805 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
816 pakfire_build_unref(build
);
824 static PyObject
* Pakfire_shell(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
830 char** packages
= NULL
;
831 int disable_snapshot
= 0;
835 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|O&p", kwlist
,
836 convert_packages
, &packages
, &disable_snapshot
))
841 if (disable_snapshot
)
842 flags
|= PAKFIRE_BUILD_DISABLE_SNAPSHOT
;
844 Py_BEGIN_ALLOW_THREADS
846 r
= pakfire_shell(self
->pakfire
, (const char**)packages
, flags
);
850 return execute_return_value(r
);
853 static PyObject
* Pakfire_clean(PakfireObject
* self
) {
856 Py_BEGIN_ALLOW_THREADS
858 r
= pakfire_clean(self
->pakfire
, 0);
861 PyErr_SetFromErrno(PyExc_OSError
);
870 static PyObject
* Pakfire_refresh(PakfireObject
* self
, PyObject
* args
) {
874 if (!PyArg_ParseTuple(args
, "|p", &force
))
877 Py_BEGIN_ALLOW_THREADS
879 r
= pakfire_refresh(self
->pakfire
, force
);
882 PyErr_SetFromErrno(PyExc_OSError
);
891 static PyObject
* Pakfire_open(PakfireObject
* self
, PyObject
* args
) {
892 struct pakfire_archive
* archive
= NULL
;
893 const char* path
= NULL
;
895 if (!PyArg_ParseTuple(args
, "s", &path
))
898 Py_BEGIN_ALLOW_THREADS
900 int r
= pakfire_archive_open(&archive
, self
->pakfire
, path
);
903 PyErr_SetFromErrno(PyExc_OSError
);
909 // Create Python object
910 PyObject
* object
= new_archive(&ArchiveType
, archive
);
911 pakfire_archive_unref(archive
);
916 static PyObject
* Pakfire_repo_compose(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
917 char* kwlist
[] = { "path", "files", "key", NULL
};
918 const char* path
= NULL
;
919 PyObject
* list
= NULL
;
920 KeyObject
* key
= NULL
;
922 PyObject
* ret
= NULL
;
924 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "sO|O!", kwlist
, &path
, &list
, &KeyType
, &key
))
927 // List must be a sequence
928 if (!PySequence_Check(list
)) {
929 PyErr_SetString(PyExc_ValueError
, "Expected a sequence.");
933 // How many new files do we have?
934 ssize_t num_files
= PySequence_Length(list
);
938 // Allocate files array
939 const char** files
= calloc(num_files
+ 1, sizeof(*files
));
941 PyErr_SetFromErrno(PyExc_OSError
);
945 for (int i
= 0; i
< num_files
; i
++) {
946 PyObject
* file
= PySequence_GetItem(list
, i
);
950 // Check if file is a Unicode object
951 if (!PyUnicode_Check(file
)) {
952 PyErr_SetString(PyExc_ValueError
, "Expected a string.");
956 // Add pointer to string to files array
957 files
[i
] = PyUnicode_AsUTF8(file
);
964 Py_BEGIN_ALLOW_THREADS
966 int r
= pakfire_repo_compose(self
->pakfire
, path
, (key
) ? key
->key
: NULL
, files
);
969 PyErr_SetFromErrno(PyExc_OSError
);
975 // Return None on success
986 static PyObject
* Pakfire_mkimage(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
992 struct pakfire_build
* build
= NULL
;
993 const char* type
= NULL
;
994 PyObject
* file
= NULL
;
998 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "sO", kwlist
, &type
, &file
))
1001 // Make a file handle out of file
1002 f
= PyObject_AsFileHandle(file
, "w");
1006 // Create a new build environment
1007 r
= pakfire_build_create(&build
, self
->pakfire
, NULL
, 0);
1009 PyErr_SetFromErrno(PyExc_OSError
);
1013 Py_BEGIN_ALLOW_THREADS
1016 r
= pakfire_build_mkimage(build
, type
, f
);
1021 PyErr_SetFromErrno(PyExc_OSError
);
1026 Py_END_ALLOW_THREADS
1030 pakfire_build_unref(build
);
1039 static struct PyMethodDef Pakfire_methods
[] = {
1042 (PyCFunction
)Pakfire_build
,
1043 METH_VARARGS
|METH_KEYWORDS
,
1048 (PyCFunction
)Pakfire_clean
,
1054 (PyCFunction
)Pakfire_dist
,
1060 (PyCFunction
)Pakfire_execute
,
1061 METH_VARARGS
|METH_KEYWORDS
,
1066 (PyCFunction
)Pakfire_generate_key
,
1067 METH_VARARGS
|METH_KEYWORDS
,
1072 (PyCFunction
)Pakfire_get_repo
,
1078 (PyCFunction
)Pakfire_import_key
,
1084 (PyCFunction
)Pakfire_mkimage
,
1085 METH_VARARGS
|METH_KEYWORDS
,
1090 (PyCFunction
)Pakfire_open
,
1096 (PyCFunction
)Pakfire_refresh
,
1102 (PyCFunction
)Pakfire_repo_compose
,
1103 METH_VARARGS
|METH_KEYWORDS
,
1108 (PyCFunction
)Pakfire_search
,
1109 METH_VARARGS
|METH_KEYWORDS
,
1114 (PyCFunction
)Pakfire_shell
,
1115 METH_VARARGS
|METH_KEYWORDS
,
1120 (PyCFunction
)Pakfire_version_compare
,
1126 (PyCFunction
)Pakfire_whatprovides
,
1132 (PyCFunction
)Pakfire_whatrequires
,
1139 static struct PyGetSetDef Pakfire_getsetters
[] = {
1142 (getter
)Pakfire_get_arch
,
1149 (getter
)Pakfire_get_path
,
1156 (getter
)Pakfire_get_repos
,
1164 PyTypeObject PakfireType
= {
1165 PyVarObject_HEAD_INIT(NULL
, 0)
1166 tp_name
: "_pakfire.Pakfire",
1167 tp_basicsize
: sizeof(PakfireObject
),
1168 tp_flags
: Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
,
1169 tp_new
: Pakfire_new
,
1170 tp_dealloc
: (destructor
)Pakfire_dealloc
,
1171 tp_init
: (initproc
)Pakfire_init
,
1172 tp_doc
: "Pakfire object",
1173 tp_methods
: Pakfire_methods
,
1174 tp_getset
: Pakfire_getsetters
,
1175 tp_repr
: (reprfunc
)Pakfire_repr
,