]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/_pakfire/pakfire.c
_pakfire: Implement loading keys
[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 PyObject* file = NULL;
623 int r;
624
625 // Parse arguments
626 if (!PyArg_ParseTuple(args, "O", &file))
627 return NULL;
628
629 // Treat the object as a file
630 FILE* f = PyObject_AsFileHandle(file, "r");
631 if (!f)
632 return NULL;
633
634 // Import the key
635 r = pakfire_key_import(&key, self->pakfire, f);
636 if (r) {
637 PyErr_SetFromErrno(PyExc_OSError);
638 goto ERROR;
639 }
640
641 // Convert the key into a Key object
642 object = new_key(&KeyType, key);
643 if (!object)
644 goto ERROR;
645
646 ERROR:
647 if (key)
648 pakfire_key_unref(key);
649 if (f)
650 fclose(f);
651
652 return object;
653 }
654
655 static PyObject* Pakfire_whatprovides(PakfireObject* self, PyObject* args) {
656 const char* provides = NULL;
657 struct pakfire_packagelist* list = NULL;
658 PyObject* ret = NULL;
659 int r;
660
661 if (!PyArg_ParseTuple(args, "s", &provides))
662 return NULL;
663
664 // Create a new list
665 r = pakfire_packagelist_create(&list, self->pakfire);
666 if (r) {
667 PyErr_SetFromErrno(PyExc_OSError);
668 goto ERROR;
669 }
670
671 r = pakfire_whatprovides(self->pakfire, provides, 0, list);
672 if (r) {
673 PyErr_SetFromErrno(PyExc_OSError);
674 goto ERROR;
675 }
676
677 // Create a Python list from the package list
678 ret = PyList_FromPackageList(list);
679
680 ERROR:
681 if (list)
682 pakfire_packagelist_unref(list);
683
684 return ret;
685 }
686
687 static PyObject* Pakfire_whatrequires(PakfireObject* self, PyObject* args) {
688 const char* requires = NULL;
689 struct pakfire_packagelist* list = NULL;
690 PyObject* ret = NULL;
691 int r;
692
693 if (!PyArg_ParseTuple(args, "s", &requires))
694 return NULL;
695
696 Py_BEGIN_ALLOW_THREADS
697
698 // Create a new list
699 r = pakfire_packagelist_create(&list, self->pakfire);
700 if (r) {
701 Py_BLOCK_THREADS
702 PyErr_SetFromErrno(PyExc_OSError);
703 goto ERROR;
704 }
705
706 r = pakfire_whatrequires(self->pakfire, requires, 0, list);
707 if (r) {
708 Py_BLOCK_THREADS
709 PyErr_SetFromErrno(PyExc_OSError);
710 goto ERROR;
711 }
712
713 Py_END_ALLOW_THREADS
714
715 // Create a Python list from the package list
716 ret = PyList_FromPackageList(list);
717
718 ERROR:
719 if (list)
720 pakfire_packagelist_unref(list);
721
722 return ret;
723 }
724
725 static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* kwds) {
726 char* kwlist[] = { "pattern", "name_only", NULL };
727 struct pakfire_packagelist* list = NULL;
728 const char* pattern = NULL;
729 int name_only = 0;
730 int flags = 0;
731 PyObject* ret = NULL;
732 int r;
733
734 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|$p", kwlist, &pattern, &name_only))
735 return NULL;
736
737 // Search for package names only
738 if (name_only)
739 flags |= PAKFIRE_SEARCH_NAME_ONLY;
740
741 r = pakfire_packagelist_create(&list, self->pakfire);
742 if (r) {
743 PyErr_SetFromErrno(PyExc_OSError);
744 goto ERROR;
745 }
746
747 r = pakfire_search(self->pakfire, pattern, flags, list);
748 if (r) {
749 PyErr_SetFromErrno(PyExc_OSError);
750 goto ERROR;
751 }
752
753 ret = PyList_FromPackageList(list);
754
755 ERROR:
756 if (list)
757 pakfire_packagelist_unref(list);
758
759 return ret;
760 }
761
762 static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
763 const char* evr1 = NULL;
764 const char* evr2 = NULL;
765
766 if (!PyArg_ParseTuple(args, "ss", &evr1, &evr2))
767 return NULL;
768
769 int cmp = pakfire_version_compare(self->pakfire, evr1, evr2);
770
771 return PyLong_FromLong(cmp);
772 }
773
774 static int Pakfire_execute_output_callback(struct pakfire* pakfire, void* data,
775 int priority, const char* line, size_t length) {
776 PyObject* callback = (PyObject*)data;
777 int r = 0;
778
779 // Do nothing if callback isn't set
780 if (!callback)
781 return 0;
782
783 // Translate priority to Python logging priorities
784 switch (priority) {
785 case LOG_INFO:
786 priority = 20;
787 break;
788
789 case LOG_ERR:
790 priority = 40;
791 break;
792 }
793
794 // Remove the trailing newline
795 if (line && line[length - 1] == '\n')
796 length--;
797
798 // Create tuple with arguments for the callback function
799 PyObject* args = Py_BuildValue("(is#)", priority, line, (Py_ssize_t)length);
800 if (!args)
801 return 1;
802
803 PyObject* result = PyObject_CallObject(callback, args);
804 if (result && PyLong_Check(result)) {
805 r = PyLong_AsLong(result);
806 }
807
808 Py_XDECREF(args);
809 Py_XDECREF(result);
810
811 return r;
812 }
813
814 static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwds) {
815 char* kwlist[] = {
816 "command",
817 "environ",
818 "bind",
819 "callback",
820 "nice",
821 NULL
822 };
823
824 struct pakfire_jail* jail = NULL;
825 const char** argv = NULL;
826 int r;
827 PyObject* ret = NULL;
828
829 PyObject* command = NULL;
830 PyObject* environ = NULL;
831 PyObject* bind = NULL;
832 PyObject* callback = NULL;
833 int nice = 0;
834
835 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOi", kwlist, &command, &environ,
836 &bind, &callback, &nice))
837 return NULL;
838
839 // Check if command is a list
840 if (!PyList_Check(command)) {
841 PyErr_SetString(PyExc_TypeError, "command must be a list");
842 goto ERROR;
843 }
844
845 const ssize_t command_length = PyList_Size(command);
846
847 // Check if command is not empty
848 if (command_length == 0) {
849 PyErr_SetString(PyExc_ValueError, "command is empty");
850 goto ERROR;
851 }
852
853 // Allocate argv
854 argv = calloc(command_length + 1, sizeof(*argv));
855 if (!argv)
856 goto ERROR;
857
858 // All arguments in command must be strings
859 for (unsigned int i = 0; i < command_length; i++) {
860 PyObject* item = PyList_GET_ITEM(command, i);
861
862 if (!PyUnicode_Check(item)) {
863 PyErr_Format(PyExc_TypeError, "Item %u in command is not a string", i);
864 return NULL;
865 }
866
867 // Copy to argv
868 argv[i] = PyUnicode_AsUTF8(item);
869 }
870
871 // Check if bind is a sequence
872 if (bind && !PySequence_Check(bind)) {
873 PyErr_SetString(PyExc_ValueError, "bind is not a sequence");
874 goto ERROR;
875 }
876
877 // Check callback
878 if (callback && !PyCallable_Check(callback)) {
879 PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
880 goto ERROR;
881 }
882
883 // Create jail
884 r = pakfire_jail_create(&jail, self->pakfire);
885 if (r) {
886 PyErr_SetFromErrno(PyExc_OSError);
887 goto ERROR;
888 }
889
890 // Check callback
891 if (callback && !PyCallable_Check(callback)) {
892 PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
893 goto ERROR;
894 }
895
896 // Set nice
897 if (nice) {
898 r = pakfire_jail_nice(jail, nice);
899 if (r) {
900 PyErr_SetFromErrno(PyExc_OSError);
901 goto ERROR;
902 }
903 }
904
905 PyObject* key = NULL;
906 PyObject* value = NULL;
907 Py_ssize_t p = 0;
908
909 // Parse the environment
910 if (environ) {
911 // Check if environ is a dictionary
912 if (!PyDict_Check(environ)) {
913 PyErr_SetString(PyExc_TypeError, "environ must be a dictionary");
914 goto ERROR;
915 }
916
917 // All keys and values must be strings
918 while (PyDict_Next(environ, &p, &key, &value)) {
919 if (!PyUnicode_Check(key) || !PyUnicode_Check(value)) {
920 PyErr_SetString(PyExc_TypeError, "Environment contains a non-string object");
921 goto ERROR;
922 }
923
924 // Set environment value
925 r = pakfire_jail_set_env(jail, PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));
926 if (r) {
927 PyErr_SetFromErrno(PyExc_OSError);
928 goto ERROR;
929 }
930 }
931 }
932
933 const Py_ssize_t num_bind = PySequence_Length(bind);
934
935 // Bind
936 for (unsigned int i = 0; i < num_bind; i++) {
937 PyObject* b = PySequence_ITEM(bind, i);
938 if (!b)
939 goto ERROR;
940
941 // Check if this is a Unicode object
942 if (!PyUnicode_Check(b)) {
943 PyErr_SetString(PyExc_ValueError, "bind contains a non-Unicode object");
944 Py_DECREF(b);
945 goto ERROR;
946 }
947
948 const char* path = PyUnicode_AsUTF8(b);
949
950 // Perform bind
951 r = pakfire_jail_bind(jail, path, path, 0);
952 if (r) {
953 PyErr_SetFromErrno(PyExc_OSError);
954 Py_DECREF(b);
955 goto ERROR;
956 }
957
958 Py_DECREF(b);
959 }
960
961 Py_BEGIN_ALLOW_THREADS
962
963 // Execute command
964 r = pakfire_jail_exec(jail, argv,
965 NULL, Pakfire_execute_output_callback, callback, 0);
966
967 Py_END_ALLOW_THREADS
968
969 // If the return code was negative, we had some internal error
970 if (r < 0) {
971 PyErr_SetFromErrno(PyExc_OSError);
972 goto ERROR;
973
974 // Otherwise the executed command returned some error code
975 } else if (r > 0) {
976 PyObject* code = PyLong_FromLong(r);
977
978 // Raise CommandExecutionError
979 PyErr_SetObject(PyExc_CommandExecutionError, code);
980 Py_DECREF(code);
981
982 goto ERROR;
983 }
984
985 // The process has exited successfully
986
987 // Return None
988 ret = Py_None;
989 Py_INCREF(ret);
990
991 ERROR:
992 if (argv)
993 free(argv);
994 if (jail)
995 pakfire_jail_unref(jail);
996
997 return ret;
998 }
999
1000 static PyObject* Pakfire_dist(PakfireObject* self, PyObject* args) {
1001 const char* path = NULL;
1002 const char* target = NULL;
1003 char* result = NULL;
1004 int r;
1005
1006 if (!PyArg_ParseTuple(args, "s|z", &path, &target))
1007 return NULL;
1008
1009 Py_BEGIN_ALLOW_THREADS
1010
1011 r = pakfire_dist(self->pakfire, path, target, &result);
1012 if (r) {
1013 Py_BLOCK_THREADS
1014 PyErr_SetFromErrno(PyExc_OSError);
1015 return NULL;
1016 }
1017
1018 Py_END_ALLOW_THREADS
1019
1020 PyObject* ret = PyUnicode_FromString(result);
1021 free(result);
1022
1023 return ret;
1024 }
1025
1026 static PyObject* Pakfire_copy_in(PakfireObject* self, PyObject* args) {
1027 const char* src = NULL;
1028 const char* dst = NULL;
1029
1030 if (!PyArg_ParseTuple(args, "ss", &src, &dst))
1031 return NULL;
1032
1033 Py_BEGIN_ALLOW_THREADS
1034
1035 int r = pakfire_copy_in(self->pakfire, src, dst);
1036 if (r) {
1037 Py_BLOCK_THREADS
1038 PyErr_SetFromErrno(PyExc_OSError);
1039 return NULL;
1040 }
1041
1042 Py_END_ALLOW_THREADS
1043
1044 Py_RETURN_NONE;
1045 }
1046
1047 static PyObject* Pakfire_copy_out(PakfireObject* self, PyObject* args) {
1048 const char* src = NULL;
1049 const char* dst = NULL;
1050
1051 if (!PyArg_ParseTuple(args, "ss", &src, &dst))
1052 return NULL;
1053
1054 Py_BEGIN_ALLOW_THREADS
1055
1056 int r = pakfire_copy_out(self->pakfire, src, dst);
1057 if (r) {
1058 Py_BLOCK_THREADS
1059 PyErr_SetFromErrno(PyExc_OSError);
1060 return NULL;
1061 }
1062
1063 Py_END_ALLOW_THREADS
1064
1065 Py_RETURN_NONE;
1066 }
1067
1068 static PyObject* Pakfire_get_repos(PakfireObject* self) {
1069 struct pakfire_repolist* repos = pakfire_get_repos(self->pakfire);
1070 if (!repos) {
1071 PyErr_SetFromErrno(PyExc_OSError);
1072 return NULL;
1073 }
1074
1075 const size_t l = pakfire_repolist_size(repos);
1076
1077 PyObject* list = PyList_New(l);
1078 if (!list)
1079 goto ERROR;
1080
1081 for (unsigned int i = 0; i < l; i++) {
1082 struct pakfire_repo* repo = pakfire_repolist_get(repos, i);
1083 if (!repo)
1084 continue;
1085
1086 PyObject* obj = new_repo(&RepoType, repo);
1087 PyList_SET_ITEM(list, i, obj);
1088
1089 pakfire_repo_unref(repo);
1090 }
1091
1092 ERROR:
1093 pakfire_repolist_unref(repos);
1094
1095 return list;
1096 }
1097
1098 static PyObject* execute_return_value(int r) {
1099 // Raise an OS error if r < 0
1100 if (r < 0) {
1101 errno = -r;
1102
1103 PyErr_SetFromErrno(PyExc_OSError);
1104 return NULL;
1105
1106 // Raise exception when the command failed
1107 } else if (r > 0) {
1108 PyObject* code = PyLong_FromLong(r);
1109
1110 PyErr_SetObject(PyExc_CommandExecutionError, code);
1111 Py_DECREF(code);
1112
1113 return NULL;
1114 }
1115
1116 Py_RETURN_NONE;
1117 }
1118
1119 static PyObject* Pakfire_build(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1120 char* kwlist[] = {
1121 "path",
1122 "target",
1123 "build_id",
1124 "ccache_path",
1125 "interactive",
1126 "disable_snapshot",
1127 "disable_ccache",
1128 "disable_tests",
1129 NULL,
1130 };
1131 struct pakfire_build* build = NULL;
1132 const char* path = NULL;
1133 const char* target = NULL;
1134 const char* build_id = NULL;
1135 const char* ccache_path = NULL;
1136 int interactive = 0;
1137 int disable_snapshot = 0;
1138 int disable_ccache = 0;
1139 int disable_tests = 0;
1140 int r;
1141
1142 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzzpppp", kwlist,
1143 &path, &target, &build_id, &ccache_path, &interactive,
1144 &disable_snapshot, &disable_ccache, &disable_tests))
1145 return NULL;
1146
1147 int flags = 0;
1148
1149 if (interactive)
1150 flags |= PAKFIRE_BUILD_INTERACTIVE;
1151
1152 // Disable snapshot if requested
1153 if (disable_snapshot)
1154 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
1155
1156 // Disable ccache if requested
1157 if (disable_ccache)
1158 flags |= PAKFIRE_BUILD_DISABLE_CCACHE;
1159
1160 // Disable tests if requested
1161 if (disable_tests)
1162 flags |= PAKFIRE_BUILD_DISABLE_TESTS;
1163
1164 // Create a new build environment
1165 r = pakfire_build_create(&build, self->pakfire, build_id, flags);
1166 if (r) {
1167 PyErr_SetFromErrno(PyExc_OSError);
1168 goto ERROR;
1169 }
1170
1171 // Set target
1172 if (target) {
1173 r = pakfire_build_set_target(build, target);
1174 if (r) {
1175 PyErr_SetFromErrno(PyExc_OSError);
1176 goto ERROR;
1177 }
1178 }
1179
1180 // Set ccache path
1181 if (ccache_path) {
1182 r = pakfire_build_set_ccache_path(build, ccache_path);
1183 if (r) {
1184 PyErr_SetFromErrno(PyExc_OSError);
1185 goto ERROR;
1186 }
1187 }
1188
1189 Py_BEGIN_ALLOW_THREADS
1190
1191 // Run build
1192 r = pakfire_build_exec(build, path);
1193 if (r) {
1194 Py_BLOCK_THREADS;
1195
1196 if (r < 0) {
1197 PyErr_SetFromErrno(PyExc_OSError);
1198
1199 // Raise a command execution error
1200 } else {
1201 PyObject* code = PyLong_FromLong(r);
1202
1203 PyErr_SetObject(PyExc_CommandExecutionError, code);
1204 Py_DECREF(code);
1205 }
1206
1207 goto ERROR;
1208 }
1209
1210 Py_END_ALLOW_THREADS
1211
1212 ERROR:
1213 if (build)
1214 pakfire_build_unref(build);
1215
1216 if (r)
1217 return NULL;
1218
1219 Py_RETURN_NONE;
1220 }
1221
1222 static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1223 char* kwlist[] = {
1224 "install",
1225 "disable_snapshot",
1226 NULL,
1227 };
1228 char** packages = NULL;
1229 int disable_snapshot = 0;
1230 int r;
1231
1232 // Parse everything
1233 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&p", kwlist,
1234 convert_packages, &packages, &disable_snapshot))
1235 return NULL;
1236
1237 int flags = 0;
1238
1239 if (disable_snapshot)
1240 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
1241
1242 Py_BEGIN_ALLOW_THREADS
1243
1244 r = pakfire_shell(self->pakfire, (const char**)packages, flags);
1245
1246 Py_END_ALLOW_THREADS
1247
1248 return execute_return_value(r);
1249 }
1250
1251 static PyObject* Pakfire_clean(PakfireObject* self) {
1252 int r;
1253
1254 Py_BEGIN_ALLOW_THREADS
1255
1256 r = pakfire_clean(self->pakfire, 0);
1257 if (r) {
1258 Py_BLOCK_THREADS
1259 PyErr_SetFromErrno(PyExc_OSError);
1260 return NULL;
1261 }
1262
1263 Py_END_ALLOW_THREADS
1264
1265 Py_RETURN_NONE;
1266 }
1267
1268 static PyObject* Pakfire_refresh(PakfireObject* self, PyObject* args) {
1269 int force = 0;
1270 int r;
1271
1272 if (!PyArg_ParseTuple(args, "|p", &force))
1273 return NULL;
1274
1275 Py_BEGIN_ALLOW_THREADS
1276
1277 r = pakfire_refresh(self->pakfire, force);
1278 if (r) {
1279 Py_BLOCK_THREADS
1280 PyErr_SetFromErrno(PyExc_OSError);
1281 return NULL;
1282 }
1283
1284 Py_END_ALLOW_THREADS
1285
1286 Py_RETURN_NONE;
1287 }
1288
1289 static PyObject* Pakfire_check(PakfireObject* self) {
1290 struct pakfire_filelist* errors = NULL;
1291 int r;
1292
1293 // Allocate a filelist for errors
1294 r = pakfire_filelist_create(&errors, self->pakfire);
1295 if (r)
1296 goto ERROR;
1297
1298 Py_BEGIN_ALLOW_THREADS
1299
1300 // Perform check
1301 r = pakfire_check(self->pakfire, errors);
1302
1303 Py_END_ALLOW_THREADS
1304
1305 if (r)
1306 goto ERROR;
1307
1308 const size_t num_errors = pakfire_filelist_length(errors);
1309
1310 // Did we find any errors?
1311 if (num_errors) {
1312 PyObject* _errors = PyList_FromFileList(errors);
1313 if (!_errors)
1314 goto ERROR;
1315
1316 pakfire_filelist_unref(errors);
1317
1318 // Raise CheckFileVerificationError
1319 PyErr_SetObject(PyExc_CheckFileVerificationError, _errors);
1320 Py_DECREF(_errors);
1321 return NULL;
1322 }
1323
1324 pakfire_filelist_unref(errors);
1325 Py_RETURN_NONE;
1326
1327 ERROR:
1328 // Cleanup
1329 if (errors)
1330 pakfire_filelist_unref(errors);
1331
1332 // Raise exception from errno
1333 PyErr_SetFromErrno(PyExc_OSError);
1334 return NULL;
1335 }
1336
1337 static PyObject* Pakfire_sync(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1338 char* kwlist[] = {
1339 "keep_orphaned",
1340 "status_callback",
1341 NULL,
1342 };
1343 int keep_orphaned = 0;
1344 int flags = 0;
1345 PyObject* status_callback = NULL;
1346 int r;
1347
1348 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|$pO", kwlist,
1349 &keep_orphaned, &status_callback))
1350 return NULL;
1351
1352 if (keep_orphaned)
1353 flags |= PAKFIRE_REQUEST_KEEP_ORPHANED;
1354
1355 Py_BEGIN_ALLOW_THREADS
1356
1357 r = pakfire_sync(self->pakfire, 0, flags, NULL,
1358 Pakfire_status_callback, status_callback);
1359 if (r) {
1360 Py_BLOCK_THREADS
1361 PyErr_SetFromErrno(PyExc_OSError);
1362 return NULL;
1363 }
1364
1365 Py_END_ALLOW_THREADS
1366
1367 Py_RETURN_NONE;
1368 }
1369
1370 static PyObject* Pakfire_open(PakfireObject* self, PyObject* args) {
1371 struct pakfire_archive* archive = NULL;
1372 const char* path = NULL;
1373
1374 if (!PyArg_ParseTuple(args, "s", &path))
1375 return NULL;
1376
1377 Py_BEGIN_ALLOW_THREADS
1378
1379 int r = pakfire_archive_open(&archive, self->pakfire, path);
1380 if (r) {
1381 Py_BLOCK_THREADS
1382 PyErr_SetFromErrno(PyExc_OSError);
1383 return NULL;
1384 }
1385
1386 Py_END_ALLOW_THREADS
1387
1388 // Create Python object
1389 PyObject* object = new_archive(&ArchiveType, archive);
1390 pakfire_archive_unref(archive);
1391
1392 return object;
1393 }
1394
1395 static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObject* kwargs) {
1396 char* kwlist[] = { "path", "files", "key", NULL };
1397 const char* path = NULL;
1398 PyObject* list = NULL;
1399 KeyObject* key = NULL;
1400
1401 PyObject* ret = NULL;
1402
1403 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!", kwlist, &path, &list, &KeyType, &key))
1404 return NULL;
1405
1406 // List must be a sequence
1407 if (!PySequence_Check(list)) {
1408 PyErr_SetString(PyExc_ValueError, "Expected a sequence.");
1409 return NULL;
1410 }
1411
1412 // How many new files do we have?
1413 ssize_t num_files = PySequence_Length(list);
1414 if (num_files < 0)
1415 return NULL;
1416
1417 // Allocate files array
1418 const char** files = calloc(num_files + 1, sizeof(*files));
1419 if (!files) {
1420 PyErr_SetFromErrno(PyExc_OSError);
1421 return NULL;
1422 }
1423
1424 for (int i = 0; i < num_files; i++) {
1425 PyObject* file = PySequence_GetItem(list, i);
1426 if (!file)
1427 goto ERROR;
1428
1429 // Check if file is a Unicode object
1430 if (!PyUnicode_Check(file)) {
1431 PyErr_SetString(PyExc_ValueError, "Expected a string.");
1432 goto ERROR;
1433 }
1434
1435 // Add pointer to string to files array
1436 files[i] = PyUnicode_AsUTF8(file);
1437 if (!files[i])
1438 goto ERROR;
1439
1440 Py_DECREF(file);
1441 }
1442
1443 Py_BEGIN_ALLOW_THREADS
1444
1445 int r = pakfire_repo_compose(self->pakfire, path, (key) ? key->key : NULL, files);
1446 if (r) {
1447 Py_BLOCK_THREADS
1448 PyErr_SetFromErrno(PyExc_OSError);
1449 return NULL;
1450 }
1451
1452 Py_END_ALLOW_THREADS
1453
1454 // Return None on success
1455 ret = Py_None;
1456 Py_INCREF(ret);
1457
1458 ERROR:
1459 if (files)
1460 free(files);
1461
1462 return ret;
1463 }
1464
1465 static struct PyMethodDef Pakfire_methods[] = {
1466 {
1467 "build",
1468 (PyCFunction)Pakfire_build,
1469 METH_VARARGS|METH_KEYWORDS,
1470 NULL
1471 },
1472 {
1473 "check",
1474 (PyCFunction)Pakfire_check,
1475 METH_NOARGS,
1476 NULL,
1477 },
1478 {
1479 "clean",
1480 (PyCFunction)Pakfire_clean,
1481 METH_NOARGS,
1482 NULL,
1483 },
1484 {
1485 "copy_in",
1486 (PyCFunction)Pakfire_copy_in,
1487 METH_VARARGS,
1488 NULL,
1489 },
1490 {
1491 "copy_out",
1492 (PyCFunction)Pakfire_copy_out,
1493 METH_VARARGS,
1494 NULL,
1495 },
1496 {
1497 "dist",
1498 (PyCFunction)Pakfire_dist,
1499 METH_VARARGS,
1500 NULL
1501 },
1502 {
1503 "erase",
1504 (PyCFunction)Pakfire_erase,
1505 METH_VARARGS|METH_KEYWORDS,
1506 NULL
1507 },
1508 {
1509 "execute",
1510 (PyCFunction)Pakfire_execute,
1511 METH_VARARGS|METH_KEYWORDS,
1512 NULL
1513 },
1514 {
1515 "generate_key",
1516 (PyCFunction)Pakfire_generate_key,
1517 METH_VARARGS|METH_KEYWORDS,
1518 NULL
1519 },
1520 {
1521 "get_repo",
1522 (PyCFunction)Pakfire_get_repo,
1523 METH_VARARGS,
1524 NULL
1525 },
1526 {
1527 "import_key",
1528 (PyCFunction)Pakfire_import_key,
1529 METH_VARARGS,
1530 NULL,
1531 },
1532 {
1533 "install",
1534 (PyCFunction)Pakfire_install,
1535 METH_VARARGS|METH_KEYWORDS,
1536 NULL,
1537 },
1538 {
1539 "open",
1540 (PyCFunction)Pakfire_open,
1541 METH_VARARGS,
1542 NULL
1543 },
1544 {
1545 "refresh",
1546 (PyCFunction)Pakfire_refresh,
1547 METH_VARARGS,
1548 NULL,
1549 },
1550 {
1551 "repo_compose",
1552 (PyCFunction)Pakfire_repo_compose,
1553 METH_VARARGS|METH_KEYWORDS,
1554 NULL
1555 },
1556 {
1557 "search",
1558 (PyCFunction)Pakfire_search,
1559 METH_VARARGS|METH_KEYWORDS,
1560 NULL
1561 },
1562 {
1563 "shell",
1564 (PyCFunction)Pakfire_shell,
1565 METH_VARARGS|METH_KEYWORDS,
1566 NULL,
1567 },
1568 {
1569 "sync",
1570 (PyCFunction)Pakfire_sync,
1571 METH_VARARGS|METH_KEYWORDS,
1572 NULL,
1573 },
1574 {
1575 "update",
1576 (PyCFunction)Pakfire_update,
1577 METH_VARARGS|METH_KEYWORDS,
1578 NULL
1579 },
1580 {
1581 "version_compare",
1582 (PyCFunction)Pakfire_version_compare,
1583 METH_VARARGS,
1584 NULL
1585 },
1586 {
1587 "whatprovides",
1588 (PyCFunction)Pakfire_whatprovides,
1589 METH_VARARGS,
1590 NULL
1591 },
1592 {
1593 "whatrequires",
1594 (PyCFunction)Pakfire_whatrequires,
1595 METH_VARARGS,
1596 NULL
1597 },
1598 { NULL },
1599 };
1600
1601 static struct PyGetSetDef Pakfire_getsetters[] = {
1602 {
1603 "arch",
1604 (getter)Pakfire_get_arch,
1605 NULL,
1606 NULL,
1607 NULL
1608 },
1609 {
1610 "path",
1611 (getter)Pakfire_get_path,
1612 NULL,
1613 NULL,
1614 NULL
1615 },
1616 {
1617 "repos",
1618 (getter)Pakfire_get_repos,
1619 NULL,
1620 NULL,
1621 NULL
1622 },
1623 { NULL },
1624 };
1625
1626 PyTypeObject PakfireType = {
1627 PyVarObject_HEAD_INIT(NULL, 0)
1628 tp_name: "_pakfire.Pakfire",
1629 tp_basicsize: sizeof(PakfireObject),
1630 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
1631 tp_new: Pakfire_new,
1632 tp_dealloc: (destructor)Pakfire_dealloc,
1633 tp_init: (initproc)Pakfire_init,
1634 tp_doc: "Pakfire object",
1635 tp_methods: Pakfire_methods,
1636 tp_getset: Pakfire_getsetters,
1637 tp_repr: (reprfunc)Pakfire_repr,
1638 };