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