]> git.ipfire.org Git - pakfire.git/blame - src/_pakfire/ctx.c
python: ctx: Setup the default logger
[pakfire.git] / src / _pakfire / ctx.c
CommitLineData
049fe436
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
4# Copyright (C) 2023 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
3dd7ad1e 21#define PY_SSIZE_T_CLEAN
049fe436 22#include <Python.h>
3dd7ad1e 23#include <syslog.h>
049fe436
MT
24
25#include <pakfire/ctx.h>
26
27#include "ctx.h"
28
29static PyObject* Ctx_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
30 CtxObject* self = (CtxObject *)type->tp_alloc(type, 0);
31 if (self)
32 self->ctx = NULL;
33
34 return (PyObject *)self;
35}
36
3dd7ad1e
MT
37static void Ctx_log_callback(void* data, int level, const char* file, int line,
38 const char* fn, const char* format, va_list args) {
39 PyObject* logger = (PyObject*)data;
40 PyObject* result = NULL;
41 char* buffer = NULL;
42 int r;
43
44 PyObject* exception = NULL;
45 PyObject* type = NULL;
46 PyObject* value = NULL;
47 PyObject* traceback = NULL;
48
49 // Do nothing if the logger does not exist
50 if (!logger)
51 return;
52
53 // Translate priority to Python logging priorities
54 switch (level) {
55 case LOG_DEBUG:
56 level = 10;
57 break;
58
59 case LOG_INFO:
60 level = 20;
61 break;
62
63 case LOG_ERR:
64 level = 40;
65 break;
66
67 // Drop messages of an unknown level
68 default:
69 return;
70 }
71
72 PyGILState_STATE state = PyGILState_Ensure();
73
74 // Format the log line
75 r = vasprintf(&buffer, format, args);
76 if (r < 0)
77 goto ERROR;
78
79 // Remove trailing newline
80 if (buffer[r - 1] == '\n')
81 r--;
82
83 // Call the logger
84 result = PyObject_CallMethod(logger, "log", "is#", level, buffer, r);
85 if (!result)
86 goto ERROR;
87
88ERROR:
89 /*
90 We cannot really catch any Python errors here, since we cannot send
91 any error codes up the chain.
92
93 So, in order to debug the problem, We will check if an exception has
94 occurred and if so, print it to the console.
95 */
96 exception = PyErr_Occurred();
97 if (exception) {
98 PyErr_Print();
99
100 // Fetch the exception and destroy it
101 PyErr_Fetch(&type, &value, &traceback);
102
103 Py_XDECREF(type);
104 Py_XDECREF(value);
105 Py_XDECREF(traceback);
106 }
107
108 if (buffer)
109 free(buffer);
110 Py_XDECREF(result);
111
112 // Release the GIL
113 PyGILState_Release(state);
114}
115
116static void Ctx___set_logger(CtxObject* self, PyObject* logger) {
117 // Dereference the old logger
118 Py_XDECREF(self->logger);
119
120 // Store the new logger
121 self->logger = logger;
95ccfdc4 122 Py_XINCREF(self->logger);
3dd7ad1e
MT
123
124 // Set the logger
95ccfdc4
MT
125 if (self->ctx)
126 pakfire_ctx_set_log_callback(self->ctx, Ctx_log_callback, self->logger);
3dd7ad1e
MT
127}
128
129static int Ctx_setup_logging(CtxObject* self) {
130 PyObject* logging = NULL;
131 PyObject* logger = NULL;
132 int r = -1;
133
134 // import logging
135 logging = PyImport_ImportModule("logging");
136 if (!logging)
137 goto ERROR;
138
139 // logging.getLogger("pakfire")
140 logger = PyObject_CallMethod(logging, "getLogger", "s", "pakfire");
141 if (!logger)
142 goto ERROR;
143
144 // Set default logger
145 Ctx___set_logger(self, logger);
146
95ccfdc4
MT
147 // Success
148 r = 0;
149
3dd7ad1e
MT
150ERROR:
151 Py_XDECREF(logging);
152 Py_XDECREF(logger);
153
154 return r;
155}
156
049fe436
MT
157static int Ctx_init(CtxObject* self, PyObject* args, PyObject* kwargs) {
158 char* kwlist[] = { (char*)"path", NULL };
159 const char* path = NULL;
160 int r;
161
162 // Parse arguments
163 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &path))
164 return -1;
165
166 // Create context
167 r = pakfire_ctx_create(&self->ctx, path);
168 if (r) {
169 errno = -r;
170
171 PyErr_SetFromErrno(PyExc_OSError);
172 return -1;
173 }
174
3dd7ad1e
MT
175 // Set the log level to DEBUG
176 pakfire_ctx_set_log_level(self->ctx, LOG_DEBUG);
177
95ccfdc4
MT
178 // Setup the default logger
179 r = Ctx_setup_logging(self);
180 if (r)
181 return r;
182
049fe436
MT
183 return 0;
184}
185
186static void Ctx_dealloc(CtxObject* self) {
95ccfdc4
MT
187 // Reset the logger
188 Ctx___set_logger(self, NULL);
189
049fe436
MT
190 if (self->ctx)
191 pakfire_ctx_unref(self->ctx);
192
193 Py_TYPE(self)->tp_free((PyObject *)self);
194}
195
3dd7ad1e
MT
196// Logging
197
198static PyObject* Ctx_set_logger(CtxObject* self, PyObject* args) {
199 PyObject* logger = NULL;
200
201 if (!PyArg_ParseTuple(args, "O", &logger))
202 return NULL;
203
3dd7ad1e
MT
204 // Set the logger
205 Ctx___set_logger(self, logger);
206
207 Py_RETURN_NONE;
208}
209
210static PyObject* Ctx_set_cache_path(CtxObject* self, PyObject* value) {
211 const char* path = NULL;
212 int r;
213
214 // Fetch the path
215 path = PyUnicode_AsUTF8(value);
216 if (!path)
217 return NULL;
218
219 // Set the cache path
220 r = pakfire_ctx_set_cache_path(self->ctx, path);
221 if (r) {
222 errno = -r;
223 PyErr_SetFromErrno(PyExc_OSError);
224
225 return NULL;
226 }
227
228 Py_RETURN_NONE;
229}
230
049fe436 231static struct PyMethodDef Ctx_methods[] = {
3dd7ad1e
MT
232 {
233 "set_logger",
234 (PyCFunction)Ctx_set_logger,
235 METH_VARARGS,
236 NULL,
237 },
049fe436
MT
238 { NULL },
239};
240
241static struct PyGetSetDef Ctx_getsetters[] = {
3dd7ad1e
MT
242 {
243 "cache_path",
244 NULL,
245 (setter)Ctx_set_cache_path,
246 NULL,
247 NULL,
248 },
049fe436
MT
249 { NULL },
250};
251
252PyTypeObject CtxType = {
253 PyVarObject_HEAD_INIT(NULL, 0)
254 tp_name: "_pakfire.Ctx",
255 tp_basicsize: sizeof(CtxObject),
256 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
257 tp_new: Ctx_new,
258 tp_dealloc: (destructor)Ctx_dealloc,
259 tp_init: (initproc)Ctx_init,
260 tp_doc: "Ctx Object",
261 tp_methods: Ctx_methods,
262 tp_getset: Ctx_getsetters,
263};