]> git.ipfire.org Git - pakfire.git/blame - src/_pakfire/pakfire.c
python: Make sure we hold the GIL when jumping into the logging callback
[pakfire.git] / src / _pakfire / pakfire.c
CommitLineData
6e46b18e
MT
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
16495755 21#define PY_SSIZE_T_CLEAN
6e46b18e 22#include <Python.h>
03b9652a 23#include <errno.h>
b1a49fcc 24#include <syslog.h>
6e46b18e 25
36c98595 26#include <pakfire/archive.h>
1a276007 27#include <pakfire/build.h>
9d6b128a 28#include <pakfire/constants.h>
f2a8f4f2 29#include <pakfire/ctx.h>
af41db7e 30#include <pakfire/dist.h>
deeb4e91 31#include <pakfire/jail.h>
bfa112c3 32#include <pakfire/logging.h>
163851bc 33#include <pakfire/mount.h>
178a4506 34#include <pakfire/packagelist.h>
6e46b18e 35#include <pakfire/pakfire.h>
ad62bb07 36#include <pakfire/key.h>
843fcc66 37#include <pakfire/repo.h>
78cc8800 38#include <pakfire/repolist.h>
a693a077 39#include <pakfire/transaction.h>
b31be147 40#include <pakfire/util.h>
6e46b18e 41
36c98595 42#include "archive.h"
3dd7ad1e 43#include "ctx.h"
9d6b128a 44#include "errors.h"
ad62bb07 45#include "key.h"
6e46b18e 46#include "pakfire.h"
843fcc66 47#include "repo.h"
f989dacd 48#include "util.h"
6e46b18e
MT
49
50static PyObject* Pakfire_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
51 PakfireObject* self = (PakfireObject *)type->tp_alloc(type, 0);
3dd7ad1e
MT
52 if (self) {
53 self->ctx = NULL;
6e46b18e 54 self->pakfire = NULL;
3dd7ad1e 55 }
45cbf6f4 56
6e46b18e
MT
57 return (PyObject *)self;
58}
59
60static int Pakfire_init(PakfireObject* self, PyObject* args, PyObject* kwds) {
09f1436a 61 char* kwlist[] = {
3dd7ad1e 62 "ctx",
09f1436a
MT
63 "path",
64 "arch",
09f1436a 65 "conf",
09f1436a
MT
66 NULL,
67 };
3dd7ad1e 68 CtxObject* ctx = NULL;
6e46b18e 69 const char* path = NULL;
72caad76 70 const char* arch = NULL;
7b6bb645 71 PyObject* conf = Py_None;
509aaad2 72 int r = 1;
6e46b18e 73
509aaad2
MT
74 FILE* fconf = NULL;
75
3dd7ad1e
MT
76 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|zzO", kwlist,
77 &CtxType, &ctx, &path, &arch, &conf))
509aaad2 78 goto ERROR;
0e45f213 79
509aaad2 80 // Map the configuration
7b6bb645 81 if (conf != Py_None) {
509aaad2
MT
82 fconf = PyObject_AsFileHandle(conf, "r");
83 if (!fconf)
84 goto ERROR;
d6409945
MT
85 }
86
af894b8d 87 int flags = 0;
26452aef 88
a853ec56
MT
89 Py_BEGIN_ALLOW_THREADS
90
3dd7ad1e
MT
91 // Store a reference to the context
92 self->ctx = pakfire_ctx_ref(ctx->ctx);
93
34c0706d 94 // Create a new Pakfire instance
3dd7ad1e 95 r = pakfire_create(&self->pakfire, self->ctx, path, arch, fconf, flags);
a853ec56
MT
96
97 Py_END_ALLOW_THREADS
98
0b8f80bb
MT
99 if (r < 0) {
100 errno = -r;
101
f4e91beb
MT
102 switch (errno) {
103 // Invalid architecture or path
104 case EINVAL:
d971fe19 105 PyErr_SetString(PyExc_ValueError, "Invalid architecture or path");
03b9652a
MT
106 break;
107
108 // Anything else
109 default:
f4e91beb 110 PyErr_SetFromErrno(PyExc_OSError);
03b9652a 111 }
6e46b18e 112
509aaad2
MT
113 r = -1;
114 goto ERROR;
03b9652a
MT
115 }
116
509aaad2
MT
117ERROR:
118 if (fconf)
119 fclose(fconf);
120
121 return -r;
6e46b18e
MT
122}
123
124static void Pakfire_dealloc(PakfireObject* self) {
d122626c 125 if (self->pakfire) {
a853ec56
MT
126 Py_BEGIN_ALLOW_THREADS
127
d122626c 128 pakfire_unref(self->pakfire);
a853ec56
MT
129
130 Py_END_ALLOW_THREADS
d122626c 131 }
3dd7ad1e
MT
132 if (self->ctx)
133 pakfire_ctx_unref(self->ctx);
a02be46b 134
6e46b18e
MT
135 Py_TYPE(self)->tp_free((PyObject *)self);
136}
137
138static PyObject* Pakfire_repr(PakfireObject* self) {
a853ec56
MT
139 const char* path = pakfire_get_path(self->pakfire);
140 const char* arch = pakfire_get_arch(self->pakfire);
6e46b18e
MT
141
142 return PyUnicode_FromFormat("<_pakfire.Pakfire %s (%s)>", path, arch);
143}
144
145static PyObject* Pakfire_get_path(PakfireObject* self) {
a853ec56 146 const char* path = pakfire_get_path(self->pakfire);
6e46b18e 147
a853ec56 148 return PyUnicode_FromString(path);
6e46b18e
MT
149}
150
151static PyObject* Pakfire_get_arch(PakfireObject* self) {
a853ec56 152 const char* arch = pakfire_get_arch(self->pakfire);
6e46b18e 153
a853ec56 154 return PyUnicode_FromString(arch);
6e46b18e
MT
155}
156
8ef6c388
MT
157static PyObject* Pakfire_get_repo(PakfireObject* self, PyObject* args) {
158 const char* name = NULL;
159
160 if (!PyArg_ParseTuple(args, "s", &name))
161 return NULL;
162
4651122b 163 struct pakfire_repo* repo = pakfire_get_repo(self->pakfire, name);
8ef6c388
MT
164 if (!repo)
165 Py_RETURN_NONE;
166
167 PyObject* obj = new_repo(&RepoType, repo);
168 pakfire_repo_unref(repo);
169
170 return obj;
171}
172
b7e72fe2
MT
173static int convert_packages(PyObject* object, void* address) {
174 char*** packages = (char***)address;
175
176 // Called for cleanup
177 if (!object)
178 goto ERROR;
179
67a3604c
MT
180 // Nothing to do when object is None
181 if (object == Py_None)
182 return Py_CLEANUP_SUPPORTED;
183
b7e72fe2
MT
184 if (!PySequence_Check(object)) {
185 PyErr_SetString(PyExc_ValueError, "Packages must be a sequence");
186 goto ERROR;
187 }
188
189 const unsigned int length = PySequence_Length(object);
cb3f13aa
MT
190 if (!length)
191 return Py_CLEANUP_SUPPORTED;
b7e72fe2
MT
192
193 // Allocate array
194 *packages = calloc(length + 1, sizeof(*packages));
195 if (!*packages) {
196 PyErr_SetFromErrno(PyExc_OSError);
197 goto ERROR;
198 }
199
200 for (unsigned int i = 0; i < length; i++) {
201 PyObject* item = PySequence_GetItem(object, i);
202
203 // Check if input is a string
204 if (!PyUnicode_Check(item)) {
205 Py_DECREF(item);
206
207 PyErr_SetString(PyExc_AttributeError, "Expected a string");
208 goto ERROR;
209 }
210
211 // Fetch string
212 const char* package = PyUnicode_AsUTF8(item);
213 if (!package) {
214 Py_DECREF(item);
215 goto ERROR;
216 }
217
218 // Add package to array
9236f1b9
MT
219 (*packages)[i] = strdup(package);
220 if (!(*packages)[i]) {
b7e72fe2
MT
221 Py_DECREF(item);
222 goto ERROR;
223 }
224
225 Py_DECREF(item);
226 }
227
228 // Success
229 return Py_CLEANUP_SUPPORTED;
230
231ERROR:
232 if (*packages) {
233 for (char** package = *packages; *package; package++)
234 free(*package);
235 free(*packages);
236 }
237
238 return 0;
239}
240
866d96ff
MT
241/*
242 XXX This could be moved out of here as this no longer depends on Pakfire
243*/
f54744b1 244static PyObject* Pakfire_generate_key(PakfireObject* self, PyObject* args, PyObject* kwds) {
391eeca0 245 char* kwlist[] = { "algorithm", "comment", NULL };
f54744b1 246 struct pakfire_key* key = NULL;
b8effd38 247 pakfire_key_algo_t algo = PAKFIRE_KEY_ALGO_NULL;
391eeca0 248 const char* comment = NULL;
ad62bb07 249
391eeca0 250 if (!PyArg_ParseTupleAndKeywords(args, kwds, "is", kwlist, &algo, &comment))
ad62bb07
MT
251 return NULL;
252
f54744b1 253 // Generate a new key
3dd7ad1e 254 int r = pakfire_key_generate(&key, self->ctx, algo, comment);
f54744b1
MT
255 if (r) {
256 PyErr_SetFromErrno(PyExc_OSError);
257 return NULL;
258 }
ad62bb07 259
116a426f
MT
260 PyObject* object = new_key(&KeyType, key);
261 pakfire_key_unref(key);
262
263 return object;
ad62bb07 264}
866d96ff
MT
265/*
266 XXX This could be moved out of here as this no longer depends on Pakfire
267*/
44cbdc0a
MT
268static PyObject* Pakfire_import_key(PakfireObject* self, PyObject* args) {
269 struct pakfire_key* key = NULL;
270 PyObject* object = NULL;
4636b5f2
MT
271 char* data = NULL;
272 Py_ssize_t data_length = 0;
44cbdc0a
MT
273 int r;
274
275 // Parse arguments
4636b5f2 276 if (!PyArg_ParseTuple(args, "s#", &data, &data_length))
44cbdc0a
MT
277 return NULL;
278
4636b5f2
MT
279 // Map the object
280 FILE* f = fmemopen(data, data_length, "r");
44cbdc0a
MT
281 if (!f)
282 return NULL;
283
284 // Import the key
3dd7ad1e 285 r = pakfire_key_import(&key, self->ctx, f);
44cbdc0a
MT
286 if (r) {
287 PyErr_SetFromErrno(PyExc_OSError);
288 goto ERROR;
289 }
290
291 // Convert the key into a Key object
292 object = new_key(&KeyType, key);
293 if (!object)
294 goto ERROR;
295
296ERROR:
297 if (key)
298 pakfire_key_unref(key);
299 if (f)
300 fclose(f);
301
302 return object;
303}
304
54334355
MT
305static PyObject* Pakfire_whatprovides(PakfireObject* self, PyObject* args) {
306 const char* provides = NULL;
307 struct pakfire_packagelist* list = NULL;
79bdcddf
MT
308 PyObject* ret = NULL;
309 int r;
f989dacd 310
54334355 311 if (!PyArg_ParseTuple(args, "s", &provides))
f989dacd
MT
312 return NULL;
313
79bdcddf 314 // Create a new list
3dd7ad1e 315 r = pakfire_packagelist_create(&list, self->ctx);
54334355
MT
316 if (r) {
317 PyErr_SetFromErrno(PyExc_OSError);
79bdcddf 318 goto ERROR;
54334355 319 }
f989dacd 320
79bdcddf
MT
321 r = pakfire_whatprovides(self->pakfire, provides, 0, list);
322 if (r) {
323 PyErr_SetFromErrno(PyExc_OSError);
324 goto ERROR;
325 }
178a4506 326
79bdcddf
MT
327 // Create a Python list from the package list
328 ret = PyList_FromPackageList(list);
329
330ERROR:
331 if (list)
332 pakfire_packagelist_unref(list);
333
334 return ret;
f989dacd
MT
335}
336
8201cef2
MT
337static PyObject* Pakfire_whatrequires(PakfireObject* self, PyObject* args) {
338 const char* requires = NULL;
b055ddf1 339 struct pakfire_packagelist* list = NULL;
79bdcddf
MT
340 PyObject* ret = NULL;
341 int r;
8201cef2
MT
342
343 if (!PyArg_ParseTuple(args, "s", &requires))
344 return NULL;
345
a853ec56
MT
346 Py_BEGIN_ALLOW_THREADS
347
79bdcddf 348 // Create a new list
3dd7ad1e 349 r = pakfire_packagelist_create(&list, self->ctx);
b055ddf1 350 if (r) {
a853ec56 351 Py_BLOCK_THREADS
b055ddf1 352 PyErr_SetFromErrno(PyExc_OSError);
79bdcddf 353 goto ERROR;
b055ddf1 354 }
8201cef2 355
4a25eca3 356 r = pakfire_whatrequires(self->pakfire, requires, 0, list);
79bdcddf 357 if (r) {
a853ec56 358 Py_BLOCK_THREADS
79bdcddf
MT
359 PyErr_SetFromErrno(PyExc_OSError);
360 goto ERROR;
361 }
8201cef2 362
a853ec56
MT
363 Py_END_ALLOW_THREADS
364
79bdcddf
MT
365 // Create a Python list from the package list
366 ret = PyList_FromPackageList(list);
367
368ERROR:
369 if (list)
370 pakfire_packagelist_unref(list);
371
372 return ret;
8201cef2
MT
373}
374
4952c631
MT
375static PyObject* Pakfire_search(PakfireObject* self, PyObject* args, PyObject* kwds) {
376 char* kwlist[] = { "pattern", "name_only", NULL };
689b4aca 377 struct pakfire_packagelist* list = NULL;
4952c631
MT
378 const char* pattern = NULL;
379 int name_only = 0;
380 int flags = 0;
644d2660
MT
381 PyObject* ret = NULL;
382 int r;
f989dacd 383
4952c631 384 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|$p", kwlist, &pattern, &name_only))
f989dacd
MT
385 return NULL;
386
4952c631
MT
387 // Search for package names only
388 if (name_only)
389 flags |= PAKFIRE_SEARCH_NAME_ONLY;
390
3dd7ad1e 391 r = pakfire_packagelist_create(&list, self->ctx);
689b4aca
MT
392 if (r) {
393 PyErr_SetFromErrno(PyExc_OSError);
644d2660 394 goto ERROR;
689b4aca 395 }
178a4506 396
644d2660
MT
397 r = pakfire_search(self->pakfire, pattern, flags, list);
398 if (r) {
399 PyErr_SetFromErrno(PyExc_OSError);
400 goto ERROR;
401 }
178a4506 402
644d2660
MT
403 ret = PyList_FromPackageList(list);
404
405ERROR:
406 if (list)
407 pakfire_packagelist_unref(list);
408
409 return ret;
f989dacd
MT
410}
411
412static PyObject* Pakfire_version_compare(PakfireObject* self, PyObject* args) {
413 const char* evr1 = NULL;
414 const char* evr2 = NULL;
415
416 if (!PyArg_ParseTuple(args, "ss", &evr1, &evr2))
417 return NULL;
418
419 int cmp = pakfire_version_compare(self->pakfire, evr1, evr2);
420
421 return PyLong_FromLong(cmp);
422}
423
04d3c185
MT
424static int Pakfire_execute_output_callback(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
425 void* data, const char* line, size_t length) {
deeb4e91 426 PyObject* callback = (PyObject*)data;
f3f0778c
MT
427 PyObject* args = NULL;
428 PyObject* result = NULL;
429 int r = 1;
d5bb2996
MT
430
431 // Do nothing if callback isn't set
deeb4e91 432 if (!callback)
d5bb2996
MT
433 return 0;
434
8e632745 435 // Remove the trailing newline
f3f0778c 436 if (line && length && line[length - 1] == '\n')
8e632745
MT
437 length--;
438
f3f0778c
MT
439 // Get the GIL
440 PyGILState_STATE state = PyGILState_Ensure();
441
d5bb2996 442 // Create tuple with arguments for the callback function
f3f0778c 443 args = Py_BuildValue("(is#)", LOG_INFO, line, (Py_ssize_t)length);
d5bb2996 444 if (!args)
f3f0778c 445 goto ERROR;
d5bb2996 446
f3f0778c
MT
447 // Call the callback method
448 result = PyObject_CallObject(callback, args);
d5bb2996
MT
449 if (result && PyLong_Check(result)) {
450 r = PyLong_AsLong(result);
451 }
452
f3f0778c 453ERROR:
d5bb2996 454 Py_XDECREF(args);
b1a539ef 455 Py_XDECREF(result);
d5bb2996 456
f3f0778c
MT
457 // Release the GIL
458 PyGILState_Release(state);
459
d5bb2996
MT
460 return r;
461}
462
9d6b128a 463static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwds) {
deeb4e91
MT
464 char* kwlist[] = {
465 "command",
466 "environ",
061223f7 467 "bind",
2015cb92 468 "callback",
cf440db8 469 "nice",
deeb4e91
MT
470 NULL
471 };
472
473 struct pakfire_jail* jail = NULL;
474 const char** argv = NULL;
deeb4e91
MT
475 int r;
476 PyObject* ret = NULL;
9d6b128a
MT
477
478 PyObject* command = NULL;
479 PyObject* environ = NULL;
061223f7 480 PyObject* bind = NULL;
2015cb92 481 PyObject* callback = NULL;
cf440db8 482 int nice = 0;
9d6b128a 483
2015cb92
MT
484 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOOi", kwlist, &command, &environ,
485 &bind, &callback, &nice))
9d6b128a
MT
486 return NULL;
487
488 // Check if command is a list
489 if (!PyList_Check(command)) {
490 PyErr_SetString(PyExc_TypeError, "command must be a list");
deeb4e91 491 goto ERROR;
9d6b128a
MT
492 }
493
deeb4e91 494 const ssize_t command_length = PyList_Size(command);
9d6b128a
MT
495
496 // Check if command is not empty
497 if (command_length == 0) {
498 PyErr_SetString(PyExc_ValueError, "command is empty");
deeb4e91 499 goto ERROR;
9d6b128a
MT
500 }
501
deeb4e91
MT
502 // Allocate argv
503 argv = calloc(command_length + 1, sizeof(*argv));
504 if (!argv)
505 goto ERROR;
506
9d6b128a
MT
507 // All arguments in command must be strings
508 for (unsigned int i = 0; i < command_length; i++) {
509 PyObject* item = PyList_GET_ITEM(command, i);
510
511 if (!PyUnicode_Check(item)) {
512 PyErr_Format(PyExc_TypeError, "Item %u in command is not a string", i);
6f1bdfd3 513 goto ERROR;
9d6b128a 514 }
deeb4e91
MT
515
516 // Copy to argv
517 argv[i] = PyUnicode_AsUTF8(item);
9d6b128a
MT
518 }
519
061223f7
MT
520 // Check if bind is a sequence
521 if (bind && !PySequence_Check(bind)) {
522 PyErr_SetString(PyExc_ValueError, "bind is not a sequence");
523 goto ERROR;
524 }
525
2015cb92
MT
526 // Check callback
527 if (callback && !PyCallable_Check(callback)) {
528 PyErr_SetString(PyExc_TypeError, "callback must be callable\n");
529 goto ERROR;
530 }
531
deeb4e91 532 // Create jail
9fa1afb6 533 r = pakfire_jail_create(&jail, self->pakfire);
deeb4e91
MT
534 if (r) {
535 PyErr_SetFromErrno(PyExc_OSError);
536 goto ERROR;
d5bb2996
MT
537 }
538
cf440db8
MT
539 // Set nice
540 if (nice) {
541 r = pakfire_jail_nice(jail, nice);
542 if (r) {
543 PyErr_SetFromErrno(PyExc_OSError);
544 goto ERROR;
545 }
546 }
547
deeb4e91
MT
548 PyObject* key = NULL;
549 PyObject* value = NULL;
9d6b128a
MT
550 Py_ssize_t p = 0;
551
deeb4e91 552 // Parse the environment
9d6b128a
MT
553 if (environ) {
554 // Check if environ is a dictionary
555 if (!PyDict_Check(environ)) {
556 PyErr_SetString(PyExc_TypeError, "environ must be a dictionary");
deeb4e91 557 goto ERROR;
9d6b128a
MT
558 }
559
560 // All keys and values must be strings
561 while (PyDict_Next(environ, &p, &key, &value)) {
562 if (!PyUnicode_Check(key) || !PyUnicode_Check(value)) {
563 PyErr_SetString(PyExc_TypeError, "Environment contains a non-string object");
deeb4e91 564 goto ERROR;
9d6b128a 565 }
9d6b128a 566
deeb4e91
MT
567 // Set environment value
568 r = pakfire_jail_set_env(jail, PyUnicode_AsUTF8(key), PyUnicode_AsUTF8(value));
569 if (r) {
570 PyErr_SetFromErrno(PyExc_OSError);
571 goto ERROR;
9d6b128a
MT
572 }
573 }
574 }
575
061223f7 576 // Bind
6f1bdfd3
MT
577 if (bind && PySequence_Check(bind)) {
578 const Py_ssize_t num_bind = PySequence_Length(bind);
061223f7 579
6f1bdfd3
MT
580 for (unsigned int i = 0; i < num_bind; i++) {
581 PyObject* b = PySequence_ITEM(bind, i);
582 if (!b)
583 goto ERROR;
061223f7 584
6f1bdfd3
MT
585 // Check if this is a Unicode object
586 if (!PyUnicode_Check(b)) {
587 PyErr_SetString(PyExc_ValueError, "bind contains a non-Unicode object");
588 Py_DECREF(b);
589 goto ERROR;
590 }
591
592 const char* path = PyUnicode_AsUTF8(b);
593
594 // Perform bind
595 r = pakfire_jail_bind(jail, path, path, 0);
596 if (r) {
597 PyErr_SetFromErrno(PyExc_OSError);
598 Py_DECREF(b);
599 goto ERROR;
600 }
061223f7 601
061223f7 602 Py_DECREF(b);
061223f7 603 }
061223f7
MT
604 }
605
a853ec56
MT
606 Py_BEGIN_ALLOW_THREADS
607
04d3c185
MT
608 // Set callback
609 pakfire_jail_set_stdout_callback(jail, Pakfire_execute_output_callback, callback);
610
9d6b128a 611 // Execute command
04d3c185 612 r = pakfire_jail_exec(jail, argv, 0);
9d6b128a 613
a853ec56
MT
614 Py_END_ALLOW_THREADS
615
deeb4e91 616 // If the return code was negative, we had some internal error
6d16c080 617 if (r < 0) {
6d16c080 618 PyErr_SetFromErrno(PyExc_OSError);
deeb4e91 619 goto ERROR;
6d16c080 620
deeb4e91 621 // Otherwise the executed command returned some error code
6d16c080 622 } else if (r > 0) {
9d6b128a
MT
623 PyObject* code = PyLong_FromLong(r);
624
deeb4e91 625 // Raise CommandExecutionError
9d6b128a
MT
626 PyErr_SetObject(PyExc_CommandExecutionError, code);
627 Py_DECREF(code);
628
deeb4e91 629 goto ERROR;
9d6b128a
MT
630 }
631
34ba6846
MT
632 // The process has exited successfully
633
2015cb92
MT
634 // Return None
635 ret = Py_None;
636 Py_INCREF(ret);
deeb4e91
MT
637
638ERROR:
639 if (argv)
640 free(argv);
2015cb92 641 if (jail)
deeb4e91 642 pakfire_jail_unref(jail);
deeb4e91
MT
643
644 return ret;
9d6b128a
MT
645}
646
af41db7e
MT
647static PyObject* Pakfire_dist(PakfireObject* self, PyObject* args) {
648 const char* path = NULL;
649 const char* target = NULL;
45448dbd 650 char* result = NULL;
a853ec56 651 int r;
af41db7e
MT
652
653 if (!PyArg_ParseTuple(args, "s|z", &path, &target))
654 return NULL;
655
a853ec56
MT
656 Py_BEGIN_ALLOW_THREADS
657
658 r = pakfire_dist(self->pakfire, path, target, &result);
af41db7e 659 if (r) {
a853ec56 660 Py_BLOCK_THREADS
af41db7e
MT
661 PyErr_SetFromErrno(PyExc_OSError);
662 return NULL;
663 }
664
a853ec56
MT
665 Py_END_ALLOW_THREADS
666
45448dbd
MT
667 PyObject* ret = PyUnicode_FromString(result);
668 free(result);
669
670 return ret;
af41db7e
MT
671}
672
78cc8800
MT
673static PyObject* Pakfire_get_repos(PakfireObject* self) {
674 struct pakfire_repolist* repos = pakfire_get_repos(self->pakfire);
675 if (!repos) {
676 PyErr_SetFromErrno(PyExc_OSError);
677 return NULL;
678 }
679
680 const size_t l = pakfire_repolist_size(repos);
681
682 PyObject* list = PyList_New(l);
683 if (!list)
684 goto ERROR;
685
686 for (unsigned int i = 0; i < l; i++) {
4651122b 687 struct pakfire_repo* repo = pakfire_repolist_get(repos, i);
78cc8800
MT
688 if (!repo)
689 continue;
690
691 PyObject* obj = new_repo(&RepoType, repo);
692 PyList_SET_ITEM(list, i, obj);
693
694 pakfire_repo_unref(repo);
695 }
696
697ERROR:
698 pakfire_repolist_unref(repos);
699
700 return list;
701}
702
1a276007
MT
703static PyObject* execute_return_value(int r) {
704 // Raise an OS error if r < 0
705 if (r < 0) {
706 errno = -r;
707
708 PyErr_SetFromErrno(PyExc_OSError);
709 return NULL;
710
711 // Raise exception when the command failed
712 } else if (r > 0) {
713 PyObject* code = PyLong_FromLong(r);
714
715 PyErr_SetObject(PyExc_CommandExecutionError, code);
716 Py_DECREF(code);
717
718 return NULL;
719 }
720
721 Py_RETURN_NONE;
722}
723
724static PyObject* Pakfire_build(PakfireObject* self, PyObject* args, PyObject* kwargs) {
f4e10417
MT
725 char* kwlist[] = {
726 "path",
195ab60e 727 "target",
f4e10417 728 "build_id",
ef009305 729 "ccache_path",
08ca63e9 730 "interactive",
c0f2502a 731 "disable_snapshot",
5a06668c 732 "disable_ccache",
9605d535 733 "disable_tests",
f4e10417
MT
734 NULL,
735 };
a829d33b 736 struct pakfire_build* build = NULL;
1a276007 737 const char* path = NULL;
195ab60e 738 const char* target = NULL;
eeba5748 739 const char* build_id = NULL;
ef009305 740 const char* ccache_path = NULL;
08ca63e9 741 int interactive = 0;
c0f2502a 742 int disable_snapshot = 0;
5a06668c 743 int disable_ccache = 0;
9605d535 744 int disable_tests = 0;
a853ec56 745 int r;
1a276007 746
ef009305
MT
747 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|zzzpppp", kwlist,
748 &path, &target, &build_id, &ccache_path, &interactive,
749 &disable_snapshot, &disable_ccache, &disable_tests))
1a276007
MT
750 return NULL;
751
c0f2502a
MT
752 int flags = 0;
753
08ca63e9
MT
754 if (interactive)
755 flags |= PAKFIRE_BUILD_INTERACTIVE;
756
c0f2502a
MT
757 // Disable snapshot if requested
758 if (disable_snapshot)
759 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
760
5a06668c
MT
761 // Disable ccache if requested
762 if (disable_ccache)
763 flags |= PAKFIRE_BUILD_DISABLE_CCACHE;
764
9605d535
MT
765 // Disable tests if requested
766 if (disable_tests)
767 flags |= PAKFIRE_BUILD_DISABLE_TESTS;
768
a829d33b
MT
769 // Create a new build environment
770 r = pakfire_build_create(&build, self->pakfire, build_id, flags);
da7dab6a
MT
771 if (r) {
772 PyErr_SetFromErrno(PyExc_OSError);
a829d33b 773 goto ERROR;
da7dab6a 774 }
a829d33b
MT
775
776 // Set target
777 if (target) {
778 r = pakfire_build_set_target(build, target);
da7dab6a
MT
779 if (r) {
780 PyErr_SetFromErrno(PyExc_OSError);
ef009305
MT
781 goto ERROR;
782 }
783 }
784
785 // Set ccache path
786 if (ccache_path) {
787 r = pakfire_build_set_ccache_path(build, ccache_path);
788 if (r) {
789 PyErr_SetFromErrno(PyExc_OSError);
a829d33b 790 goto ERROR;
da7dab6a 791 }
a829d33b
MT
792 }
793
da7dab6a
MT
794 Py_BEGIN_ALLOW_THREADS
795
1a276007 796 // Run build
a829d33b 797 r = pakfire_build_exec(build, path);
da7dab6a
MT
798 if (r) {
799 Py_BLOCK_THREADS;
800
801 if (r < 0) {
802 PyErr_SetFromErrno(PyExc_OSError);
803
804 // Raise a command execution error
805 } else {
806 PyObject* code = PyLong_FromLong(r);
807
808 PyErr_SetObject(PyExc_CommandExecutionError, code);
809 Py_DECREF(code);
810 }
811
812 goto ERROR;
813 }
814
815 Py_END_ALLOW_THREADS
a829d33b
MT
816
817ERROR:
818 if (build)
819 pakfire_build_unref(build);
a853ec56 820
da7dab6a
MT
821 if (r)
822 return NULL;
1a276007 823
da7dab6a 824 Py_RETURN_NONE;
1a276007
MT
825}
826
7f068a08
MT
827static PyObject* Pakfire_shell(PakfireObject* self, PyObject* args, PyObject* kwargs) {
828 char* kwlist[] = {
829 "install",
51840d97 830 "disable_snapshot",
7f068a08
MT
831 NULL,
832 };
833 char** packages = NULL;
51840d97 834 int disable_snapshot = 0;
a853ec56 835 int r;
7f068a08
MT
836
837 // Parse everything
51840d97
MT
838 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O&p", kwlist,
839 convert_packages, &packages, &disable_snapshot))
7f068a08
MT
840 return NULL;
841
51840d97
MT
842 int flags = 0;
843
844 if (disable_snapshot)
845 flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT;
846
a853ec56
MT
847 Py_BEGIN_ALLOW_THREADS
848
849 r = pakfire_shell(self->pakfire, (const char**)packages, flags);
850
851 Py_END_ALLOW_THREADS
1a276007
MT
852
853 return execute_return_value(r);
854}
855
d1ed1ada 856static PyObject* Pakfire_clean(PakfireObject* self) {
a853ec56
MT
857 int r;
858
859 Py_BEGIN_ALLOW_THREADS
860
861 r = pakfire_clean(self->pakfire, 0);
d1ed1ada 862 if (r) {
a853ec56 863 Py_BLOCK_THREADS
d1ed1ada
MT
864 PyErr_SetFromErrno(PyExc_OSError);
865 return NULL;
866 }
867
a853ec56
MT
868 Py_END_ALLOW_THREADS
869
d1ed1ada
MT
870 Py_RETURN_NONE;
871}
872
03359f52
MT
873static PyObject* Pakfire_refresh(PakfireObject* self, PyObject* args) {
874 int force = 0;
a853ec56 875 int r;
03359f52
MT
876
877 if (!PyArg_ParseTuple(args, "|p", &force))
878 return NULL;
879
a853ec56
MT
880 Py_BEGIN_ALLOW_THREADS
881
882 r = pakfire_refresh(self->pakfire, force);
03359f52 883 if (r) {
a853ec56 884 Py_BLOCK_THREADS
03359f52
MT
885 PyErr_SetFromErrno(PyExc_OSError);
886 return NULL;
887 }
888
a853ec56
MT
889 Py_END_ALLOW_THREADS
890
03359f52
MT
891 Py_RETURN_NONE;
892}
893
36c98595 894static PyObject* Pakfire_open(PakfireObject* self, PyObject* args) {
900faa2f 895 struct pakfire_archive* archive = NULL;
36c98595
MT
896 const char* path = NULL;
897
898 if (!PyArg_ParseTuple(args, "s", &path))
899 return NULL;
900
a853ec56
MT
901 Py_BEGIN_ALLOW_THREADS
902
36c98595
MT
903 int r = pakfire_archive_open(&archive, self->pakfire, path);
904 if (r) {
a853ec56 905 Py_BLOCK_THREADS
36c98595
MT
906 PyErr_SetFromErrno(PyExc_OSError);
907 return NULL;
908 }
909
a853ec56
MT
910 Py_END_ALLOW_THREADS
911
36c98595
MT
912 // Create Python object
913 PyObject* object = new_archive(&ArchiveType, archive);
914 pakfire_archive_unref(archive);
915
916 return object;
917}
918
c54d9960 919static PyObject* Pakfire_repo_compose(PakfireObject* self, PyObject* args, PyObject* kwargs) {
764ab42a 920 char* kwlist[] = { "path", "files", "key", NULL };
c54d9960 921 const char* path = NULL;
0c3dce19 922 PyObject* list = NULL;
764ab42a 923 KeyObject* key = NULL;
c54d9960 924
0c3dce19
MT
925 PyObject* ret = NULL;
926
764ab42a 927 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO|O!", kwlist, &path, &list, &KeyType, &key))
0c3dce19
MT
928 return NULL;
929
930 // List must be a sequence
931 if (!PySequence_Check(list)) {
932 PyErr_SetString(PyExc_ValueError, "Expected a sequence.");
c54d9960 933 return NULL;
0c3dce19 934 }
c54d9960 935
0c3dce19
MT
936 // How many new files do we have?
937 ssize_t num_files = PySequence_Length(list);
938 if (num_files < 0)
939 return NULL;
940
941 // Allocate files array
942 const char** files = calloc(num_files + 1, sizeof(*files));
943 if (!files) {
944 PyErr_SetFromErrno(PyExc_OSError);
945 return NULL;
946 }
947
948 for (int i = 0; i < num_files; i++) {
949 PyObject* file = PySequence_GetItem(list, i);
950 if (!file)
951 goto ERROR;
952
953 // Check if file is a Unicode object
954 if (!PyUnicode_Check(file)) {
955 PyErr_SetString(PyExc_ValueError, "Expected a string.");
956 goto ERROR;
957 }
958
959 // Add pointer to string to files array
960 files[i] = PyUnicode_AsUTF8(file);
961 if (!files[i])
962 goto ERROR;
963
964 Py_DECREF(file);
965 }
966
a853ec56
MT
967 Py_BEGIN_ALLOW_THREADS
968
764ab42a 969 int r = pakfire_repo_compose(self->pakfire, path, (key) ? key->key : NULL, files);
c54d9960 970 if (r) {
a853ec56 971 Py_BLOCK_THREADS
c54d9960
MT
972 PyErr_SetFromErrno(PyExc_OSError);
973 return NULL;
974 }
975
a853ec56
MT
976 Py_END_ALLOW_THREADS
977
0c3dce19
MT
978 // Return None on success
979 ret = Py_None;
980 Py_INCREF(ret);
981
982ERROR:
983 if (files)
984 free(files);
985
986 return ret;
c54d9960
MT
987}
988
a2612d13
MT
989static PyObject* Pakfire_mkimage(PakfireObject* self, PyObject* args, PyObject* kwargs) {
990 char* kwlist[] = {
991 "type",
992 "path",
993 NULL,
994 };
995 struct pakfire_build* build = NULL;
996 const char* type = NULL;
c8f04af2
MT
997 PyObject* file = NULL;
998 FILE* f = NULL;
a2612d13
MT
999 int r;
1000
c8f04af2
MT
1001 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO", kwlist, &type, &file))
1002 return NULL;
1003
1004 // Make a file handle out of file
1005 f = PyObject_AsFileHandle(file, "w");
1006 if (!f)
a2612d13
MT
1007 return NULL;
1008
1009 // Create a new build environment
1010 r = pakfire_build_create(&build, self->pakfire, NULL, 0);
1011 if (r) {
1012 PyErr_SetFromErrno(PyExc_OSError);
1013 goto ERROR;
1014 }
1015
1016 Py_BEGIN_ALLOW_THREADS
1017
1018 // Run mkimage
c8f04af2 1019 r = pakfire_build_mkimage(build, type, f);
a2612d13
MT
1020 if (r) {
1021 Py_BLOCK_THREADS;
1022
1023 if (r < 0)
1024 PyErr_SetFromErrno(PyExc_OSError);
1025
1026 goto ERROR;
1027 }
1028
1029 Py_END_ALLOW_THREADS
1030
1031ERROR:
1032 if (build)
1033 pakfire_build_unref(build);
c8f04af2
MT
1034 if (f)
1035 fclose(f);
a2612d13
MT
1036 if (r)
1037 return NULL;
1038
1039 Py_RETURN_NONE;
1040}
1041
6e46b18e 1042static struct PyMethodDef Pakfire_methods[] = {
1a276007
MT
1043 {
1044 "build",
1045 (PyCFunction)Pakfire_build,
1046 METH_VARARGS|METH_KEYWORDS,
1047 NULL
1048 },
d1ed1ada
MT
1049 {
1050 "clean",
1051 (PyCFunction)Pakfire_clean,
1052 METH_NOARGS,
1053 NULL,
1054 },
af41db7e
MT
1055 {
1056 "dist",
1057 (PyCFunction)Pakfire_dist,
1058 METH_VARARGS,
1059 NULL
1060 },
9d6b128a
MT
1061 {
1062 "execute",
1063 (PyCFunction)Pakfire_execute,
1064 METH_VARARGS|METH_KEYWORDS,
1065 NULL
1066 },
ad62bb07
MT
1067 {
1068 "generate_key",
1069 (PyCFunction)Pakfire_generate_key,
f54744b1 1070 METH_VARARGS|METH_KEYWORDS,
ad62bb07
MT
1071 NULL
1072 },
8ef6c388
MT
1073 {
1074 "get_repo",
1075 (PyCFunction)Pakfire_get_repo,
1076 METH_VARARGS,
1077 NULL
1078 },
44cbdc0a
MT
1079 {
1080 "import_key",
1081 (PyCFunction)Pakfire_import_key,
1082 METH_VARARGS,
1083 NULL,
1084 },
a2612d13
MT
1085 {
1086 "mkimage",
1087 (PyCFunction)Pakfire_mkimage,
1088 METH_VARARGS|METH_KEYWORDS,
1089 NULL
1090 },
36c98595
MT
1091 {
1092 "open",
1093 (PyCFunction)Pakfire_open,
1094 METH_VARARGS,
1095 NULL
1096 },
03359f52
MT
1097 {
1098 "refresh",
1099 (PyCFunction)Pakfire_refresh,
1100 METH_VARARGS,
1101 NULL,
1102 },
c54d9960
MT
1103 {
1104 "repo_compose",
1105 (PyCFunction)Pakfire_repo_compose,
1106 METH_VARARGS|METH_KEYWORDS,
1107 NULL
1108 },
f989dacd
MT
1109 {
1110 "search",
1111 (PyCFunction)Pakfire_search,
4952c631 1112 METH_VARARGS|METH_KEYWORDS,
f989dacd
MT
1113 NULL
1114 },
1a276007
MT
1115 {
1116 "shell",
1117 (PyCFunction)Pakfire_shell,
7f068a08 1118 METH_VARARGS|METH_KEYWORDS,
1a276007
MT
1119 NULL,
1120 },
f989dacd
MT
1121 {
1122 "version_compare",
1123 (PyCFunction)Pakfire_version_compare,
1124 METH_VARARGS,
1125 NULL
1126 },
1127 {
1128 "whatprovides",
1129 (PyCFunction)Pakfire_whatprovides,
54334355 1130 METH_VARARGS,
f989dacd
MT
1131 NULL
1132 },
8201cef2
MT
1133 {
1134 "whatrequires",
1135 (PyCFunction)Pakfire_whatrequires,
1136 METH_VARARGS,
1137 NULL
1138 },
6e46b18e
MT
1139 { NULL },
1140};
1141
1142static struct PyGetSetDef Pakfire_getsetters[] = {
1143 {
1144 "arch",
1145 (getter)Pakfire_get_arch,
1146 NULL,
1147 NULL,
1148 NULL
1149 },
1150 {
1151 "path",
1152 (getter)Pakfire_get_path,
1153 NULL,
1154 NULL,
1155 NULL
1156 },
78cc8800
MT
1157 {
1158 "repos",
1159 (getter)Pakfire_get_repos,
1160 NULL,
1161 NULL,
1162 NULL
1163 },
6e46b18e
MT
1164 { NULL },
1165};
1166
1167PyTypeObject PakfireType = {
1168 PyVarObject_HEAD_INIT(NULL, 0)
1169 tp_name: "_pakfire.Pakfire",
1170 tp_basicsize: sizeof(PakfireObject),
1171 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
1172 tp_new: Pakfire_new,
1173 tp_dealloc: (destructor)Pakfire_dealloc,
1174 tp_init: (initproc)Pakfire_init,
1175 tp_doc: "Pakfire object",
1176 tp_methods: Pakfire_methods,
1177 tp_getset: Pakfire_getsetters,
1178 tp_repr: (reprfunc)Pakfire_repr,
1179};