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