]> git.ipfire.org Git - pakfire.git/blob - src/_pakfire/ctx.c
e8fd927bf3a65c93ffe6cb727da8f7d3d5fe1fdb
[pakfire.git] / src / _pakfire / ctx.c
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
21 #define PY_SSIZE_T_CLEAN
22 #include <Python.h>
23 #include <syslog.h>
24
25 #include <pakfire/ctx.h>
26
27 #include "ctx.h"
28
29 static 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
37 static 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
88 ERROR:
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
116 static 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;
122 Py_INCREF(self->logger);
123
124 // Set the logger
125 pakfire_ctx_set_log_callback(self->ctx, Ctx_log_callback, self->logger);
126 }
127
128 static 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
146 ERROR:
147 Py_XDECREF(logging);
148 Py_XDECREF(logger);
149
150 return r;
151 }
152
153 static int Ctx_init(CtxObject* self, PyObject* args, PyObject* kwargs) {
154 char* kwlist[] = { (char*)"path", NULL };
155 const char* path = NULL;
156 int r;
157
158 // Parse arguments
159 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", kwlist, &path))
160 return -1;
161
162 // Create context
163 r = pakfire_ctx_create(&self->ctx, path);
164 if (r) {
165 errno = -r;
166
167 PyErr_SetFromErrno(PyExc_OSError);
168 return -1;
169 }
170
171 // Set the log level to DEBUG
172 pakfire_ctx_set_log_level(self->ctx, LOG_DEBUG);
173
174 return 0;
175 }
176
177 static void Ctx_dealloc(CtxObject* self) {
178 if (self->ctx)
179 pakfire_ctx_unref(self->ctx);
180
181 Py_XDECREF(self->logger);
182
183 Py_TYPE(self)->tp_free((PyObject *)self);
184 }
185
186 // Logging
187
188 static PyObject* Ctx_set_logger(CtxObject* self, PyObject* args) {
189 PyObject* logger = NULL;
190
191 if (!PyArg_ParseTuple(args, "O", &logger))
192 return NULL;
193
194 // XXX Check if we have a log method
195
196 // Set the logger
197 Ctx___set_logger(self, logger);
198
199 Py_RETURN_NONE;
200 }
201
202 static PyObject* Ctx_set_cache_path(CtxObject* self, PyObject* value) {
203 const char* path = NULL;
204 int r;
205
206 // Fetch the path
207 path = PyUnicode_AsUTF8(value);
208 if (!path)
209 return NULL;
210
211 // Set the cache path
212 r = pakfire_ctx_set_cache_path(self->ctx, path);
213 if (r) {
214 errno = -r;
215 PyErr_SetFromErrno(PyExc_OSError);
216
217 return NULL;
218 }
219
220 Py_RETURN_NONE;
221 }
222
223 static struct PyMethodDef Ctx_methods[] = {
224 {
225 "set_logger",
226 (PyCFunction)Ctx_set_logger,
227 METH_VARARGS,
228 NULL,
229 },
230 { NULL },
231 };
232
233 static struct PyGetSetDef Ctx_getsetters[] = {
234 {
235 "cache_path",
236 NULL,
237 (setter)Ctx_set_cache_path,
238 NULL,
239 NULL,
240 },
241 { NULL },
242 };
243
244 PyTypeObject CtxType = {
245 PyVarObject_HEAD_INIT(NULL, 0)
246 tp_name: "_pakfire.Ctx",
247 tp_basicsize: sizeof(CtxObject),
248 tp_flags: Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE,
249 tp_new: Ctx_new,
250 tp_dealloc: (destructor)Ctx_dealloc,
251 tp_init: (initproc)Ctx_init,
252 tp_doc: "Ctx Object",
253 tp_methods: Ctx_methods,
254 tp_getset: Ctx_getsetters,
255 };