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