]> git.ipfire.org Git - pakfire.git/blame - src/_pakfire/ctx.c
python: ctx: Cleanup code that dereferences the logging function
[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
c5273b4c 125 pakfire_ctx_set_log_callback(self->ctx, Ctx_log_callback, self->logger);
3dd7ad1e
MT
126}
127
128static int Ctx_setup_logging(CtxObject* self) {
129 PyObject* logging = NULL;
130 PyObject* logger = NULL;
131 int r = -1;
132
133 // import logging
134 logging = PyImport_ImportModule("logging");
135 if (!logging)
136 goto ERROR;
137
138 // logging.getLogger("pakfire")
139 logger = PyObject_CallMethod(logging, "getLogger", "s", "pakfire");
140 if (!logger)
141 goto ERROR;
142
143 // Set default logger
144 Ctx___set_logger(self, logger);
145
95ccfdc4
MT
146 // Success
147 r = 0;
148
3dd7ad1e
MT
149ERROR:
150 Py_XDECREF(logging);
151 Py_XDECREF(logger);
152
153 return r;
154}
155
049fe436
MT
156static int Ctx_init(CtxObject* self, PyObject* args, PyObject* kwargs) {
157 char* kwlist[] = { (char*)"path", NULL };
158 const char* path = NULL;
159 int r;
160
161 // Parse arguments
162 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &path))
163 return -1;
164
165 // Create context
166 r = pakfire_ctx_create(&self->ctx, path);
167 if (r) {
168 errno = -r;
169
170 PyErr_SetFromErrno(PyExc_OSError);
171 return -1;
172 }
173
3dd7ad1e
MT
174 // Set the log level to DEBUG
175 pakfire_ctx_set_log_level(self->ctx, LOG_DEBUG);
176
95ccfdc4
MT
177 // Setup the default logger
178 r = Ctx_setup_logging(self);
179 if (r)
180 return r;
181
049fe436
MT
182 return 0;
183}
184
185static void Ctx_dealloc(CtxObject* self) {
c5273b4c
MT
186 if (self->ctx) {
187 // Reset the logger
188 pakfire_ctx_set_log_callback(self->ctx, NULL, NULL);
95ccfdc4 189
049fe436 190 pakfire_ctx_unref(self->ctx);
c5273b4c
MT
191 }
192
193 Py_XDECREF(self->logger);
049fe436
MT
194
195 Py_TYPE(self)->tp_free((PyObject *)self);
196}
197
3dd7ad1e
MT
198// Logging
199
200static PyObject* Ctx_set_logger(CtxObject* self, PyObject* args) {
201 PyObject* logger = NULL;
202
203 if (!PyArg_ParseTuple(args, "O", &logger))
204 return NULL;
205
3dd7ad1e
MT
206 // Set the logger
207 Ctx___set_logger(self, logger);
208
209 Py_RETURN_NONE;
210}
211
212static PyObject* Ctx_set_cache_path(CtxObject* self, PyObject* value) {
213 const char* path = NULL;
214 int r;
215
216 // Fetch the path
217 path = PyUnicode_AsUTF8(value);
218 if (!path)
219 return NULL;
220
221 // Set the cache path
222 r = pakfire_ctx_set_cache_path(self->ctx, path);
223 if (r) {
224 errno = -r;
225 PyErr_SetFromErrno(PyExc_OSError);
226
227 return NULL;
228 }
229
230 Py_RETURN_NONE;
231}
232
049fe436 233static struct PyMethodDef Ctx_methods[] = {
3dd7ad1e
MT
234 {
235 "set_logger",
236 (PyCFunction)Ctx_set_logger,
237 METH_VARARGS,
238 NULL,
239 },
049fe436
MT
240 { NULL },
241};
242
243static struct PyGetSetDef Ctx_getsetters[] = {
3dd7ad1e
MT
244 {
245 "cache_path",
246 NULL,
247 (setter)Ctx_set_cache_path,
248 NULL,
249 NULL,
250 },
049fe436
MT
251 { NULL },
252};
253
254PyTypeObject CtxType = {
255 PyVarObject_HEAD_INIT(NULL, 0)
256 tp_name: "_pakfire.Ctx",
257 tp_basicsize: sizeof(CtxObject),
258 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
259 tp_new: Ctx_new,
260 tp_dealloc: (destructor)Ctx_dealloc,
261 tp_init: (initproc)Ctx_init,
262 tp_doc: "Ctx Object",
263 tp_methods: Ctx_methods,
264 tp_getset: Ctx_getsetters,
265};