]>
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 | |
95ccfdc4 MT |
125 | if (self->ctx) |
126 | pakfire_ctx_set_log_callback(self->ctx, Ctx_log_callback, self->logger); | |
3dd7ad1e MT |
127 | } |
128 | ||
129 | static 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 |
150 | ERROR: |
151 | Py_XDECREF(logging); | |
152 | Py_XDECREF(logger); | |
153 | ||
154 | return r; | |
155 | } | |
156 | ||
049fe436 MT |
157 | static 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 | ||
186 | static 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 | ||
198 | static 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 | ||
210 | static 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 | 231 | static 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 | ||
241 | static 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 | ||
252 | PyTypeObject 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 | }; |