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