]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/_pakfire/pakfire.c
_pakfire: Import/export keys as strings
[people/ms/pakfire.git] / src / _pakfire / pakfire.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2017 Pakfire development team #
5 # #
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. #
10 # #
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. #
15 # #
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/>. #
18 # #
19 #############################################################################*/
20
21 #define PY_SSIZE_T_CLEAN
22 #include <Python.h>
23 #include <errno.h>
24
25 #include <pakfire/archive.h>
26 #include <pakfire/build.h>
27 #include <pakfire/constants.h>
28 #include <pakfire/dist.h>
29 #include <pakfire/jail.h>
30 #include <pakfire/logging.h>
31 #include <pakfire/mount.h>
32 #include <pakfire/packagelist.h>
33 #include <pakfire/pakfire.h>
34 #include <pakfire/key.h>
35 #include <pakfire/repo.h>
36 #include <pakfire/repolist.h>
37 #include <pakfire/request.h>
38 #include <pakfire/transaction.h>
39 #include <pakfire/util.h>
40
41 #include "archive.h"
42 #include "errors.h"
43 #include "key.h"
44 #include "pakfire.h"
45 #include "repo.h"
46 #include "util.h"
47
48 static PyObject* Pakfire_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
49 PakfireObject* self = (PakfireObject *)type->tp_alloc(type, 0);
50 if (self) {
51 self->pakfire = NULL;
52
53 // Callbacks
54 self->callbacks.log = NULL;
55 self->callbacks.confirm = NULL;
56 }
57
58 return (PyObject *)self;
59 }
60
61 static void Pakfire_log_callback(void* data, int priority, const char* file, int line,
62 const char* fn, const char* format, va_list args) {
63 PyObject* callback = (PyObject*)data;
64 PyObject* exception = NULL;
65
66 // Do nothing if callback isn't set
67 if (!callback)
68 return;
69
70 PyGILState_STATE state = PyGILState_Ensure();
71
72 // Translate priority to Python logging priorities
73 switch (priority) {
74 case LOG_DEBUG:
75 priority = 10;
76 break;
77
78 case LOG_INFO:
79 priority = 20;
80 break;
81
82 case LOG_ERR:
83 priority = 40;
84 break;
85
86 // Drop messages of an unknown priority
87 default:
88 return;
89 }
90
91 PyObject* tuple = NULL;
92 PyObject* result = NULL;
93 char* buffer = NULL;
94
95 // Make line
96 int r = vasprintf(&buffer, format, args);
97 if (r < 0)
98 goto ERROR;
99
100 // Build a tuple with the priority and the log message
101 tuple = Py_BuildValue("(is)", priority, buffer);
102 if (!tuple)
103 goto ERROR;
104
105 // Call the callback
106 result = PyObject_CallObject(callback, tuple);
107
108 ERROR:
109 /*
110 We cannot really catch any Python errors here, since we cannot send
111 any error codes up the chain.
112
113 So, in order to debug the problem, We will check if an exception has
114 occurred and if so, print it to the console.
115 */
116 exception = PyErr_Occurred();
117 if (exception)
118 PyErr_Print();
119
120 if (buffer)
121 free(buffer);
122 Py_XDECREF(tuple);
123 Py_XDECREF(result);
124
125 // Release the GIL
126 PyGILState_Release(state);
127 }
128
129 static int Pakfire_confirm_callback(struct pakfire* pakfire, void* data,
130 const char* message, const char* question) {
131 PyObject* callback = (PyObject*)data;
132 PyObject* result = NULL;
133 int r = 0;
134
135 // Do nothing if callback isn't set
136 if (!callback)
137 return 0;
138
139 // Acquire GIL
140 PyGILState_STATE state = PyGILState_Ensure();
141
142 PyObject* args = Py_BuildValue("(ss)", message, question);
143 if (!args) {
144 r = -1;
145 goto ERROR;
146 }
147
148 result = PyObject_CallObject(callback, args);
149
150 // If the callback raised an exception, we will ignore it and indicate
151 // that an error happened, but we cannot re-raise the exception
152 if (!result) {
153 r = -1;
154 goto ERROR;
155 }
156
157 // Set the return code
158 if (result == Py_True)
159 r = 0;
160 else
161 r = 1;
162
163 ERROR:
164 Py_DECREF(args);
165 Py_DECREF(result);
166
167 // Release the GIL
168 PyGILState_Release(state);
169
170 return r;
171 }
172
173 static int Pakfire_init(PakfireObject* self, PyObject* args, PyObject* kwds) {
174 char* kwlist[] = {
175 "path",
176 "arch",
177 "logger",
178 "offline",
179 "conf",
180 "confirm_callback",
181 NULL,
182 };
183 const char* path = NULL;
184 const char* arch = NULL;
185 const char* conf = NULL;
186 int offline = 0;
187 int r;
188
189 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzOpzO", kwlist,
190 &path, &arch, &self->callbacks.log, &offline, &conf, &self->callbacks.confirm))
191 return -1;
192
193 // Check if log callback is callable
194 if (self->callbacks.log && !PyCallable_Check(self->callbacks.log)) {
195 PyErr_SetString(PyExc_TypeError, "logger must be callable\n");
196 return -1;
197 }
198
199 // Check if confirm callback is callable
200 if (self->callbacks.confirm && !PyCallable_Check(self->callbacks.confirm)) {
201 PyErr_SetString(PyExc_TypeError, "Confirm callback is not callable");
202 return -1;
203 }
204
205 int flags = 0;
206
207 // Enable offline mode
208 if (offline)
209 flags |= PAKFIRE_FLAGS_OFFLINE;
210
211 // Configure callbacks
212 if (self->callbacks.log)
213 Py_INCREF(self->callbacks.log);
214
215 Py_BEGIN_ALLOW_THREADS
216
217 // Create a new Pakfire instance
218 r = pakfire_create(&self->pakfire, path, arch, conf, flags,
219 LOG_DEBUG, Pakfire_log_callback, self->callbacks.log);
220
221 Py_END_ALLOW_THREADS
222
223 if (r) {
224 switch (errno) {
225 // Invalid architecture or path
226 case EINVAL:
227 PyErr_SetString(PyExc_ValueError, "Invalid architecture or path");
228 break;
229
230 // Anything else
231 default:
232 PyErr_SetFromErrno(PyExc_OSError);
233 }
234
235 return -1;
236 }
237
238 // Configure confirm callback
239 if (self->callbacks.confirm) {
240 pakfire_set_confirm_callback(self->pakfire,
241 Pakfire_confirm_callback, self->callbacks.confirm);
242
243 Py_INCREF(self->callbacks.confirm);
244 }
245
246 return 0;
247 }
248
249 static void Pakfire_dealloc(PakfireObject* self) {
250 if (self->pakfire) {
251 // Reset log callback
252 if (self->callbacks.log) {
253 pakfire_set_log_callback(self->pakfire, NULL, NULL);
254 Py_DECREF(self->callbacks.log);
255 }
256
257 // Reset confirm callback
258 if (self->callbacks.confirm) {
259 pakfire_set_confirm_callback(self->pakfire, NULL, NULL);
260 Py_DECREF(self->callbacks.confirm);
261 }
262
263 Py_BEGIN_ALLOW_THREADS
264
265 pakfire_unref(self->pakfire);
266
267 Py_END_ALLOW_THREADS
268 }
269
270 Py_TYPE(self)->tp_free((PyObject *)self);
271 }
272
273 static PyObject* Pakfire_repr(PakfireObject* self) {
274 const char* path = pakfire_get_path(self->pakfire);
275 const char* arch = pakfire_get_arch(self->pakfire);
276
277 return PyUnicode_FromFormat("<_pakfire.Pakfire %s (%s)>", path, arch);
278 }
279
280 static PyObject* Pakfire_get_path(PakfireObject* self) {
281 const char* path = pakfire_get_path(self->pakfire);
282
283 return PyUnicode_FromString(path);
284 }
285
286 static PyObject* Pakfire_get_arch(PakfireObject* self) {
287 const char* arch = pakfire_get_arch(self->pakfire);
288
289 return PyUnicode_FromString(arch);
290 }
291
292 static PyObject* Pakfire_get_repo(PakfireObject* self, PyObject* args) {
293 const char* name = NULL;
294
295 if (!PyArg_ParseTuple(args, "s", &name))
296 return NULL;
297
298 struct pakfire_repo* repo = pakfire_get_repo(self->pakfire, name);
299 if (!repo)
300 Py_RETURN_NONE;
301
302 PyObject* obj = new_repo(&RepoType, repo);
303 pakfire_repo_unref(repo);
304
305 return obj;
306 }
307
308 static void Pakfire_status_callback(struct pakfire* pakfire, void* data,
309 int progress, const char* status) {
310 PyObject* callback = (PyObject*)data;
311
312 // Do not attempt to call nothing
313 if (!callback)
314 return;
315
316 // Acquire GIL
317 PyGILState_STATE state = PyGILState_Ensure();
318
319 // Compile arguments
320 PyObject* args = Py_BuildValue("(is)", progress, status);
321 if (args) {
322 // Call the callback
323 PyObject* result = PyObject_CallObject(callback, args);
324
325 Py_XDECREF(result);
326 Py_DECREF(args);
327 }
328
329 // Release the GIL
330 PyGILState_Release(state);
331 }
332
333 static int convert_packages(PyObject* object, void* address) {
334 char*** packages = (char***)address;
335
336 // Called for cleanup
337 if (!object)
338 goto ERROR;
339
340 // Nothing to do when object is None
341 if (object == Py_None)
342 return Py_CLEANUP_SUPPORTED;
343
344 if (!PySequence_Check(object)) {
345 PyErr_SetString(PyExc_ValueError, "Packages must be a sequence");
346 goto ERROR;
347 }
348
349 const unsigned int length = PySequence_Length(object);
350 if (!length)
351 return Py_CLEANUP_SUPPORTED;
352
353 // Allocate array
354 *packages = calloc(length + 1, sizeof(*packages));
355 if (!*packages) {
356 PyErr_SetFromErrno(PyExc_OSError);
357 goto ERROR;
358 }
359
360 for (unsigned int i = 0; i < length; i++) {
361 PyObject* item = PySequence_GetItem(object, i);
362
363 // Check if input is a string
364 if (!PyUnicode_Check(item)) {
365 Py_DECREF(item);
366
367 PyErr_SetString(PyExc_AttributeError, "Expected a string");
368 goto ERROR;
369 }
370
371 // Fetch string
372 const char* package = PyUnicode_AsUTF8(item);
373 if (!package) {
374 Py_DECREF(item);
375 goto ERROR;
376 }
377
378 // Add package to array
379 (*packages)[i] = strdup(package);
380 if (!(*packages)[i]) {
381 Py_DECREF(item);
382 goto ERROR;
383 }
384
385 Py_DECREF(item);
386 }
387
388 // Success
389 return Py_CLEANUP_SUPPORTED;
390
391 ERROR:
392 if (*packages) {
393 for (char** package = *packages; *package; package++)
394 free(*package);
395 free(*packages);
396 }
397
398 return 0;
399 }
400
401 static PyObject* Pakfire_install(PakfireObject* self, PyObject* args, PyObject* kwargs) {
402 char* kwlist[] = {
403 "packages",
404 "dryrun",
405 "without_recommended",
406 "allow_uninstall",
407 "allow_downgrade",
408 "status_callback",
409 NULL
410 };
411 char** packages = NULL;
412 int dryrun = 0;
413 int without_recommended = 0;
414 int allow_uninstall = 0;
415 int allow_downgrade = 0;
416 int solver_flags = 0;
417 int transaction_flags = 0;
418 PyObject* status_callback = NULL;
419 int r;
420
421 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$ppppO", kwlist,
422 convert_packages, &packages, &dryrun, &without_recommended, &allow_uninstall,
423 &allow_downgrade, &status_callback))
424 return NULL;
425
426 // Check if callback is callable
427 if (status_callback && !PyCallable_Check(status_callback)) {
428 PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
429 return NULL;
430 }
431
432 // Enable dry-run mode
433 if (dryrun)
434 transaction_flags |= PAKFIRE_TRANSACTION_DRY_RUN;
435
436 // Do not install recommended packages
437 if (without_recommended)
438 solver_flags |= PAKFIRE_REQUEST_WITHOUT_RECOMMENDED;
439
440 // Can the solver uninstall packages?
441 if (allow_uninstall)
442 solver_flags |= PAKFIRE_REQUEST_ALLOW_UNINSTALL;
443
444 // Can the solver downgrade packages?
445 if (allow_downgrade)
446 solver_flags |= PAKFIRE_REQUEST_ALLOW_DOWNGRADE;
447
448 Py_BEGIN_ALLOW_THREADS
449
450 // Run pakfire_install
451 r = pakfire_install(self->pakfire, transaction_flags, solver_flags,
452 (const char**)packages, NULL, 0, NULL, Pakfire_status_callback, status_callback);
453
454 Py_END_ALLOW_THREADS
455
456 if (r)
457 PyErr_SetFromErrno(PyExc_OSError);
458
459 if (packages) {
460 for (char** package = packages; *package; package++)
461 free(*package);
462 free(packages);
463 }
464
465 if (r)
466 return NULL;
467
468 Py_RETURN_NONE;
469 }
470
471 static PyObject* Pakfire_erase(PakfireObject* self, PyObject* args, PyObject* kwargs) {
472 char* kwlist[] = {
473 "packages",
474 "dryrun",
475 "keep_dependencies",
476 "status_callback",
477 NULL
478 };
479 char** packages = NULL;
480 int dryrun = 0;
481 int keep_dependencies = 0;
482 int transaction_flags = 0;
483 int flags = 0;
484 PyObject* status_callback = NULL;
485 int r;
486
487 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$ppO", kwlist,
488 convert_packages, &packages, &dryrun, &keep_dependencies, &status_callback))
489 return NULL;
490
491 // Check if callback is callable
492 if (status_callback && !PyCallable_Check(status_callback)) {
493 PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
494 return NULL;
495 }
496
497 if (dryrun)
498 transaction_flags |= PAKFIRE_TRANSACTION_DRY_RUN;
499
500 if (keep_dependencies)
501 flags |= PAKFIRE_REQUEST_KEEP_DEPS;
502
503 Py_BEGIN_ALLOW_THREADS
504
505 // Run pakfire_erase
506 r = pakfire_erase(self->pakfire, transaction_flags, 0, (const char**)packages,
507 NULL, flags, NULL, Pakfire_status_callback, status_callback);
508
509 Py_END_ALLOW_THREADS
510
511 if (r)
512 PyErr_SetFromErrno(PyExc_OSError);
513
514 if (packages) {
515 for (char** package = packages; *package; package++)
516 free(*package);
517 free(packages);
518 }
519
520 if (r)
521 return NULL;
522
523 Py_RETURN_NONE;
524 }
525
526 static PyObject* Pakfire_update(PakfireObject* self, PyObject* args, PyObject* kwargs) {
527 char* kwlist[] = {
528 "packages",
529 "dryrun",
530 "excludes",
531 "allow_uninstall",
532 "allow_downgrade",
533 "status_callback",
534 NULL
535 };
536 char** packages = NULL;
537 char** excludes = NULL;
538 int dryrun = 0;
539 int allow_uninstall = 0;
540 int allow_downgrade = 0;
541 int solver_flags = 0;
542 int transaction_flags = 0;
543 PyObject* status_callback = NULL;
544 int r;
545
546 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|$pO&ppO", kwlist,
547 convert_packages, &packages, &dryrun, convert_packages, &excludes,
548 &allow_uninstall, &allow_downgrade, &status_callback))
549 return NULL;
550
551 // Check if callback is callable
552 if (status_callback && !PyCallable_Check(status_callback)) {
553 PyErr_SetString(PyExc_TypeError, "status_callback must be callable");
554 return NULL;
555 }
556
557 if (dryrun)
558 transaction_flags = PAKFIRE_TRANSACTION_DRY_RUN;
559
560 // Can the solver uninstall packages?
561 if (allow_uninstall)
562 solver_flags |= PAKFIRE_REQUEST_ALLOW_UNINSTALL;
563
564 // Can the solver downgrade packages?
565 if (allow_downgrade)
566 solver_flags |= PAKFIRE_REQUEST_ALLOW_DOWNGRADE;
567
568 Py_BEGIN_ALLOW_THREADS
569
570 // Run pakfire_update
571 r = pakfire_update(self->pakfire, transaction_flags, solver_flags,
572 (const char**)packages, (const char**)excludes, 0, NULL,
573 Pakfire_status_callback, status_callback);
574
575 Py_END_ALLOW_THREADS
576
577 if (r)
578 PyErr_SetFromErrno(PyExc_OSError);
579
580 if (packages) {
581 for (char** package = packages; *package; package++)
582 free(*package);
583 free(packages);
584 }
585
586 if (excludes) {
587 for (char** exclude = excludes; *exclude; exclude++)
588 free(*exclude);
589 free(excludes);
590 }
591
592 if (r)
593 return NULL;
594
595 Py_RETURN_NONE;
596 }
597
598 static PyObject* Pakfire_generate_key(PakfireObject* self, PyObject* args, PyObject* kwds) {
599 char* kwlist[] = { "algorithm", NULL };
600 struct pakfire_key* key = NULL;
601 pakfire_key_algo_t algo = PAKFIRE_KEY_ALGO_NULL;
602
603 if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, &algo))
604 return NULL;
605
606 // Generate a new key
607 int r = pakfire_key_generate(&key, self->pakfire, algo);
608 if (r) {
609 PyErr_SetFromErrno(PyExc_OSError);
610 return NULL;
611 }
612
613 PyObject* object = new_key(&KeyType, key);
614 pakfire_key_unref(key);
615
616 return object;
617 }
618
619 static PyObject* Pakfire_import_key(PakfireObject* self, PyObject* args) {
620 struct pakfire_key* key = NULL;
621 PyObject* object = NULL;
622 char* data = NULL;
623 Py_ssize_t data_length = 0;
624 int r;
625
626 // Parse arguments
627 if (!PyArg_ParseTuple(args, "s#", &data, &data_length))
628 return NULL;
629
630 // Map the object
631 FILE* f = fmemopen(data, data_length, "r");
632 if (!f)
633 return NULL;
634
635 // Import the key
636 r = pakfire_key_import(&key, self->pakfire, f);
637 if (r) {
638 PyErr_SetFromErrno(PyExc_OSError);
639 goto ERROR;
640 }
641
642 // Convert the key into a Key object
643 object = new_key(&KeyType, key);
644 if (!object)
645 goto ERROR;
646
647 ERROR:
648 if (key)
649 pakfire_key_unref(key);
650 if (f)
651 fclose(f);
652
653 return object;
654 }
655
656 static PyObject* Pakfire_whatprovides(PakfireObject* self, PyObject* args) {
657 const char* provides = NULL;
658 struct pakfire_packagelist* list = NULL;
659 PyObject* ret = NULL;
660 int r;
661
662 if (!PyArg_ParseTuple(args, "s", &provides))
663 return NULL;
664
665 // Create a new list
666 r = pakfire_packagelist_create(&list, self->pakfire);
667 if (r) {
668 PyErr_SetFromErrno(PyExc_OSError);
669 goto ERROR;
670 }
671
672 r = pakfire_whatprovides(self->pakfire, provides, 0, list);
673 if (r) {
674 PyErr_SetFromErrno(PyExc_OSError);
675 goto ERROR;
676 }
677
678 // Create a Python list from the package list
679 ret = PyList_FromPackageList(list);
680
681 ERROR:
682 if (list)
683 pakfire_packagelist_unref(list);
684
685 return ret;
686 }
687
688 static PyObject* Pakfire_whatrequires(PakfireObject* self, PyObject* args) {
689 const char* requires = NULL;
690 struct pakfire_packagelist* list = NULL;
691 PyObject* ret = NULL;
692 int r;
693
694 if (!PyArg_ParseTuple(args, "s", &requires))
695 return NULL;
696
697 Py_BEGIN_ALLOW_THREADS
698
699 // Create a new list
700 r = pakfire_packagelist_create(&list, self->pakfire);
701 if (r) {
702 Py_BLOCK_THREADS
703 PyErr_SetFromErrno(PyExc_OSError);
704 goto ERROR;
705 }
706
707 r = pakfire_whatrequires(self->pakfire, requires, 0, list);
708 if (r) {
709 Py_BLOCK_THREADS
710 PyErr_SetFromErrno(PyExc_OSError);
711 goto ERROR;
712 }
713
714 Py_END_ALLOW_THREADS
715
716 // Create a Python list from the package list
717 ret = PyList_FromPackageList(list);
718
719 ERROR:
720 if (list)
721 pakfire_packagelist_unref(list);
722
723 return ret;
724 }
725
726 static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* kwds) {
727 char* kwlist[] = { "pattern", "name_only", NULL };
728 struct pakfire_packagelist* list = NULL;
729 const char* pattern = NULL;
730 int name_only = 0;
731 int flags = 0;
732 PyObject* ret = NULL;
733 int r;
734
735 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|$p", kwlist, &pattern, &name_only))
736 return NULL;
737
738 // Search for package names only
739 if (name_only)
740 flags |= PAKFIRE_SEARCH_NAME_ONLY;
741
742 r = pakfire_packagelist_create(&list, self->pakfire);
743 if (r) {
744 PyErr_SetFromErrno(PyExc_OSError);
745 goto ERROR;
746 }
747
748 r = pakfire_search(self->pakfire, pattern, flags, list);
749 if (r) {
750 PyErr_SetFromErrno(PyExc_OSError);
751 goto ERROR;
752 }
753
754 ret = PyList_FromPackageList(list);
755
756 ERROR:
757 if (list)
758 pakfire_packagelist_unref(list);
759
760 return ret;
761 }
762
763 static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
764 const char* evr1 = NULL;
765 const char* evr2 = NULL;
766
767 if (!PyArg_ParseTuple(args, "ss", &evr1, &evr2))
768 return NULL;
769
770 int cmp = pakfire_version_compare(self->pakfire, evr1, evr2);
771
772 return PyLong_FromLong(cmp);
773 }
774
775 static int Pakfire_execute_output_callback(struct pakfire* pakfire, void* data,
776 int priority, const char* line, size_t length) {
777 PyObject* callback = (PyObject*)data;
778 int r = 0;
779
780 // Do nothing if callback isn't set
781 if (!callback)
782 return 0;
783
784 // Translate priority to Python logging priorities
785 switch (priority) {
786 case LOG_INFO:
787 priority = 20;
788 break;
789
790 case LOG_ERR:
791 priority = 40;
792 break;
793 }
794
795 // Remove the trailing newline
796 if (line && line[length - 1] == '\n')
797 length--;
798
799 // Create tuple with arguments for the callback function
800 PyObject* args = Py_BuildValue("(is#)", priority, line, (Py_ssize_t)length);
801 if (!args)
802 return 1;
803
804 PyObject* result = PyObject_CallObject(callback, args);
805 if (result && PyLong_Check(result)) {
806 r = PyLong_AsLong(result);
807 }
808
809 Py_XDECREF(args);
810 Py_XDECREF(result);
811
812 return r;
813 }
814
815 static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwds) {
816 char* kwlist[] = {
817 "command",
818 "environ",
819 "bind",
820 "callback",
821 "nice",
822 NULL
823 };
824
825 struct pakfire_jail* jail = NULL;
826 const char** argv = NULL;
827 int r;
828 PyObject* ret = NULL;
829
830 PyObject* command = NULL;
831 PyObject* environ = NULL;
832 PyObject* bind = NULL;
833 PyObject* callback = NULL;
834 int nice = 0;
835
836 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOi", kwlist, &command, &environ,
837 &bind, &callback, &nice))
838 return NULL;
839
840 // Check if command is a list
841 if (!PyList_Check(command)) {
842 PyErr_SetString(PyExc_TypeError, "command must be a list");
843 goto ERROR;
844 }
845
846 const ssize_t command_length = PyList_Size(command);
847
848 // Check if command is not empty
849 if (command_length == 0) {
850 PyErr_SetString(PyExc_ValueError, "command is empty");
851 goto ERROR;
852 }
853
854 // Allocate argv
855 argv = calloc(command_length + 1, sizeof(*argv));
856 if (!argv)
857 goto ERROR;
858
859 // All arguments in command must be strings
860 for (unsigned int i = 0; i < command_length; i++) {
861 PyObject* item = PyList_GET_ITEM(command, i);
862
863 if (!PyUnicode_Check(item)) {
864 PyErr_Format(PyExc_TypeError, "Item %u in command is not a string", i);
865 return NULL;
866 }
867
868 // Copy to argv
869 argv[i] = PyUnicode_AsUTF8(item);
870 }
871
872 // Check if bind is a sequence
873 if (bind && !PySequence_Check(bind)) {
874 PyErr_SetString(PyExc_ValueError, "bind is not a sequence");
875 goto ERROR;
876 }
877
878 // Check callback
879 if (callback && !PyCallable_Check(callback)) {
880 PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
881 goto ERROR;
882 }
883
884 // Create jail
885 r = pakfire_jail_create(&jail, self->pakfire);
886 if (r) {
887 PyErr_SetFromErrno(PyExc_OSError);
888 goto ERROR;
889 }
890
891 // Check callback
892 if (callback && !PyCallable_Check(callback)) {
893 PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
894 goto ERROR;
895 }
896
897 // Set nice
898 if (nice) {
899 r = pakfire_jail_nice(jail, nice);
900 if (r) {
901 PyErr_SetFromErrno(PyExc_OSError);
902 goto ERROR;
903 }
904 }
905
906 PyObject* key = NULL;
907 PyObject* value = NULL;
908 Py_ssize_t p = 0;
909
910 // Parse the environment
911 if (environ) {
912 // Check if environ is a dictionary
913 if (!PyDict_Check(environ)) {
914 PyErr_SetString(PyExc_TypeError, "environ must be a dictionary");
915 goto ERROR;
916 }
917
918 // All keys and values must be strings
919 while (PyDict_Next(environ, &p, &key, &value)) {
920 if (!PyUnicode_Check(key) || !PyUnicode_Check(value)) {
921 PyErr_SetString(PyExc_TypeError, "Environment contains a non-string object");
922 goto ERROR;
923 }
924
925 // Set environment value
926 r = pakfire_jail_set_env(jail, PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));
927 if (r) {
928 PyErr_SetFromErrno(PyExc_OSError);
929 goto ERROR;
930 }
931 }
932 }
933
934 const Py_ssize_t num_bind = PySequence_Length(bind);
935
936 // Bind
937 for (unsigned int i = 0; i < num_bind; i++) {
938 PyObject* b = PySequence_ITEM(bind, i);
939 if (!b)
940 goto ERROR;
941
942 // Check if this is a Unicode object
943 if (!PyUnicode_Check(b)) {
944 PyErr_SetString(PyExc_ValueError, "bind contains a non-Unicode object");
945 Py_DECREF(b);
946 goto ERROR;
947 }
948
949 const char* path = PyUnicode_AsUTF8(b);
950
951 // Perform bind
952 r = pakfire_jail_bind(jail, path, path, 0);
953 if (r) {
954 PyErr_SetFromErrno(PyExc_OSError);
955 Py_DECREF(b);
956 goto ERROR;
957 }
958
959 Py_DECREF(b);
960 }
961
962 Py_BEGIN_ALLOW_THREADS
963
964 // Execute command
965 r = pakfire_jail_exec(jail, argv,
966 NULL, Pakfire_execute_output_callback, callback, 0);
967
968 Py_END_ALLOW_THREADS
969
970 // If the return code was negative, we had some internal error
971 if (r < 0) {
972 PyErr_SetFromErrno(PyExc_OSError);
973 goto ERROR;
974
975 // Otherwise the executed command returned some error code
976 } else if (r > 0) {
977 PyObject* code = PyLong_FromLong(r);
978
979 // Raise CommandExecutionError
980 PyErr_SetObject(PyExc_CommandExecutionError, code);
981 Py_DECREF(code);
982
983 goto ERROR;
984 }
985
986 // The process has exited successfully
987
988 // Return None
989 ret = Py_None;
990 Py_INCREF(ret);
991
992 ERROR:
993 if (argv)
994 free(argv);
995 if (jail)
996 pakfire_jail_unref(jail);
997
998 return ret;
999 }
1000
1001 static PyObject* Pakfire_dist(PakfireObject* self, PyObject* args) {
1002 const char* path = NULL;
1003 const char* target = NULL;
1004 char* result = NULL;
1005 int r;
1006
1007 if (!PyArg_ParseTuple(args, "s|z", &path, &target))
1008 return NULL;
1009
1010 Py_BEGIN_ALLOW_THREADS
1011
1012 r = pakfire_dist(self->pakfire, path, target, &result);
1013 if (r) {
1014 Py_BLOCK_THREADS
1015 PyErr_SetFromErrno(PyExc_OSError);
1016 return NULL;
1017 }
1018
1019 Py_END_ALLOW_THREADS
1020
1021 PyObject* ret = PyUnicode_FromString(result);
1022 free(result);
1023
1024 return ret;
1025 }
1026
1027 static PyObject* Pakfire_copy_in(PakfireObject* self, PyObject* args) {
1028 const char* src = NULL;
1029 const char* dst = NULL;
1030
1031 if (!PyArg_ParseTuple(args, "ss", &src, &dst))
1032 return NULL;
1033
1034 Py_BEGIN_ALLOW_THREADS
1035
1036 int r = pakfire_copy_in(self->pakfire, src, dst);
1037 if (r) {
1038 Py_BLOCK_THREADS
1039 PyErr_SetFromErrno(PyExc_OSError);
1040 return NULL;
1041 }
1042
1043 Py_END_ALLOW_THREADS
1044
1045 Py_RETURN_NONE;
1046 }
1047
1048 static PyObject* Pakfire_copy_out(PakfireObject* self, PyObject* args) {
1049 const char* src = NULL;
1050 const char* dst = NULL;
1051
1052 if (!PyArg_ParseTuple(args, "ss", &src, &dst))
1053 return NULL;
1054
1055 Py_BEGIN_ALLOW_THREADS
1056
1057 int r = pakfire_copy_out(self->pakfire, src, dst);
1058 if (r) {
1059 Py_BLOCK_THREADS
1060 PyErr_SetFromErrno(PyExc_OSError);
1061 return NULL;
1062 }
1063
1064 Py_END_ALLOW_THREADS
1065
1066 Py_RETURN_NONE;
1067 }
1068
1069 static PyObject* Pakfire_get_repos(PakfireObject* self) {
1070 struct pakfire_repolist* repos = pakfire_get_repos(self->pakfire);
1071 if (!repos) {
1072 PyErr_SetFromErrno(PyExc_OSError);
1073 return NULL;
1074 }
1075
1076 const size_t l = pakfire_repolist_size(repos);
1077
1078 PyObject* list = PyList_New(l);
1079 if (!list)
1080 goto ERROR;
1081
1082 for (unsigned int i = 0; i < l; i++) {
1083 struct pakfire_repo* repo = pakfire_repolist_get(repos, i);
1084 if (!repo)
1085 continue;
1086
1087 PyObject* obj = new_repo(&RepoType, repo);
1088 PyList_SET_ITEM(list, i, obj);
1089
1090 pakfire_repo_unref(repo);
1091 }
1092
1093 ERROR:
1094 pakfire_repolist_unref(repos);
1095
1096 return list;
1097 }
1098
1099 static PyObject* execute_return_value(int r) {
1100 // Raise an OS error if r < 0
1101 if (r < 0) {
1102 errno = -r;
1103
1104 PyErr_SetFromErrno(PyExc_OSError);
1105 return NULL;
1106
1107 // Raise exception when the command failed
1108 } else if (r > 0) {
1109 PyObject* code = PyLong_FromLong(r);
1110
1111 PyErr_SetObject(PyExc_CommandExecutionError, code);
1112 Py_DECREF(code);
1113
1114 return NULL;
1115 }
1116
1117 Py_RETURN_NONE;
1118 }
1119
1120 static PyObject* Pakfire_build(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1121 char* kwlist[] = {
1122 "path",
1123 "target",
1124 "build_id",
1125 "ccache_path",
1126 "interactive",
1127 "disable_snapshot",
1128 "disable_ccache",
1129 "disable_tests",
1130 NULL,
1131 };
1132 struct pakfire_build* build = NULL;
1133 const char* path = NULL;
1134 const char* target = NULL;
1135 const char* build_id = NULL;
1136 const char* ccache_path = NULL;
1137 int interactive = 0;
1138 int disable_snapshot = 0;
1139 int disable_ccache = 0;
1140 int disable_tests = 0;
1141 int r;
1142
1143 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzzpppp", kwlist,
1144 &path, &target, &build_id, &ccache_path, &interactive,
1145 &disable_snapshot, &disable_ccache, &disable_tests))
1146 return NULL;
1147
1148 int flags = 0;
1149
1150 if (interactive)
1151 flags |= PAKFIRE_BUILD_INTERACTIVE;
1152
1153 // Disable snapshot if requested
1154 if (disable_snapshot)
1155 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
1156
1157 // Disable ccache if requested
1158 if (disable_ccache)
1159 flags |= PAKFIRE_BUILD_DISABLE_CCACHE;
1160
1161 // Disable tests if requested
1162 if (disable_tests)
1163 flags |= PAKFIRE_BUILD_DISABLE_TESTS;
1164
1165 // Create a new build environment
1166 r = pakfire_build_create(&build, self->pakfire, build_id, flags);
1167 if (r) {
1168 PyErr_SetFromErrno(PyExc_OSError);
1169 goto ERROR;
1170 }
1171
1172 // Set target
1173 if (target) {
1174 r = pakfire_build_set_target(build, target);
1175 if (r) {
1176 PyErr_SetFromErrno(PyExc_OSError);
1177 goto ERROR;
1178 }
1179 }
1180
1181 // Set ccache path
1182 if (ccache_path) {
1183 r = pakfire_build_set_ccache_path(build, ccache_path);
1184 if (r) {
1185 PyErr_SetFromErrno(PyExc_OSError);
1186 goto ERROR;
1187 }
1188 }
1189
1190 Py_BEGIN_ALLOW_THREADS
1191
1192 // Run build
1193 r = pakfire_build_exec(build, path);
1194 if (r) {
1195 Py_BLOCK_THREADS;
1196
1197 if (r < 0) {
1198 PyErr_SetFromErrno(PyExc_OSError);
1199
1200 // Raise a command execution error
1201 } else {
1202 PyObject* code = PyLong_FromLong(r);
1203
1204 PyErr_SetObject(PyExc_CommandExecutionError, code);
1205 Py_DECREF(code);
1206 }
1207
1208 goto ERROR;
1209 }
1210
1211 Py_END_ALLOW_THREADS
1212
1213 ERROR:
1214 if (build)
1215 pakfire_build_unref(build);
1216
1217 if (r)
1218 return NULL;
1219
1220 Py_RETURN_NONE;
1221 }
1222
1223 static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1224 char* kwlist[] = {
1225 "install",
1226 "disable_snapshot",
1227 NULL,
1228 };
1229 char** packages = NULL;
1230 int disable_snapshot = 0;
1231 int r;
1232
1233 // Parse everything
1234 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&p", kwlist,
1235 convert_packages, &packages, &disable_snapshot))
1236 return NULL;
1237
1238 int flags = 0;
1239
1240 if (disable_snapshot)
1241 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
1242
1243 Py_BEGIN_ALLOW_THREADS
1244
1245 r = pakfire_shell(self->pakfire, (const char**)packages, flags);
1246
1247 Py_END_ALLOW_THREADS
1248
1249 return execute_return_value(r);
1250 }
1251
1252 static PyObject* Pakfire_clean(PakfireObject* self) {
1253 int r;
1254
1255 Py_BEGIN_ALLOW_THREADS
1256
1257 r = pakfire_clean(self->pakfire, 0);
1258 if (r) {
1259 Py_BLOCK_THREADS
1260 PyErr_SetFromErrno(PyExc_OSError);
1261 return NULL;
1262 }
1263
1264 Py_END_ALLOW_THREADS
1265
1266 Py_RETURN_NONE;
1267 }
1268
1269 static PyObject* Pakfire_refresh(PakfireObject* self, PyObject* args) {
1270 int force = 0;
1271 int r;
1272
1273 if (!PyArg_ParseTuple(args, "|p", &force))
1274 return NULL;
1275
1276 Py_BEGIN_ALLOW_THREADS
1277
1278 r = pakfire_refresh(self->pakfire, force);
1279 if (r) {
1280 Py_BLOCK_THREADS
1281 PyErr_SetFromErrno(PyExc_OSError);
1282 return NULL;
1283 }
1284
1285 Py_END_ALLOW_THREADS
1286
1287 Py_RETURN_NONE;
1288 }
1289
1290 static PyObject* Pakfire_check(PakfireObject* self) {
1291 struct pakfire_filelist* errors = NULL;
1292 int r;
1293
1294 // Allocate a filelist for errors
1295 r = pakfire_filelist_create(&errors, self->pakfire);
1296 if (r)
1297 goto ERROR;
1298
1299 Py_BEGIN_ALLOW_THREADS
1300
1301 // Perform check
1302 r = pakfire_check(self->pakfire, errors);
1303
1304 Py_END_ALLOW_THREADS
1305
1306 if (r)
1307 goto ERROR;
1308
1309 const size_t num_errors = pakfire_filelist_length(errors);
1310
1311 // Did we find any errors?
1312 if (num_errors) {
1313 PyObject* _errors = PyList_FromFileList(errors);
1314 if (!_errors)
1315 goto ERROR;
1316
1317 pakfire_filelist_unref(errors);
1318
1319 // Raise CheckFileVerificationError
1320 PyErr_SetObject(PyExc_CheckFileVerificationError, _errors);
1321 Py_DECREF(_errors);
1322 return NULL;
1323 }
1324
1325 pakfire_filelist_unref(errors);
1326 Py_RETURN_NONE;
1327
1328 ERROR:
1329 // Cleanup
1330 if (errors)
1331 pakfire_filelist_unref(errors);
1332
1333 // Raise exception from errno
1334 PyErr_SetFromErrno(PyExc_OSError);
1335 return NULL;
1336 }
1337
1338 static PyObject* Pakfire_sync(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1339 char* kwlist[] = {
1340 "keep_orphaned",
1341 "status_callback",
1342 NULL,
1343 };
1344 int keep_orphaned = 0;
1345 int flags = 0;
1346 PyObject* status_callback = NULL;
1347 int r;
1348
1349 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$pO", kwlist,
1350 &keep_orphaned, &status_callback))
1351 return NULL;
1352
1353 if (keep_orphaned)
1354 flags |= PAKFIRE_REQUEST_KEEP_ORPHANED;
1355
1356 Py_BEGIN_ALLOW_THREADS
1357
1358 r = pakfire_sync(self->pakfire, 0, flags, NULL,
1359 Pakfire_status_callback, status_callback);
1360 if (r) {
1361 Py_BLOCK_THREADS
1362 PyErr_SetFromErrno(PyExc_OSError);
1363 return NULL;
1364 }
1365
1366 Py_END_ALLOW_THREADS
1367
1368 Py_RETURN_NONE;
1369 }
1370
1371 static PyObject* Pakfire_open(PakfireObject* self, PyObject* args) {
1372 struct pakfire_archive* archive = NULL;
1373 const char* path = NULL;
1374
1375 if (!PyArg_ParseTuple(args, "s", &path))
1376 return NULL;
1377
1378 Py_BEGIN_ALLOW_THREADS
1379
1380 int r = pakfire_archive_open(&archive, self->pakfire, path);
1381 if (r) {
1382 Py_BLOCK_THREADS
1383 PyErr_SetFromErrno(PyExc_OSError);
1384 return NULL;
1385 }
1386
1387 Py_END_ALLOW_THREADS
1388
1389 // Create Python object
1390 PyObject* object = new_archive(&ArchiveType, archive);
1391 pakfire_archive_unref(archive);
1392
1393 return object;
1394 }
1395
1396 static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1397 char* kwlist[] = { "path", "files", "key", NULL };
1398 const char* path = NULL;
1399 PyObject* list = NULL;
1400 KeyObject* key = NULL;
1401
1402 PyObject* ret = NULL;
1403
1404 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!", kwlist, &path, &list, &KeyType, &key))
1405 return NULL;
1406
1407 // List must be a sequence
1408 if (!PySequence_Check(list)) {
1409 PyErr_SetString(PyExc_ValueError, "Expected a sequence.");
1410 return NULL;
1411 }
1412
1413 // How many new files do we have?
1414 ssize_t num_files = PySequence_Length(list);
1415 if (num_files < 0)
1416 return NULL;
1417
1418 // Allocate files array
1419 const char** files = calloc(num_files + 1, sizeof(*files));
1420 if (!files) {
1421 PyErr_SetFromErrno(PyExc_OSError);
1422 return NULL;
1423 }
1424
1425 for (int i = 0; i < num_files; i++) {
1426 PyObject* file = PySequence_GetItem(list, i);
1427 if (!file)
1428 goto ERROR;
1429
1430 // Check if file is a Unicode object
1431 if (!PyUnicode_Check(file)) {
1432 PyErr_SetString(PyExc_ValueError, "Expected a string.");
1433 goto ERROR;
1434 }
1435
1436 // Add pointer to string to files array
1437 files[i] = PyUnicode_AsUTF8(file);
1438 if (!files[i])
1439 goto ERROR;
1440
1441 Py_DECREF(file);
1442 }
1443
1444 Py_BEGIN_ALLOW_THREADS
1445
1446 int r = pakfire_repo_compose(self->pakfire, path, (key) ? key->key : NULL, files);
1447 if (r) {
1448 Py_BLOCK_THREADS
1449 PyErr_SetFromErrno(PyExc_OSError);
1450 return NULL;
1451 }
1452
1453 Py_END_ALLOW_THREADS
1454
1455 // Return None on success
1456 ret = Py_None;
1457 Py_INCREF(ret);
1458
1459 ERROR:
1460 if (files)
1461 free(files);
1462
1463 return ret;
1464 }
1465
1466 static struct PyMethodDef Pakfire_methods[] = {
1467 {
1468 "build",
1469 (PyCFunction)Pakfire_build,
1470 METH_VARARGS|METH_KEYWORDS,
1471 NULL
1472 },
1473 {
1474 "check",
1475 (PyCFunction)Pakfire_check,
1476 METH_NOARGS,
1477 NULL,
1478 },
1479 {
1480 "clean",
1481 (PyCFunction)Pakfire_clean,
1482 METH_NOARGS,
1483 NULL,
1484 },
1485 {
1486 "copy_in",
1487 (PyCFunction)Pakfire_copy_in,
1488 METH_VARARGS,
1489 NULL,
1490 },
1491 {
1492 "copy_out",
1493 (PyCFunction)Pakfire_copy_out,
1494 METH_VARARGS,
1495 NULL,
1496 },
1497 {
1498 "dist",
1499 (PyCFunction)Pakfire_dist,
1500 METH_VARARGS,
1501 NULL
1502 },
1503 {
1504 "erase",
1505 (PyCFunction)Pakfire_erase,
1506 METH_VARARGS|METH_KEYWORDS,
1507 NULL
1508 },
1509 {
1510 "execute",
1511 (PyCFunction)Pakfire_execute,
1512 METH_VARARGS|METH_KEYWORDS,
1513 NULL
1514 },
1515 {
1516 "generate_key",
1517 (PyCFunction)Pakfire_generate_key,
1518 METH_VARARGS|METH_KEYWORDS,
1519 NULL
1520 },
1521 {
1522 "get_repo",
1523 (PyCFunction)Pakfire_get_repo,
1524 METH_VARARGS,
1525 NULL
1526 },
1527 {
1528 "import_key",
1529 (PyCFunction)Pakfire_import_key,
1530 METH_VARARGS,
1531 NULL,
1532 },
1533 {
1534 "install",
1535 (PyCFunction)Pakfire_install,
1536 METH_VARARGS|METH_KEYWORDS,
1537 NULL,
1538 },
1539 {
1540 "open",
1541 (PyCFunction)Pakfire_open,
1542 METH_VARARGS,
1543 NULL
1544 },
1545 {
1546 "refresh",
1547 (PyCFunction)Pakfire_refresh,
1548 METH_VARARGS,
1549 NULL,
1550 },
1551 {
1552 "repo_compose",
1553 (PyCFunction)Pakfire_repo_compose,
1554 METH_VARARGS|METH_KEYWORDS,
1555 NULL
1556 },
1557 {
1558 "search",
1559 (PyCFunction)Pakfire_search,
1560 METH_VARARGS|METH_KEYWORDS,
1561 NULL
1562 },
1563 {
1564 "shell",
1565 (PyCFunction)Pakfire_shell,
1566 METH_VARARGS|METH_KEYWORDS,
1567 NULL,
1568 },
1569 {
1570 "sync",
1571 (PyCFunction)Pakfire_sync,
1572 METH_VARARGS|METH_KEYWORDS,
1573 NULL,
1574 },
1575 {
1576 "update",
1577 (PyCFunction)Pakfire_update,
1578 METH_VARARGS|METH_KEYWORDS,
1579 NULL
1580 },
1581 {
1582 "version_compare",
1583 (PyCFunction)Pakfire_version_compare,
1584 METH_VARARGS,
1585 NULL
1586 },
1587 {
1588 "whatprovides",
1589 (PyCFunction)Pakfire_whatprovides,
1590 METH_VARARGS,
1591 NULL
1592 },
1593 {
1594 "whatrequires",
1595 (PyCFunction)Pakfire_whatrequires,
1596 METH_VARARGS,
1597 NULL
1598 },
1599 { NULL },
1600 };
1601
1602 static struct PyGetSetDef Pakfire_getsetters[] = {
1603 {
1604 "arch",
1605 (getter)Pakfire_get_arch,
1606 NULL,
1607 NULL,
1608 NULL
1609 },
1610 {
1611 "path",
1612 (getter)Pakfire_get_path,
1613 NULL,
1614 NULL,
1615 NULL
1616 },
1617 {
1618 "repos",
1619 (getter)Pakfire_get_repos,
1620 NULL,
1621 NULL,
1622 NULL
1623 },
1624 { NULL },
1625 };
1626
1627 PyTypeObject PakfireType = {
1628 PyVarObject_HEAD_INIT(NULL, 0)
1629 tp_name: "_pakfire.Pakfire",
1630 tp_basicsize: sizeof(PakfireObject),
1631 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
1632 tp_new: Pakfire_new,
1633 tp_dealloc: (destructor)Pakfire_dealloc,
1634 tp_init: (initproc)Pakfire_init,
1635 tp_doc: "Pakfire object",
1636 tp_methods: Pakfire_methods,
1637 tp_getset: Pakfire_getsetters,
1638 tp_repr: (reprfunc)Pakfire_repr,
1639 };