]> git.ipfire.org Git - pakfire.git/blob - src/_pakfire/_pakfiremodule.c
_pakfire: Add method to configure the log level
[pakfire.git] / src / _pakfire / _pakfiremodule.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2011 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <Python.h>
22 #include <libintl.h>
23 #include <syslog.h>
24
25 #include <pakfire/arch.h>
26 #include <pakfire/dependencies.h>
27 #include <pakfire/key.h>
28
29 #include "archive.h"
30 #include "archive_file.h"
31 #include "buildservice.h"
32 #include "errors.h"
33 #include "file.h"
34 #include "key.h"
35 #include "package.h"
36 #include "pakfire.h"
37 #include "problem.h"
38 #include "repo.h"
39 #include "solution.h"
40 #include "util.h"
41
42 PyMODINIT_FUNC PyInit__pakfire(void);
43
44 PyObject* PyExc_BadSignatureError;
45 PyObject* PyExc_CommandExecutionError;
46 PyObject* PyExc_DependencyError;
47
48 PyObject* PyExc_CheckError;
49 PyObject* PyExc_CheckFileVerificationError;
50
51 struct pakfire_ctx* pakfire_ctx = NULL;
52
53 static PyObject* setup_logger(void) {
54 PyObject* logging = NULL;
55 PyObject* logger = NULL;
56
57 // import logging
58 logging = PyImport_ImportModule("logging");
59 if (!logging)
60 goto ERROR;
61
62 // logging.getLogger("pakfire")
63 logger = PyObject_CallMethod(logging, "getLogger", "s", "pakfire");
64 if (!logger)
65 goto ERROR;
66
67 ERROR:
68 Py_XDECREF(logging);
69
70 return logger;
71 }
72
73 static void __pakfire_log_callback(void* data, int level, const char* file, int line,
74 const char* fn, const char* format, va_list args) {
75 PyObject* logger = (PyObject*)data;
76 PyObject* result = NULL;
77 char* buffer = NULL;
78 int r;
79
80 PyObject* exception = NULL;
81 PyObject* type = NULL;
82 PyObject* value = NULL;
83 PyObject* traceback = NULL;
84
85 // Do nothing if the logger does not exist
86 if (!logger)
87 return;
88
89 // Translate priority to Python logging priorities
90 switch (level) {
91 case LOG_DEBUG:
92 level = 10;
93 break;
94
95 case LOG_INFO:
96 level = 20;
97 break;
98
99 case LOG_ERR:
100 level = 40;
101 break;
102
103 // Drop messages of an unknown level
104 default:
105 return;
106 }
107
108 PyGILState_STATE state = PyGILState_Ensure();
109
110 // Format the log line
111 r = vasprintf(&buffer, format, args);
112 if (r < 0)
113 goto ERROR;
114
115 // Call the logger
116 result = PyObject_CallMethod(logger, "log", "is", level, buffer);
117 if (!result)
118 goto ERROR;
119
120 ERROR:
121 /*
122 We cannot really catch any Python errors here, since we cannot send
123 any error codes up the chain.
124
125 So, in order to debug the problem, We will check if an exception has
126 occurred and if so, print it to the console.
127 */
128 exception = PyErr_Occurred();
129 if (exception) {
130 PyErr_Print();
131
132 // Fetch the exception and destroy it
133 PyErr_Fetch(&type, &value, &traceback);
134
135 Py_XDECREF(type);
136 Py_XDECREF(value);
137 Py_XDECREF(traceback);
138 }
139
140 if (buffer)
141 free(buffer);
142 Py_XDECREF(result);
143
144 // Release the GIL
145 PyGILState_Release(state);
146 }
147
148 static int initialize_context(void) {
149 PyObject* logger = NULL;
150 int r;
151
152 // Create a new context
153 r = pakfire_ctx_create(&pakfire_ctx, NULL);
154 if (r) {
155 PyErr_SetFromErrno(PyExc_OSError);
156 goto ERROR;
157 }
158
159 // Setup the python logger
160 logger = setup_logger();
161 if (r)
162 goto ERROR;
163
164 // Pass all log messages to Python
165 pakfire_ctx_set_log_callback(pakfire_ctx, __pakfire_log_callback, logger);
166 Py_INCREF(logger);
167
168 // Set the log level to DEBUG
169 pakfire_ctx_set_log_level(pakfire_ctx, LOG_DEBUG);
170
171 goto CLEANUP;
172
173 ERROR:
174 if (pakfire_ctx)
175 pakfire_ctx_unref(pakfire_ctx);
176
177 CLEANUP:
178 Py_XDECREF(logger);
179
180 return r;
181 }
182
183 static PyObject* _pakfire_native_arch(void) {
184 const char* arch = pakfire_arch_native();
185 if (!arch)
186 Py_RETURN_NONE;
187
188 return PyUnicode_FromString(arch);
189 }
190
191 static PyObject* _pakfire_set_log_level(PyObject* self, PyObject* args) {
192 int level = 0;
193
194 if (!PyArg_ParseTuple(args, "i", &level))
195 return NULL;
196
197 // Set the log level
198 pakfire_ctx_set_log_level(pakfire_ctx, level);
199
200 Py_RETURN_NONE;
201 }
202
203 static PyObject* _pakfire_supported_arches(void) {
204 int r;
205
206 // Fetch all architectures
207 const char** arches = pakfire_supported_arches();
208 if (!arches) {
209 PyErr_SetFromErrno(PyExc_OSError);
210 return NULL;
211 }
212
213 // Create a new list
214 PyObject* list = PyList_New(0);
215 if (!list)
216 return NULL;
217
218 // Append all architectures
219 for (const char** arch = arches; *arch; arch++) {
220 PyObject* object = PyUnicode_FromString(*arch);
221 if (!object)
222 goto ERROR;
223
224 // Append to list
225 r = PyList_Append(list, object);
226 if (r) {
227 Py_DECREF(object);
228 goto ERROR;
229 }
230
231 Py_DECREF(object);
232 }
233
234 return list;
235
236 ERROR:
237 Py_DECREF(list);
238 return NULL;
239 }
240
241 static PyObject* _pakfire_version_compare(PyObject* self, PyObject* args) {
242 const char* evr1 = NULL;
243 const char* evr2 = NULL;
244 int r;
245
246 if (!PyArg_ParseTuple(args, "ss", &evr1, &evr2))
247 return NULL;
248
249 // Compare versions
250 r = pakfire_static_version_compare(evr1, evr2);
251
252 // Return the return code
253 return PyLong_FromLong(r);
254 }
255
256 static PyMethodDef pakfireModuleMethods[] = {
257 {"native_arch", (PyCFunction)_pakfire_native_arch, METH_NOARGS, NULL },
258 {
259 "set_log_level",
260 (PyCFunction)_pakfire_set_log_level,
261 METH_VARARGS,
262 NULL,
263 },
264 {"supported_arches", (PyCFunction)_pakfire_supported_arches, METH_NOARGS, NULL },
265 {
266 "version_compare",
267 (PyCFunction)_pakfire_version_compare,
268 METH_VARARGS,
269 NULL,
270 },
271 { NULL, NULL, 0, NULL }
272 };
273
274 static struct PyModuleDef moduledef = {
275 .m_base = PyModuleDef_HEAD_INIT,
276 .m_name = "_pakfire",
277 .m_size = -1,
278 .m_methods = pakfireModuleMethods,
279 };
280
281 PyMODINIT_FUNC PyInit__pakfire(void) {
282 int r;
283
284 /* Initialize locale */
285 setlocale(LC_ALL, "");
286 bindtextdomain(PACKAGE_TARNAME, "/usr/share/locale");
287 textdomain(PACKAGE_TARNAME);
288
289 // Create the module
290 PyObject* module = PyModule_Create(&moduledef);
291 if (!module)
292 return NULL;
293
294 PyExc_BadSignatureError = PyErr_NewException("_pakfire.BadSignatureError", NULL, NULL);
295 Py_INCREF(PyExc_BadSignatureError);
296 PyModule_AddObject(module, "BadSignatureError", PyExc_BadSignatureError);
297
298 PyExc_CommandExecutionError = PyErr_NewException("_pakfire.CommandExecutionError", NULL, NULL);
299 Py_INCREF(PyExc_CommandExecutionError);
300 PyModule_AddObject(module, "CommandExecutionError", PyExc_CommandExecutionError);
301
302 PyExc_DependencyError = PyErr_NewException("_pakfire.DependencyError", NULL, NULL);
303 Py_INCREF(PyExc_DependencyError);
304 PyModule_AddObject(module, "DependencyError", PyExc_DependencyError);
305
306 // Check Exceptions
307
308 // CheckError
309 PyExc_CheckError = PyErr_NewException("_pakfire.CheckError", NULL, NULL);
310 Py_INCREF(PyExc_CheckError);
311 if (PyModule_AddObject(module, "CheckError", PyExc_CheckError) < 0) {
312 Py_XDECREF(PyExc_CheckError);
313 Py_CLEAR(PyExc_CheckError);
314 goto ERROR;
315 }
316
317 // CheckFileVerificationError based on CheckError
318 PyExc_CheckFileVerificationError = PyErr_NewExceptionWithDoc(
319 "_pakfire.CheckFileVerificationError",
320 "The file verification process failed",
321 PyExc_CheckError,
322 NULL
323 );
324 Py_INCREF(PyExc_CheckFileVerificationError);
325 if (PyModule_AddObject(module, "CheckFileVerificationError", PyExc_CheckFileVerificationError) < 0) {
326 Py_XDECREF(PyExc_CheckFileVerificationError);
327 Py_CLEAR(PyExc_CheckFileVerificationError);
328 goto ERROR;
329 }
330
331 // Pakfire
332 if (PyType_Ready(&PakfireType) < 0)
333 return NULL;
334
335 Py_INCREF(&PakfireType);
336 PyModule_AddObject(module, "Pakfire", (PyObject *)&PakfireType);
337
338 // Archive
339 if (PyType_Ready(&ArchiveType) < 0)
340 return NULL;
341
342 Py_INCREF(&ArchiveType);
343 PyModule_AddObject(module, "Archive", (PyObject *)&ArchiveType);
344
345 // Archive File
346 if (PyType_Ready(&ArchiveFileType) < 0)
347 return NULL;
348
349 Py_INCREF(&ArchiveFileType);
350 PyModule_AddObject(module, "ArchiveFile", (PyObject*)&ArchiveFileType);
351
352 // BuildService
353 if (PyType_Ready(&BuildServiceType) < 0)
354 return NULL;
355
356 Py_INCREF(&BuildServiceType);
357 PyModule_AddObject(module, "BuildService", (PyObject*)&BuildServiceType);
358
359 // File
360 if (PyType_Ready(&FileType) < 0)
361 return NULL;
362
363 Py_INCREF(&FileType);
364 PyModule_AddObject(module, "File", (PyObject *)&FileType);
365
366 // Key
367 if (PyType_Ready(&KeyType) < 0)
368 return NULL;
369
370 Py_INCREF(&KeyType);
371 PyModule_AddObject(module, "Key", (PyObject *)&KeyType);
372
373 // Package
374 if (PyType_Ready(&PackageType) < 0)
375 return NULL;
376
377 Py_INCREF(&PackageType);
378 PyModule_AddObject(module, "Package", (PyObject *)&PackageType);
379
380 // Problem
381 if (PyType_Ready(&ProblemType) < 0)
382 return NULL;
383 Py_INCREF(&ProblemType);
384 PyModule_AddObject(module, "Problem", (PyObject *)&ProblemType);
385
386 // Repo
387 if (PyType_Ready(&RepoType) < 0)
388 return NULL;
389
390 Py_INCREF(&RepoType);
391 PyModule_AddObject(module, "Repo", (PyObject *)&RepoType);
392
393 // Solution
394 if (PyType_Ready(&SolutionType) < 0)
395 return NULL;
396
397 Py_INCREF(&SolutionType);
398 PyModule_AddObject(module, "Solution", (PyObject *)&SolutionType);
399
400 // Constants
401 if (PyModule_AddIntMacro(module, PAKFIRE_KEY_ALGO_NULL) < 0)
402 goto ERROR;
403 if (PyModule_AddIntMacro(module, PAKFIRE_KEY_ALGO_ED25519) < 0)
404 goto ERROR;
405
406 // Initialize the context
407 r = initialize_context();
408 if (r)
409 goto ERROR;
410
411 return module;
412
413 ERROR:
414 Py_DECREF(module);
415
416 return NULL;
417 }