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