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