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>
50 static PyObject
* Pakfire_new(PyTypeObject
* type
, PyObject
* args
, PyObject
* kwds
) {
51 PakfireObject
* self
= (PakfireObject
*)type
->tp_alloc(type
, 0);
57 return (PyObject
*)self
;
60 static int Pakfire_init(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
68 CtxObject
* ctx
= NULL
;
69 const char* path
= NULL
;
70 const char* arch
= NULL
;
71 PyObject
* conf
= Py_None
;
76 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "O!|zzO", kwlist
,
77 &CtxType
, &ctx
, &path
, &arch
, &conf
))
80 // Map the configuration
81 if (conf
!= Py_None
) {
82 fconf
= PyObject_AsFileHandle(conf
, "r");
89 Py_BEGIN_ALLOW_THREADS
91 // Store a reference to the context
92 self
->ctx
= pakfire_ctx_ref(ctx
->ctx
);
94 // Create a new Pakfire instance
95 r
= pakfire_create(&self
->pakfire
, self
->ctx
, path
, arch
, fconf
, flags
);
103 // Invalid architecture or path
105 PyErr_SetString(PyExc_ValueError
, "Invalid architecture or path");
110 PyErr_SetFromErrno(PyExc_OSError
);
124 static void Pakfire_dealloc(PakfireObject
* self
) {
126 Py_BEGIN_ALLOW_THREADS
128 pakfire_unref(self
->pakfire
);
133 pakfire_ctx_unref(self
->ctx
);
135 Py_TYPE(self
)->tp_free((PyObject
*)self
);
138 static PyObject
* Pakfire_repr(PakfireObject
* self
) {
139 const char* path
= pakfire_get_path(self
->pakfire
);
140 const char* arch
= pakfire_get_arch(self
->pakfire
);
142 return PyUnicode_FromFormat("<_pakfire.Pakfire %s (%s)>", path
, arch
);
145 static PyObject
* Pakfire_get_path(PakfireObject
* self
) {
146 const char* path
= pakfire_get_path(self
->pakfire
);
148 return PyUnicode_FromString(path
);
151 static PyObject
* Pakfire_get_arch(PakfireObject
* self
) {
152 const char* arch
= pakfire_get_arch(self
->pakfire
);
154 return PyUnicode_FromString(arch
);
157 static PyObject
* Pakfire_get_repo(PakfireObject
* self
, PyObject
* args
) {
158 const char* name
= NULL
;
160 if (!PyArg_ParseTuple(args
, "s", &name
))
163 struct pakfire_repo
* repo
= pakfire_get_repo(self
->pakfire
, name
);
167 PyObject
* obj
= new_repo(&RepoType
, repo
);
168 pakfire_repo_unref(repo
);
173 static int convert_packages(PyObject
* object
, void* address
) {
174 char*** packages
= (char***)address
;
176 // Called for cleanup
180 // Nothing to do when object is None
181 if (object
== Py_None
)
182 return Py_CLEANUP_SUPPORTED
;
184 if (!PySequence_Check(object
)) {
185 PyErr_SetString(PyExc_ValueError
, "Packages must be a sequence");
189 const unsigned int length
= PySequence_Length(object
);
191 return Py_CLEANUP_SUPPORTED
;
194 *packages
= calloc(length
+ 1, sizeof(*packages
));
196 PyErr_SetFromErrno(PyExc_OSError
);
200 for (unsigned int i
= 0; i
< length
; i
++) {
201 PyObject
* item
= PySequence_GetItem(object
, i
);
203 // Check if input is a string
204 if (!PyUnicode_Check(item
)) {
207 PyErr_SetString(PyExc_AttributeError
, "Expected a string");
212 const char* package
= PyUnicode_AsUTF8(item
);
218 // Add package to array
219 (*packages
)[i
] = strdup(package
);
220 if (!(*packages
)[i
]) {
229 return Py_CLEANUP_SUPPORTED
;
233 for (char** package
= *packages
; *package
; package
++)
242 XXX This could be moved out of here as this no longer depends on Pakfire
244 static PyObject
* Pakfire_generate_key(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
245 char* kwlist
[] = { "algorithm", "comment", NULL
};
246 struct pakfire_key
* key
= NULL
;
247 pakfire_key_algo_t algo
= PAKFIRE_KEY_ALGO_NULL
;
248 const char* comment
= NULL
;
250 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "is", kwlist
, &algo
, &comment
))
253 // Generate a new key
254 int r
= pakfire_key_generate(&key
, self
->ctx
, algo
, comment
);
256 PyErr_SetFromErrno(PyExc_OSError
);
260 PyObject
* object
= new_key(&KeyType
, key
);
261 pakfire_key_unref(key
);
266 XXX This could be moved out of here as this no longer depends on Pakfire
268 static PyObject
* Pakfire_import_key(PakfireObject
* self
, PyObject
* args
) {
269 struct pakfire_key
* key
= NULL
;
270 PyObject
* object
= NULL
;
272 Py_ssize_t data_length
= 0;
276 if (!PyArg_ParseTuple(args
, "s#", &data
, &data_length
))
280 FILE* f
= fmemopen(data
, data_length
, "r");
285 r
= pakfire_key_import(&key
, self
->ctx
, f
);
287 PyErr_SetFromErrno(PyExc_OSError
);
291 // Convert the key into a Key object
292 object
= new_key(&KeyType
, key
);
298 pakfire_key_unref(key
);
305 static PyObject
* Pakfire_whatprovides(PakfireObject
* self
, PyObject
* args
) {
306 const char* provides
= NULL
;
307 struct pakfire_packagelist
* list
= NULL
;
308 PyObject
* ret
= NULL
;
311 if (!PyArg_ParseTuple(args
, "s", &provides
))
315 r
= pakfire_packagelist_create(&list
, self
->ctx
);
317 PyErr_SetFromErrno(PyExc_OSError
);
321 r
= pakfire_whatprovides(self
->pakfire
, provides
, 0, list
);
323 PyErr_SetFromErrno(PyExc_OSError
);
327 // Create a Python list from the package list
328 ret
= PyList_FromPackageList(list
);
332 pakfire_packagelist_unref(list
);
337 static PyObject
* Pakfire_whatrequires(PakfireObject
* self
, PyObject
* args
) {
338 const char* requires
= NULL
;
339 struct pakfire_packagelist
* list
= NULL
;
340 PyObject
* ret
= NULL
;
343 if (!PyArg_ParseTuple(args
, "s", &requires
))
346 Py_BEGIN_ALLOW_THREADS
349 r
= pakfire_packagelist_create(&list
, self
->ctx
);
352 PyErr_SetFromErrno(PyExc_OSError
);
356 r
= pakfire_whatrequires(self
->pakfire
, requires
, 0, list
);
359 PyErr_SetFromErrno(PyExc_OSError
);
365 // Create a Python list from the package list
366 ret
= PyList_FromPackageList(list
);
370 pakfire_packagelist_unref(list
);
375 static PyObject
* Pakfire_search(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
376 char* kwlist
[] = { "pattern", "name_only", NULL
};
377 struct pakfire_packagelist
* list
= NULL
;
378 const char* pattern
= NULL
;
381 PyObject
* ret
= NULL
;
384 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "s|$p", kwlist
, &pattern
, &name_only
))
387 // Search for package names only
389 flags
|= PAKFIRE_SEARCH_NAME_ONLY
;
391 r
= pakfire_packagelist_create(&list
, self
->ctx
);
393 PyErr_SetFromErrno(PyExc_OSError
);
397 r
= pakfire_search(self
->pakfire
, pattern
, flags
, list
);
399 PyErr_SetFromErrno(PyExc_OSError
);
403 ret
= PyList_FromPackageList(list
);
407 pakfire_packagelist_unref(list
);
412 static PyObject
* Pakfire_version_compare(PakfireObject
* self
, PyObject
* args
) {
413 const char* evr1
= NULL
;
414 const char* evr2
= NULL
;
416 if (!PyArg_ParseTuple(args
, "ss", &evr1
, &evr2
))
419 int cmp
= pakfire_version_compare(self
->pakfire
, evr1
, evr2
);
421 return PyLong_FromLong(cmp
);
424 static int Pakfire_execute_output_callback(struct pakfire_ctx
* ctx
, struct pakfire_jail
* jail
,
425 void* data
, const char* line
, size_t length
) {
426 PyObject
* callback
= (PyObject
*)data
;
429 // Do nothing if callback isn't set
433 // Remove the trailing newline
434 if (line
&& line
[length
- 1] == '\n')
437 // Create tuple with arguments for the callback function
438 PyObject
* args
= Py_BuildValue("(is#)", LOG_INFO
, line
, (Py_ssize_t
)length
);
442 PyObject
* result
= PyObject_CallObject(callback
, args
);
443 if (result
&& PyLong_Check(result
)) {
444 r
= PyLong_AsLong(result
);
453 static PyObject
* Pakfire_execute(PakfireObject
* self
, PyObject
* args
, PyObject
* kwds
) {
463 struct pakfire_jail
* jail
= NULL
;
464 const char** argv
= NULL
;
466 PyObject
* ret
= NULL
;
468 PyObject
* command
= NULL
;
469 PyObject
* environ
= NULL
;
470 PyObject
* bind
= NULL
;
471 PyObject
* callback
= NULL
;
474 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "O|OOOi", kwlist
, &command
, &environ
,
475 &bind
, &callback
, &nice
))
478 // Check if command is a list
479 if (!PyList_Check(command
)) {
480 PyErr_SetString(PyExc_TypeError
, "command must be a list");
484 const ssize_t command_length
= PyList_Size(command
);
486 // Check if command is not empty
487 if (command_length
== 0) {
488 PyErr_SetString(PyExc_ValueError
, "command is empty");
493 argv
= calloc(command_length
+ 1, sizeof(*argv
));
497 // All arguments in command must be strings
498 for (unsigned int i
= 0; i
< command_length
; i
++) {
499 PyObject
* item
= PyList_GET_ITEM(command
, i
);
501 if (!PyUnicode_Check(item
)) {
502 PyErr_Format(PyExc_TypeError
, "Item %u in command is not a string", i
);
507 argv
[i
] = PyUnicode_AsUTF8(item
);
510 // Check if bind is a sequence
511 if (bind
&& !PySequence_Check(bind
)) {
512 PyErr_SetString(PyExc_ValueError
, "bind is not a sequence");
517 if (callback
&& !PyCallable_Check(callback
)) {
518 PyErr_SetString(PyExc_TypeError
, "callback must be callable\n");
523 r
= pakfire_jail_create(&jail
, self
->pakfire
);
525 PyErr_SetFromErrno(PyExc_OSError
);
531 r
= pakfire_jail_nice(jail
, nice
);
533 PyErr_SetFromErrno(PyExc_OSError
);
538 PyObject
* key
= NULL
;
539 PyObject
* value
= NULL
;
542 // Parse the environment
544 // Check if environ is a dictionary
545 if (!PyDict_Check(environ
)) {
546 PyErr_SetString(PyExc_TypeError
, "environ must be a dictionary");
550 // All keys and values must be strings
551 while (PyDict_Next(environ
, &p
, &key
, &value
)) {
552 if (!PyUnicode_Check(key
) || !PyUnicode_Check(value
)) {
553 PyErr_SetString(PyExc_TypeError
, "Environment contains a non-string object");
557 // Set environment value
558 r
= pakfire_jail_set_env(jail
, PyUnicode_AsUTF8(key
), PyUnicode_AsUTF8(value
));
560 PyErr_SetFromErrno(PyExc_OSError
);
567 if (bind
&& PySequence_Check(bind
)) {
568 const Py_ssize_t num_bind
= PySequence_Length(bind
);
570 for (unsigned int i
= 0; i
< num_bind
; i
++) {
571 PyObject
* b
= PySequence_ITEM(bind
, i
);
575 // Check if this is a Unicode object
576 if (!PyUnicode_Check(b
)) {
577 PyErr_SetString(PyExc_ValueError
, "bind contains a non-Unicode object");
582 const char* path
= PyUnicode_AsUTF8(b
);
585 r
= pakfire_jail_bind(jail
, path
, path
, 0);
587 PyErr_SetFromErrno(PyExc_OSError
);
596 Py_BEGIN_ALLOW_THREADS
599 pakfire_jail_set_stdout_callback(jail
, Pakfire_execute_output_callback
, callback
);
602 r
= pakfire_jail_exec(jail
, argv
, 0);
606 // If the return code was negative, we had some internal error
608 PyErr_SetFromErrno(PyExc_OSError
);
611 // Otherwise the executed command returned some error code
613 PyObject
* code
= PyLong_FromLong(r
);
615 // Raise CommandExecutionError
616 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
622 // The process has exited successfully
632 pakfire_jail_unref(jail
);
637 static PyObject
* Pakfire_dist(PakfireObject
* self
, PyObject
* args
) {
638 const char* path
= NULL
;
639 const char* target
= NULL
;
643 if (!PyArg_ParseTuple(args
, "s|z", &path
, &target
))
646 Py_BEGIN_ALLOW_THREADS
648 r
= pakfire_dist(self
->pakfire
, path
, target
, &result
);
651 PyErr_SetFromErrno(PyExc_OSError
);
657 PyObject
* ret
= PyUnicode_FromString(result
);
663 static PyObject
* Pakfire_get_repos(PakfireObject
* self
) {
664 struct pakfire_repolist
* repos
= pakfire_get_repos(self
->pakfire
);
666 PyErr_SetFromErrno(PyExc_OSError
);
670 const size_t l
= pakfire_repolist_size(repos
);
672 PyObject
* list
= PyList_New(l
);
676 for (unsigned int i
= 0; i
< l
; i
++) {
677 struct pakfire_repo
* repo
= pakfire_repolist_get(repos
, i
);
681 PyObject
* obj
= new_repo(&RepoType
, repo
);
682 PyList_SET_ITEM(list
, i
, obj
);
684 pakfire_repo_unref(repo
);
688 pakfire_repolist_unref(repos
);
693 static PyObject
* execute_return_value(int r
) {
694 // Raise an OS error if r < 0
698 PyErr_SetFromErrno(PyExc_OSError
);
701 // Raise exception when the command failed
703 PyObject
* code
= PyLong_FromLong(r
);
705 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
714 static PyObject
* Pakfire_build(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
726 struct pakfire_build
* build
= NULL
;
727 const char* path
= NULL
;
728 const char* target
= NULL
;
729 const char* build_id
= NULL
;
730 const char* ccache_path
= NULL
;
732 int disable_snapshot
= 0;
733 int disable_ccache
= 0;
734 int disable_tests
= 0;
737 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "s|zzzpppp", kwlist
,
738 &path
, &target
, &build_id
, &ccache_path
, &interactive
,
739 &disable_snapshot
, &disable_ccache
, &disable_tests
))
745 flags
|= PAKFIRE_BUILD_INTERACTIVE
;
747 // Disable snapshot if requested
748 if (disable_snapshot
)
749 flags
|= PAKFIRE_BUILD_DISABLE_SNAPSHOT
;
751 // Disable ccache if requested
753 flags
|= PAKFIRE_BUILD_DISABLE_CCACHE
;
755 // Disable tests if requested
757 flags
|= PAKFIRE_BUILD_DISABLE_TESTS
;
759 // Create a new build environment
760 r
= pakfire_build_create(&build
, self
->pakfire
, build_id
, flags
);
762 PyErr_SetFromErrno(PyExc_OSError
);
768 r
= pakfire_build_set_target(build
, target
);
770 PyErr_SetFromErrno(PyExc_OSError
);
777 r
= pakfire_build_set_ccache_path(build
, ccache_path
);
779 PyErr_SetFromErrno(PyExc_OSError
);
784 Py_BEGIN_ALLOW_THREADS
787 r
= pakfire_build_exec(build
, path
);
792 PyErr_SetFromErrno(PyExc_OSError
);
794 // Raise a command execution error
796 PyObject
* code
= PyLong_FromLong(r
);
798 PyErr_SetObject(PyExc_CommandExecutionError
, code
);
809 pakfire_build_unref(build
);
817 static PyObject
* Pakfire_shell(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
823 char** packages
= NULL
;
824 int disable_snapshot
= 0;
828 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|O&p", kwlist
,
829 convert_packages
, &packages
, &disable_snapshot
))
834 if (disable_snapshot
)
835 flags
|= PAKFIRE_BUILD_DISABLE_SNAPSHOT
;
837 Py_BEGIN_ALLOW_THREADS
839 r
= pakfire_shell(self
->pakfire
, (const char**)packages
, flags
);
843 return execute_return_value(r
);
846 static PyObject
* Pakfire_clean(PakfireObject
* self
) {
849 Py_BEGIN_ALLOW_THREADS
851 r
= pakfire_clean(self
->pakfire
, 0);
854 PyErr_SetFromErrno(PyExc_OSError
);
863 static PyObject
* Pakfire_refresh(PakfireObject
* self
, PyObject
* args
) {
867 if (!PyArg_ParseTuple(args
, "|p", &force
))
870 Py_BEGIN_ALLOW_THREADS
872 r
= pakfire_refresh(self
->pakfire
, force
);
875 PyErr_SetFromErrno(PyExc_OSError
);
884 static PyObject
* Pakfire_open(PakfireObject
* self
, PyObject
* args
) {
885 struct pakfire_archive
* archive
= NULL
;
886 const char* path
= NULL
;
888 if (!PyArg_ParseTuple(args
, "s", &path
))
891 Py_BEGIN_ALLOW_THREADS
893 int r
= pakfire_archive_open(&archive
, self
->pakfire
, path
);
896 PyErr_SetFromErrno(PyExc_OSError
);
902 // Create Python object
903 PyObject
* object
= new_archive(&ArchiveType
, archive
);
904 pakfire_archive_unref(archive
);
909 static PyObject
* Pakfire_repo_compose(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
910 char* kwlist
[] = { "path", "files", "key", NULL
};
911 const char* path
= NULL
;
912 PyObject
* list
= NULL
;
913 KeyObject
* key
= NULL
;
915 PyObject
* ret
= NULL
;
917 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "sO|O!", kwlist
, &path
, &list
, &KeyType
, &key
))
920 // List must be a sequence
921 if (!PySequence_Check(list
)) {
922 PyErr_SetString(PyExc_ValueError
, "Expected a sequence.");
926 // How many new files do we have?
927 ssize_t num_files
= PySequence_Length(list
);
931 // Allocate files array
932 const char** files
= calloc(num_files
+ 1, sizeof(*files
));
934 PyErr_SetFromErrno(PyExc_OSError
);
938 for (int i
= 0; i
< num_files
; i
++) {
939 PyObject
* file
= PySequence_GetItem(list
, i
);
943 // Check if file is a Unicode object
944 if (!PyUnicode_Check(file
)) {
945 PyErr_SetString(PyExc_ValueError
, "Expected a string.");
949 // Add pointer to string to files array
950 files
[i
] = PyUnicode_AsUTF8(file
);
957 Py_BEGIN_ALLOW_THREADS
959 int r
= pakfire_repo_compose(self
->pakfire
, path
, (key
) ? key
->key
: NULL
, files
);
962 PyErr_SetFromErrno(PyExc_OSError
);
968 // Return None on success
979 static PyObject
* Pakfire_mkimage(PakfireObject
* self
, PyObject
* args
, PyObject
* kwargs
) {
985 struct pakfire_build
* build
= NULL
;
986 const char* type
= NULL
;
987 PyObject
* file
= NULL
;
991 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "sO", kwlist
, &type
, &file
))
994 // Make a file handle out of file
995 f
= PyObject_AsFileHandle(file
, "w");
999 // Create a new build environment
1000 r
= pakfire_build_create(&build
, self
->pakfire
, NULL
, 0);
1002 PyErr_SetFromErrno(PyExc_OSError
);
1006 Py_BEGIN_ALLOW_THREADS
1009 r
= pakfire_build_mkimage(build
, type
, f
);
1014 PyErr_SetFromErrno(PyExc_OSError
);
1019 Py_END_ALLOW_THREADS
1023 pakfire_build_unref(build
);
1032 static struct PyMethodDef Pakfire_methods
[] = {
1035 (PyCFunction
)Pakfire_build
,
1036 METH_VARARGS
|METH_KEYWORDS
,
1041 (PyCFunction
)Pakfire_clean
,
1047 (PyCFunction
)Pakfire_dist
,
1053 (PyCFunction
)Pakfire_execute
,
1054 METH_VARARGS
|METH_KEYWORDS
,
1059 (PyCFunction
)Pakfire_generate_key
,
1060 METH_VARARGS
|METH_KEYWORDS
,
1065 (PyCFunction
)Pakfire_get_repo
,
1071 (PyCFunction
)Pakfire_import_key
,
1077 (PyCFunction
)Pakfire_mkimage
,
1078 METH_VARARGS
|METH_KEYWORDS
,
1083 (PyCFunction
)Pakfire_open
,
1089 (PyCFunction
)Pakfire_refresh
,
1095 (PyCFunction
)Pakfire_repo_compose
,
1096 METH_VARARGS
|METH_KEYWORDS
,
1101 (PyCFunction
)Pakfire_search
,
1102 METH_VARARGS
|METH_KEYWORDS
,
1107 (PyCFunction
)Pakfire_shell
,
1108 METH_VARARGS
|METH_KEYWORDS
,
1113 (PyCFunction
)Pakfire_version_compare
,
1119 (PyCFunction
)Pakfire_whatprovides
,
1125 (PyCFunction
)Pakfire_whatrequires
,
1132 static struct PyGetSetDef Pakfire_getsetters
[] = {
1135 (getter
)Pakfire_get_arch
,
1142 (getter
)Pakfire_get_path
,
1149 (getter
)Pakfire_get_repos
,
1157 PyTypeObject PakfireType
= {
1158 PyVarObject_HEAD_INIT(NULL
, 0)
1159 tp_name
: "_pakfire.Pakfire",
1160 tp_basicsize
: sizeof(PakfireObject
),
1161 tp_flags
: Py_TPFLAGS_DEFAULT
|Py_TPFLAGS_BASETYPE
,
1162 tp_new
: Pakfire_new
,
1163 tp_dealloc
: (destructor
)Pakfire_dealloc
,
1164 tp_init
: (initproc
)Pakfire_init
,
1165 tp_doc
: "Pakfire object",
1166 tp_methods
: Pakfire_methods
,
1167 tp_getset
: Pakfire_getsetters
,
1168 tp_repr
: (reprfunc
)Pakfire_repr
,