]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
3dd7ad1e MT |
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; | |
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 | ||
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 | ||
95ccfdc4 MT |
146 | // Success |
147 | r = 0; | |
148 | ||
3dd7ad1e MT |
149 | ERROR: |
150 | Py_XDECREF(logging); | |
151 | Py_XDECREF(logger); | |
152 | ||
153 | return r; | |
154 | } | |
155 | ||
049fe436 MT |
156 | static 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 | ||
185 | static 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 | ||
200 | static 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 | ||
212 | static 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 | 233 | static 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 | ||
243 | static 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 | ||
254 | PyTypeObject 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 | }; |