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