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