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