1 /*
2 * Copyright (C) 2012-2019 Red Hat, Inc.
3 *
4 * Licensed under the GNU Lesser General Public License Version 2.1
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library 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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "Python.h"
22
23
24 // hawkey
25 #include "libdnf/repo/Repo.hpp"
26 #include "dnf-types.h"
27 #include "hy-packageset.h"
28 #include "hy-repo.h"
29 #include "hy-util.h"
30 #include "dnf-version.h"
31 #include "dnf-sack-private.hpp"
32 #include "libdnf/module/ModulePackageContainer.hpp"
33
34 // pyhawkey
35 #include "exception-py.hpp"
36 #include "hawkey-pysys.hpp"
37 #include "iutil-py.hpp"
38 #include "package-py.hpp"
39 #include "repo-py.hpp"
40 #include "sack-py.hpp"
41
42 #include "pycomp.hpp"
43 #include "sack/packageset.hpp"
44
45 #include <algorithm>
46 #include <functional>
47
48 typedef struct {
49 PyObject_HEAD
50 DnfSack *sack;
51 PyObject *custom_package_class;
52 PyObject *custom_package_val;
53 PyObject * ModulePackageContainerPy;
54
55 // g_log handler IDs
56 // Multiple sacks can be created during a run of an application and each
57 // sack opens a log file and registers two g_log handlers. To avoid dangling
58 // handlers with invalid FILE pointers (we close them when destroying the
59 // sack), we need to keep track of the handlers so that we can also remove
60 // them.
61 //
62 // g_log is clever about adding log handlers. It does store all handlers
63 // registered for a given domain, but only the one that was registered last
64 // is used. If you remove the last registered one, the next in line will be
65 // used. That means stacking sacks is ok, the handler from the last
66 // undeleted sack will be the one that is used.
67 guint default_log_handler_id;
68 guint libdnf_log_handler_id;
69
70 FILE *log_out;
71 } _SackObject;
72
73 typedef struct {
74 PyObject_HEAD
75 libdnf::ModulePackageContainer * ptr;
76 void * ty;
77 int own;
78 PyObject * next;
79 } ModulePackageContainerPyObject;
80
81 PyObject *
new_package(PyObject * sack,Id id)82 new_package(PyObject *sack, Id id)
83 {
84 _SackObject *self;
85
86 if (!sackObject_Check(sack)) {
87 PyErr_SetString(PyExc_TypeError, "Expected a _hawkey.Sack object.");
88 return NULL;
89 }
90 self = (_SackObject*)sack;
91 UniquePtrPyObject arglist;
92 if (self->custom_package_class || self->custom_package_val) {
93 arglist.reset(Py_BuildValue("(Oi)O", sack, id, self->custom_package_val));
94 } else {
95 arglist.reset(Py_BuildValue("((Oi))", sack, id));
96 }
97 if (!arglist)
98 return NULL;
99 if (self->custom_package_class)
100 return PyObject_CallObject(self->custom_package_class, arglist.get());
101 return PyObject_CallObject((PyObject*)&package_Type, arglist.get());
102 }
103
104 DnfSack *
sackFromPyObject(PyObject * o)105 sackFromPyObject(PyObject *o)
106 {
107 if (!sackObject_Check(o)) {
108 PyErr_SetString(PyExc_TypeError, "Expected a _hawkey.Sack object.");
109 return NULL;
110 }
111 return ((_SackObject *)o)->sack;
112 }
113
114 int
sack_converter(PyObject * o,DnfSack ** sack_ptr)115 sack_converter(PyObject *o, DnfSack **sack_ptr)
116 {
117 DnfSack *sack = sackFromPyObject(o);
118 if (sack == NULL)
119 return 0;
120 *sack_ptr = sack;
121 return 1;
122 }
123
124 /* helpers */
125 static PyObject *
repo_enabled(_SackObject * self,PyObject * reponame,int enabled)126 repo_enabled(_SackObject *self, PyObject *reponame, int enabled)
127 {
128 PycompString cname(reponame);
129 if (!cname.getCString())
130 return NULL;
131 dnf_sack_repo_enabled(self->sack, cname.getCString(), enabled);
132 Py_RETURN_NONE;
133 }
134
135 /* functions on the type */
136
137 static void
sack_dealloc(_SackObject * o)138 sack_dealloc(_SackObject *o)
139 {
140 Py_XDECREF(o->custom_package_class);
141 Py_XDECREF(o->custom_package_val);
142 if (o->sack) {
143 if (auto moduleContainer = o->ModulePackageContainerPy) {
144 dnf_sack_set_module_container(o->sack, NULL);
145 Py_DECREF(moduleContainer);
146 }
147 g_object_unref(o->sack);
148 }
149
150 if (o->log_out) {
151 g_log_remove_handler(nullptr, o->default_log_handler_id);
152 g_log_remove_handler("libdnf", o->libdnf_log_handler_id);
153 fclose(o->log_out);
154 }
155
156 Py_TYPE(o)->tp_free(o);
157 }
158
159 static PyObject *
sack_new(PyTypeObject * type,PyObject * args,PyObject * kwds)160 sack_new(PyTypeObject *type, PyObject *args, PyObject *kwds) try
161 {
162 _SackObject *self = (_SackObject *)type->tp_alloc(type, 0);
163
164 if (self) {
165 self->sack = NULL;
166 self->custom_package_class = NULL;
167 self->custom_package_val = NULL;
168 self->ModulePackageContainerPy = NULL;
169 }
170 return (PyObject *)self;
171 } CATCH_TO_PYTHON
172
173 const char *
log_level_name(int level)174 log_level_name(int level)
175 {
176 switch (level) {
177 case G_LOG_FLAG_FATAL:
178 return "FATAL";
179 case G_LOG_LEVEL_ERROR:
180 return "ERROR";
181 case G_LOG_LEVEL_CRITICAL:
182 return "CRITICAL";
183 case G_LOG_LEVEL_WARNING:
184 return "WARN";
185 case G_LOG_LEVEL_DEBUG:
186 return "DEBUG";
187 case G_LOG_LEVEL_INFO:
188 return "INFO";
189 default:
190 return "(level?)";
191 }
192 }
193
194 static void
log_handler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer user_data)195 log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
196 {
197 time_t t = time(NULL);
198 struct tm tm;
199 char timestr[32];
200
201 FILE *log_out = (FILE*) user_data;
202 localtime_r(&t, &tm);
203 strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S%z ", &tm);
204 gchar *msg = g_strjoin("", timestr, log_level_name(log_level), " ", message, "\n", NULL);
205 fwrite(msg, strlen(msg), 1, log_out);
206 fflush(log_out);
207 g_free(msg);
208 }
209
210 static void
log_handler_noop(const gchar *,GLogLevelFlags,const gchar *,gpointer)211 log_handler_noop(const gchar *, GLogLevelFlags, const gchar *, gpointer)
212 {
213 }
214
215 static gboolean
sack_set_logfile(_SackObject * self,const gchar * path,bool debug)216 sack_set_logfile(_SackObject *self, const gchar *path, bool debug)
217 {
218 self->log_out = fopen(path, "a");
219
220 if (!self->log_out)
221 return FALSE;
222
223 // The default log handler prints messages that weren't handled by any
224 // other logger to stderr/stdout, we do not want that
225 g_log_set_default_handler(log_handler_noop, nullptr);
226
227 GLogLevelFlags log_mask = debug ? G_LOG_LEVEL_MASK : static_cast<GLogLevelFlags>(
228 G_LOG_LEVEL_INFO |
229 G_LOG_LEVEL_MESSAGE |
230 G_LOG_LEVEL_WARNING |
231 G_LOG_LEVEL_CRITICAL |
232 G_LOG_LEVEL_ERROR);
233
234 // set the handler for the default domain as well as "libdnf"
235 self->default_log_handler_id = g_log_set_handler(nullptr, log_mask, log_handler, self->log_out);
236 self->libdnf_log_handler_id = g_log_set_handler("libdnf", log_mask, log_handler, self->log_out);
237
238 g_info("=== Started libdnf-%d.%d.%d ===", LIBDNF_MAJOR_VERSION,
239 LIBDNF_MINOR_VERSION, LIBDNF_MICRO_VERSION);
240 return TRUE;
241 }
242
243 static int
sack_init(_SackObject * self,PyObject * args,PyObject * kwds)244 sack_init(_SackObject *self, PyObject *args, PyObject *kwds) try
245 {
246 g_autoptr(GError) error = NULL;
247 PyObject *custom_class = NULL;
248 PyObject *custom_val = NULL;
249 PycompString cachedir;
250 const char *arch = NULL;
251 const char *rootdir = NULL;
252 PyObject *cachedir_py = NULL;
253 PyObject *logfile_py = NULL;
254 self->log_out = NULL;
255 int make_cache_dir = 0;
256 PyObject *debug_object = nullptr;
257 gboolean all_arch = FALSE;
258 const char *kwlist[] = {"cachedir", "arch", "rootdir", "pkgcls",
259 "pkginitval", "make_cache_dir", "logfile", "logdebug",
260 "all_arch", NULL};
261
262 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OssOOiOO!i", (char**) kwlist,
263 &cachedir_py, &arch, &rootdir,
264 &custom_class, &custom_val,
265 &make_cache_dir, &logfile_py,
266 &PyBool_Type, &debug_object,
267 &all_arch))
268 return -1;
269
270 bool debug = debug_object != nullptr && PyObject_IsTrue(debug_object);
271
272 if (cachedir_py != NULL) {
273 cachedir = PycompString(cachedir_py);
274 if (!cachedir.getCString())
275 return -1;
276 }
277 int flags = 0;
278 if (make_cache_dir)
279 flags |= DNF_SACK_SETUP_FLAG_MAKE_CACHE_DIR;
280 self->sack = dnf_sack_new();
281 if (all_arch) {
282 dnf_sack_set_all_arch(self->sack, all_arch);
283 } else {
284 if (!dnf_sack_set_arch(self->sack, arch, &error)) {
285 PyErr_SetString(HyExc_Arch, "Unrecognized arch for the sack.");
286 return -1;
287 }
288 }
289 dnf_sack_set_rootdir(self->sack, rootdir);
290 dnf_sack_set_cachedir(self->sack, cachedir.getCString());
291 if (logfile_py != NULL) {
292 PycompString logfile(logfile_py);
293 if (!logfile.getCString())
294 return -1;
295 if (!sack_set_logfile(self, logfile.getCString(), debug)) {
296 PyErr_Format(PyExc_IOError, "Failed to open log file: %s", logfile.getCString());
297 return -1;
298 }
299 }
300 if (!dnf_sack_setup(self->sack, flags, &error)) {
301 switch (error->code) {
302 case DNF_ERROR_FILE_INVALID:
303 PyErr_SetString(PyExc_IOError,
304 "Failed creating working files for the Sack.");
305 break;
306 case DNF_ERROR_INVALID_ARCHITECTURE:
307 PyErr_SetString(HyExc_Arch, "Unrecognized arch for the sack.");
308 break;
309 default:
310 assert(0);
311 }
312 return -1;
313 }
314
315 if (custom_class && custom_class != Py_None) {
316 if (!PyType_Check(custom_class)) {
317 PyErr_SetString(PyExc_TypeError, "Expected a class object.");
318 return -1;
319 }
320 Py_INCREF(custom_class);
321 self->custom_package_class = custom_class;
322 }
323 if (custom_val && custom_val != Py_None) {
324 Py_INCREF(custom_val);
325 self->custom_package_val = custom_val;
326
327 }
328 return 0;
329 } CATCH_TO_PYTHON_INT
330
331 /* getsetters */
332
333 static PyObject *
get_cache_dir(_SackObject * self,void * unused)334 get_cache_dir(_SackObject *self, void *unused) try
335 {
336 const char *cstr = dnf_sack_get_cache_dir(self->sack);
337 if (cstr == NULL)
338 Py_RETURN_NONE;
339 return PyString_FromString(cstr);
340 } CATCH_TO_PYTHON
341
342 static int
set_installonly(_SackObject * self,PyObject * obj,void * unused)343 set_installonly(_SackObject *self, PyObject *obj, void *unused) try
344 {
345 if (!PySequence_Check(obj)) {
346 PyErr_SetString(PyExc_AttributeError, "Expected a sequence.");
347 return -1;
348 }
349
350 const int len = PySequence_Length(obj);
351 PycompString pStrings[len];
352 const char *strings[len + 1];
353
354 for (int i = 0; i < len; ++i) {
355 UniquePtrPyObject item(PySequence_GetItem(obj, i));
356 if (PyUnicode_Check(item.get()) || PyString_Check(item.get())) {
357 pStrings[i] = PycompString(item.get());
358 strings[i] = pStrings[i].getCString();
359 } else
360 strings[i] = NULL;
361 if (strings[i] == NULL)
362 return -1;
363 }
364 strings[len] = NULL;
365
366 DnfSack *sack = self->sack;
367 dnf_sack_set_installonly(sack, strings);
368
369 return 0;
370 } CATCH_TO_PYTHON_INT
371
372 static int
set_installonly_limit(_SackObject * self,PyObject * obj,void * unused)373 set_installonly_limit(_SackObject *self, PyObject *obj, void *unused) try
374 {
375 int limit = (int)PyLong_AsLong(obj);
376 if (PyErr_Occurred())
377 return -1;
378 dnf_sack_set_installonly_limit(self->sack, limit);
379 return 0;
380 } CATCH_TO_PYTHON_INT
381
382 static int
set_module_container(_SackObject * self,PyObject * obj,void * unused)383 set_module_container(_SackObject *self, PyObject *obj, void *unused) try
384 {
385 UniquePtrPyObject thisPyContainer(PyObject_GetAttrString(obj, "this"));
386 auto swigContainer = reinterpret_cast< ModulePackageContainerPyObject * >(thisPyContainer.get());
387 if (swigContainer == nullptr) {
388 PyErr_SetString(PyExc_SystemError, "Unable to parse ModuleContainer object");
389 return -1;
390 }
391 auto moduleContainer = swigContainer->ptr;
392 auto sack = self->sack;
393 if (auto oldConteynerPy = self->ModulePackageContainerPy) {
394 Py_XDECREF(oldConteynerPy);
395 dnf_sack_set_module_container(sack, moduleContainer);
396 } else {
397 auto oldContainer = dnf_sack_set_module_container(sack, moduleContainer);
398 if (oldContainer) {
399 delete oldContainer;
400 }
401 }
402 self->ModulePackageContainerPy = obj;
403 Py_INCREF(obj);
404
405 return 0;
406 } CATCH_TO_PYTHON_INT
407
408 static PyObject *
get_module_container(_SackObject * self,void * unused)409 get_module_container(_SackObject *self, void *unused) try
410 {
411 if (auto moduleConteinerPy = self->ModulePackageContainerPy) {
412 Py_INCREF(moduleConteinerPy);
413 return moduleConteinerPy;
414 }
415 Py_RETURN_NONE;
416 } CATCH_TO_PYTHON
417
418 static int
set_allow_vendor_change(_SackObject * self,PyObject * obj,void * unused)419 set_allow_vendor_change(_SackObject *self, PyObject *obj, void *unused) try
420 {
421 gboolean vendor = PyObject_IsTrue(obj);
422 if (PyErr_Occurred())
423 return -1;
424 dnf_sack_set_allow_vendor_change(self->sack, vendor);
425 return 0;
426 } CATCH_TO_PYTHON_INT
427
428 static PyGetSetDef sack_getsetters[] = {
429 {(char*)"cache_dir", (getter)get_cache_dir, NULL, NULL, NULL},
430 {(char*)"installonly", NULL, (setter)set_installonly, NULL, NULL},
431 {(char*)"installonly_limit", NULL, (setter)set_installonly_limit, NULL, NULL},
432 {(char*)"allow_vendor_change", NULL,
433 (setter)set_allow_vendor_change, NULL, NULL},
434 {(char*)"_moduleContainer", (getter)get_module_container, (setter)set_module_container,
435 NULL, NULL},
436 {NULL} /* sentinel */
437 };
438
439 /* object methods */
440
441 static PyObject *
evr_cmp(_SackObject * self,PyObject * args)442 evr_cmp(_SackObject *self, PyObject *args) try
443 {
444 const char *evr1 = NULL, *evr2 = NULL;
445
446 if (!PyArg_ParseTuple(args, "ss", &evr1, &evr2))
447 return NULL;
448 int cmp = dnf_sack_evr_cmp(self->sack, evr1, evr2);
449 return PyLong_FromLong(cmp);
450 } CATCH_TO_PYTHON
451
452 static PyObject *
get_running_kernel(_SackObject * self,PyObject * unused)453 get_running_kernel(_SackObject *self, PyObject *unused) try
454 {
455 DnfSack *sack = self->sack;
456 DnfPackage *cpkg = dnf_sack_get_running_kernel(sack);
457
458 if (cpkg == NULL)
459 Py_RETURN_NONE;
460 PyObject *pkg = new_package((PyObject*)self, dnf_package_get_id(cpkg));
461 g_object_unref(cpkg);
462 return pkg;
463 } CATCH_TO_PYTHON
464
465 static PyObject *
create_cmdline_repo(_SackObject * self,PyObject * unused)466 create_cmdline_repo(_SackObject *self, PyObject *unused) try
467 {
468 Py_RETURN_NONE;
469 } CATCH_TO_PYTHON
470
471 static PyObject *
create_package(_SackObject * self,PyObject * solvable_id)472 create_package(_SackObject *self, PyObject *solvable_id) try
473 {
474 Id id = PyLong_AsLong(solvable_id);
475 if (id <= 0) {
476 PyErr_SetString(PyExc_TypeError, "Expected a positive integer.");
477 return NULL;
478 }
479 return new_package((PyObject*)self, id);
480 } CATCH_TO_PYTHON
481
482 static PyObject *
add_cmdline_package(_SackObject * self,PyObject * fn_obj)483 add_cmdline_package(_SackObject *self, PyObject *fn_obj) try
484 {
485 DnfPackage *cpkg;
486 PyObject *pkg;
487 PycompString fn(fn_obj);
488
489 if (!fn.getCString())
490 return NULL;
491 cpkg = dnf_sack_add_cmdline_package_nochecksum(self->sack, fn.getCString());
492 if (cpkg == NULL) {
493 PyErr_Format(PyExc_IOError, "Can not load RPM file: %s.", fn.getCString());
494 return NULL;
495 }
496 pkg = new_package((PyObject*)self, dnf_package_get_id(cpkg));
497 g_object_unref(cpkg);
498 return pkg;
499 } CATCH_TO_PYTHON
500
501 template<void (*sackExcludeIncludeFunc)(DnfSack *, const DnfPackageSet*)>
502 static PyObject *
modify_excl_incl(_SackObject * self,PyObject * o)503 modify_excl_incl(_SackObject *self, PyObject *o) try
504 {
505 DnfSack *sack = self->sack;
506 auto pset = pyseq_to_packageset(o, sack);
507 if (!pset)
508 return NULL;
509 sackExcludeIncludeFunc(sack, pset.get());
510 Py_RETURN_NONE;
511 } CATCH_TO_PYTHON
512
513 template<void (*sackExcludeIncludeFunc)(DnfSack *)>
514 static PyObject *
reset_excl_incl(_SackObject * self)515 reset_excl_incl(_SackObject *self) try
516 {
517 DnfSack *sack = self->sack;
518 sackExcludeIncludeFunc(sack);
519 Py_RETURN_NONE;
520 } CATCH_TO_PYTHON
521
522 template<DnfPackageSet * (*sackExcludeIncludeFunc)(DnfSack *)>
523 static PyObject *
get_excl_incl(_SackObject * self)524 get_excl_incl(_SackObject *self) try
525 {
526 DnfSack *sack = self->sack;
527 DnfPackageSet * pset = sackExcludeIncludeFunc(sack);
528 if (!pset)
529 return PyList_New(0);
530 PyObject * pyList = packageset_to_pylist(pset, (PyObject *)self);
531 delete pset;
532 return pyList;
533 } CATCH_TO_PYTHON
534
535 static PyObject *
set_use_includes(_SackObject * self,PyObject * args)536 set_use_includes(_SackObject *self, PyObject *args) try
537 {
538 PyObject *py_enabled;
539 const char *creponame = NULL;
540 if (!PyArg_ParseTuple(args, "O!|z", &PyBool_Type, &py_enabled, &creponame))
541 return NULL;
542
543 gboolean enabled = PyObject_IsTrue(py_enabled);
544 if (!dnf_sack_set_use_includes(self->sack, creponame, enabled)) {
545 PyErr_SetString(PyExc_ValueError, "Can't set use_includes for repo with given name.");
546 return NULL;
547 }
548
549 Py_RETURN_NONE;
550 } CATCH_TO_PYTHON
551
552 static PyObject *
get_use_includes(_SackObject * self,PyObject * reponame)553 get_use_includes(_SackObject *self, PyObject *reponame) try
554 {
555 DnfSack *sack = self->sack;
556
557 PycompString creponame(reponame);
558 if (!creponame.getCString())
559 return NULL;
560
561 gboolean enabled;
562 if (!dnf_sack_get_use_includes(sack, creponame.getCString(), &enabled)) {
563 PyErr_SetString(PyExc_ValueError, "Can't found repo with given name.");
564 return NULL;
565 }
566
567 if (enabled)
568 Py_RETURN_TRUE;
569 else
570 Py_RETURN_FALSE;
571 } CATCH_TO_PYTHON
572
573 static PyObject *
disable_repo(_SackObject * self,PyObject * reponame)574 disable_repo(_SackObject *self, PyObject *reponame) try
575 {
576 return repo_enabled(self, reponame, 0);
577 } CATCH_TO_PYTHON
578
579 static PyObject *
enable_repo(_SackObject * self,PyObject * reponame)580 enable_repo(_SackObject *self, PyObject *reponame) try
581 {
582 return repo_enabled(self, reponame, 1);
583 } CATCH_TO_PYTHON
584
585 static PyObject *
list_arches(_SackObject * self,PyObject * unused)586 list_arches(_SackObject *self, PyObject *unused) try
587 {
588 const char **arches = dnf_sack_list_arches(self->sack);
589 PyObject *list;
590 if (!arches && !dnf_sack_get_all_arch(self->sack)) {
591 PyErr_SetString(HyExc_Runtime, "Arches not initialized");
592 return NULL;
593 }
594
595 if (arches) {
596 list = strlist_to_pylist(arches);
597 g_free(arches);
598 return list;
599 } else {
600 return PyList_New(0);
601 }
602 } CATCH_TO_PYTHON
603
604 static PyObject *
filter_modules(_SackObject * self,PyObject * args,PyObject * kwds)605 filter_modules(_SackObject *self, PyObject *args, PyObject *kwds) try
606 {
607 const char *kwlist[] = {"module_container", "hotfix_repos", "install_root", "platform_module",
608 "update_only", "debugsolver", "module_obsoletes", NULL};
609 PyObject * pyModuleContainer;
610 PyObject * pyHotfixRepos;
611 char * installRoot = nullptr;
612 char * platformModule = nullptr;
613 PyObject * pyUpdateOnly = nullptr;
614 PyObject * pyDebugSolver = nullptr;
615 PyObject * pyModuleObsoletes = nullptr;
616
617 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOzz|O!O!O!", (char**) kwlist, &pyModuleContainer,
618 &pyHotfixRepos, &installRoot, &platformModule, &PyBool_Type, &pyUpdateOnly,
619 &PyBool_Type, &pyDebugSolver, &PyBool_Type, &pyModuleObsoletes))
620 return 0;
621 bool updateOnly = pyUpdateOnly == NULL || PyObject_IsTrue(pyUpdateOnly);
622 bool debugSolver = pyDebugSolver != NULL && PyObject_IsTrue(pyDebugSolver);
623 bool moduleObsoletes = pyModuleObsoletes != NULL && PyObject_IsTrue(pyModuleObsoletes);
624 UniquePtrPyObject thisPyModuleContainer(PyObject_GetAttrString(pyModuleContainer, "this"));
625 auto swigContainer = reinterpret_cast< ModulePackageContainerPyObject * >(thisPyModuleContainer.get());
626 auto moduleContainer = swigContainer->ptr;
627 std::vector<std::string> hotfixRepos;
628 try {
629 hotfixRepos = pySequenceConverter(pyHotfixRepos);
630 } catch (std::runtime_error &) {
631 return NULL;
632 }
633 std::vector<const char *> hotfixReposCString(hotfixRepos.size() + 1);
634 std::transform(hotfixRepos.begin(), hotfixRepos.end(), hotfixReposCString.begin(),
635 std::mem_fn(&std::string::c_str));
636 try {
637 auto problems = dnf_sack_filter_modules_v2(self->sack, moduleContainer, hotfixReposCString.data(),
638 installRoot, platformModule, updateOnly, debugSolver, moduleObsoletes);
639 if (problems.second == libdnf::ModulePackageContainer::ModuleErrorType::NO_ERROR) {
640 PyObject * returnTuple = PyTuple_New(0);
641 return returnTuple;
642 }
643 PyObject * returnTuple = PyTuple_New(2);
644 PyTuple_SetItem(returnTuple, 0, problemRulesPyConverter(problems.first));
645 PyTuple_SetItem(returnTuple, 1, PyLong_FromLong(int(problems.second)));
646 return returnTuple;
647 } catch (libdnf::ModulePackageContainer::ConflictException & exception) {
648 PyErr_SetString(HyExc_Runtime, exception.what());
649 return NULL;
650 }
651 } CATCH_TO_PYTHON
652
653
654 static PyObject *
set_modules_enabled_by_pkgset(_SackObject * self,PyObject * args,PyObject * kwds)655 set_modules_enabled_by_pkgset(_SackObject *self, PyObject *args, PyObject *kwds) try
656 {
657 const char *kwlist[] = {"module_container", "pkgs", NULL};
658 PyObject * pyModuleContainer;
659 PyObject * pyPkgSet;
660
661 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO", (char**) kwlist, &pyModuleContainer,
662 &pyPkgSet))
663 return NULL;
664 auto pset = pyseq_to_packageset(pyPkgSet, self->sack);
665 if (!pset) {
666 return NULL;
667 }
668
669 UniquePtrPyObject thisPyModuleContainer(PyObject_GetAttrString(pyModuleContainer, "this"));
670 auto swigContainer = reinterpret_cast< ModulePackageContainerPyObject * >(thisPyModuleContainer.get());
671 auto moduleContainer = swigContainer->ptr;
672 auto modules = moduleContainer->requiresModuleEnablement(*pset.get());
673 moduleContainer->enableDependencyTree(modules);
674 Py_RETURN_NONE;
675 } CATCH_TO_PYTHON
676
677 typedef struct {
678 PyObject_HEAD
679 libdnf::Repo *ptr;
680 void *ty;
681 int own;
682 PyObject *next;
683 } RepoSwigPyObject;
684
685 static PyObject *
load_system_repo(_SackObject * self,PyObject * args,PyObject * kwds)686 load_system_repo(_SackObject *self, PyObject *args, PyObject *kwds)
687 {
688 g_autoptr(GError) error = NULL;
689 const char *kwlist[] = {"repo", "build_cache", "load_filelists", "load_presto",
690 NULL};
691
692 PyObject * repoPyObj = NULL;
693 libdnf::Repo * crepo = NULL;
694 int build_cache = 0, unused_1 = 0, unused_2 = 0;
695 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|Oiii", (char**) kwlist,
696 &repoPyObj,
697 &build_cache, &unused_1, &unused_2))
698 return 0;
699
700 if (repoPyObj) {
701
702 // Is it old deprecated _hawkey.Repo object?
703 crepo = repoFromPyObject(repoPyObj);
704
705 // Or is it swig object?
706 if (!crepo) {
707 UniquePtrPyObject thisRepoPyObj(PyObject_GetAttrString(repoPyObj, "this"));
708 auto repoSwigPyObj = reinterpret_cast<RepoSwigPyObject *>(thisRepoPyObj.get());
709 if (!repoSwigPyObj) {
710 PyErr_SetString(PyExc_SystemError, "Unable to parse repoSwigPyObject");
711 return NULL;
712 }
713
714 crepo = repoSwigPyObj->ptr;
715 if (!crepo) {
716 PyErr_SetString(PyExc_SystemError, "Unable to parse repo swig object");
717 return NULL;
718 }
719 }
720 }
721
722 int flags = 0;
723 if (build_cache)
724 flags |= DNF_SACK_LOAD_FLAG_BUILD_CACHE;
725
726 gboolean ret = dnf_sack_load_system_repo(self->sack, crepo, flags, &error);
727 if (!ret)
728 return op_error2exc(error);
729 Py_RETURN_NONE;
730 }
731
732 static PyObject *
load_repo(_SackObject * self,PyObject * args,PyObject * kwds)733 load_repo(_SackObject *self, PyObject *args, PyObject *kwds) try
734 {
735 const char *kwlist[] = {"repo", "build_cache", "load_filelists", "load_presto",
736 "load_updateinfo", "load_other", NULL};
737
738 PyObject * repoPyObj = NULL;
739 int build_cache = 0, load_filelists = 0, load_presto = 0, load_updateinfo = 0, load_other = 0;
740 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|iiiii", (char**) kwlist,
741 &repoPyObj,
742 &build_cache, &load_filelists,
743 &load_presto, &load_updateinfo, &load_other))
744 return 0;
745
746 // Is it old deprecated _hawkey.Repo object?
747 libdnf::Repo * crepo = repoFromPyObject(repoPyObj);
748
749 // Or is it swig object?
750 if (!crepo) {
751 UniquePtrPyObject thisRepoPyObj(PyObject_GetAttrString(repoPyObj, "this"));
752 auto repoSwigPyObj = reinterpret_cast<RepoSwigPyObject *>(thisRepoPyObj.get());
753 if (!repoSwigPyObj) {
754 PyErr_SetString(PyExc_SystemError, "Unable to parse repoSwigPyObject");
755 return NULL;
756 }
757
758 crepo = repoSwigPyObj->ptr;
759 if (!crepo) {
760 PyErr_SetString(PyExc_SystemError, "Unable to parse repo swig object");
761 return NULL;
762 }
763 }
764
765 int flags = 0;
766 gboolean ret = 0;
767 g_autoptr(GError) error = NULL;
768 if (build_cache)
769 flags |= DNF_SACK_LOAD_FLAG_BUILD_CACHE;
770 if (load_filelists)
771 flags |= DNF_SACK_LOAD_FLAG_USE_FILELISTS;
772 if (load_presto)
773 flags |= DNF_SACK_LOAD_FLAG_USE_PRESTO;
774 if (load_updateinfo)
775 flags |= DNF_SACK_LOAD_FLAG_USE_UPDATEINFO;
776 if (load_other)
777 flags |= DNF_SACK_LOAD_FLAG_USE_OTHER;
778 Py_BEGIN_ALLOW_THREADS;
779 ret = dnf_sack_load_repo(self->sack, crepo, flags, &error);
780 Py_END_ALLOW_THREADS;
781 if (!ret)
782 return op_error2exc(error);
783 Py_RETURN_NONE;
784 } CATCH_TO_PYTHON
785
786 static PyObject *
rpmdb_version(_SackObject * self,PyObject * unused)787 rpmdb_version(_SackObject *self, PyObject *unused) try
788 {
789 auto result = dnf_sack_get_rpmdb_version(self->sack);
790 return PyString_FromString(result.c_str());
791 } CATCH_TO_PYTHON
792
793 static Py_ssize_t
len(_SackObject * self)794 len(_SackObject *self) try
795 {
796 return dnf_sack_count(self->sack);
797 } CATCH_TO_PYTHON_INT
798
799 static PyObject *
deepcopy(_SackObject * self,PyObject * args,PyObject * kwds)800 deepcopy(_SackObject *self, PyObject *args, PyObject *kwds) try
801 {
802 PyErr_SetString(PyExc_NotImplementedError, "sack can't be deepcopied");
803 return NULL;
804 } CATCH_TO_PYTHON
805
806 static struct
807 PyMethodDef sack_methods[] = {
808 {"__deepcopy__", (PyCFunction)deepcopy, METH_KEYWORDS|METH_VARARGS,
809 NULL},
810 {"evr_cmp", (PyCFunction)evr_cmp, METH_VARARGS,
811 NULL},
812 {"get_running_kernel", (PyCFunction)get_running_kernel, METH_NOARGS,
813 NULL},
814 {"create_cmdline_repo", (PyCFunction)create_cmdline_repo, METH_NOARGS,
815 NULL},
816 {"create_package", (PyCFunction)create_package, METH_O,
817 NULL},
818 {"add_cmdline_package", (PyCFunction)add_cmdline_package, METH_O,
819 NULL},
820 {"add_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_add_excludes>, METH_O, NULL},
821 {"add_module_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_add_module_excludes>, METH_O,
822 NULL},
823 {"add_includes", (PyCFunction)modify_excl_incl<&dnf_sack_add_includes>, METH_O, NULL},
824 {"remove_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_remove_excludes>, METH_O, NULL},
825 {"remove_module_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_remove_module_excludes>,
826 METH_O, NULL},
827 {"remove_includes", (PyCFunction)modify_excl_incl<&dnf_sack_remove_includes>, METH_O, NULL},
828 {"set_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_set_excludes>, METH_O, NULL},
829 {"set_module_excludes", (PyCFunction)modify_excl_incl<&dnf_sack_set_module_excludes>, METH_O,
830 NULL},
831 {"set_includes", (PyCFunction)modify_excl_incl<&dnf_sack_set_includes>, METH_O, NULL},
832 {"reset_excludes", (PyCFunction)reset_excl_incl<&dnf_sack_reset_excludes>, METH_NOARGS,
833 NULL},
834 {"reset_module_excludes", (PyCFunction)reset_excl_incl<&dnf_sack_reset_module_excludes>,
835 METH_NOARGS, NULL},
836 {"reset_includes", (PyCFunction)reset_excl_incl<&dnf_sack_reset_includes>, METH_NOARGS,
837 NULL},
838 {"get_excludes", (PyCFunction)get_excl_incl<&dnf_sack_get_excludes>, METH_NOARGS,
839 NULL},
840 {"get_module_excludes", (PyCFunction)get_excl_incl<&dnf_sack_get_module_excludes>, METH_NOARGS,
841 NULL},
842 {"get_includes", (PyCFunction)get_excl_incl<&dnf_sack_get_includes>, METH_NOARGS,
843 NULL},
844 {"set_use_includes", (PyCFunction)set_use_includes, METH_VARARGS,
845 NULL},
846 {"get_use_includes", (PyCFunction)get_use_includes, METH_O,
847 NULL},
848 {"disable_repo", (PyCFunction)disable_repo, METH_O,
849 NULL},
850 {"enable_repo", (PyCFunction)enable_repo, METH_O,
851 NULL},
852 {"list_arches", (PyCFunction)list_arches, METH_NOARGS,
853 NULL},
854 {"filter_modules", (PyCFunction)filter_modules, METH_VARARGS | METH_KEYWORDS, NULL},
855 {"set_modules_enabled_by_pkgset", (PyCFunction)set_modules_enabled_by_pkgset,
856 METH_VARARGS | METH_KEYWORDS, NULL},
857 {"load_system_repo", (PyCFunction)load_system_repo,
858 METH_VARARGS | METH_KEYWORDS, NULL},
859 {"load_repo", (PyCFunction)load_repo, METH_VARARGS | METH_KEYWORDS,
860 NULL},
861 {"_rpmdb_version", (PyCFunction)rpmdb_version, METH_VARARGS | METH_KEYWORDS, NULL},
862 {NULL} /* sentinel */
863 };
864
865 PySequenceMethods sack_sequence = {
866 (lenfunc)len, /* sq_length */
867 };
868
869 PyTypeObject sack_Type = {
870 PyVarObject_HEAD_INIT(NULL, 0)
871 "_hawkey.Sack", /*tp_name*/
872 sizeof(_SackObject), /*tp_basicsize*/
873 0, /*tp_itemsize*/
874 (destructor) sack_dealloc, /*tp_dealloc*/
875 0, /*tp_print*/
876 0, /*tp_getattr*/
877 0, /*tp_setattr*/
878 0, /*tp_compare*/
879 0, /*tp_repr*/
880 0, /*tp_as_number*/
881 &sack_sequence, /*tp_as_sequence*/
882 0, /*tp_as_mapping*/
883 0, /*tp_hash */
884 0, /*tp_call*/
885 0, /*tp_str*/
886 0, /*tp_getattro*/
887 0, /*tp_setattro*/
888 0, /*tp_as_buffer*/
889 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
890 "Sack object", /* tp_doc */
891 0, /* tp_traverse */
892 0, /* tp_clear */
893 0, /* tp_richcompare */
894 0, /* tp_weaklistoffset */
895 PyObject_SelfIter, /* tp_iter */
896 0, /* tp_iternext */
897 sack_methods, /* tp_methods */
898 0, /* tp_members */
899 sack_getsetters, /* tp_getset */
900 0, /* tp_base */
901 0, /* tp_dict */
902 0, /* tp_descr_get */
903 0, /* tp_descr_set */
904 0, /* tp_dictoffset */
905 (initproc)sack_init, /* tp_init */
906 0, /* tp_alloc */
907 sack_new, /* tp_new */
908 0, /* tp_free */
909 0, /* tp_is_gc */
910 };
911