1 /*
2 * Python binding for the packet.c module.
3 *
4 * This file is Copyright (c) 2010-2018 by the GPSD project
5 * SPDX-License-Identifier: BSD-2-clause
6 *
7 */
8
9 #include "gpsd_config.h" /* must be before all includes */
10
11 /* Python.h insists on setting GNU_SOURCE, _POSIX_C_SOURCE and
12 * _XOPEN_SOURCE. Without guards. */
13 #undef _GNU_SOURCE
14 #undef _POSIX_C_SOURCE
15 #undef _XOPEN_SOURCE
16
17 #include <Python.h>
18
19 #include <stdio.h>
20 #include "gpsd.h"
21 #include "python_compatibility.h"
22
23 static PyObject *ErrorObject = NULL;
24
25 static PyObject *report_callback = NULL;
26
basic_report(const char * buf)27 static void basic_report(const char *buf)
28 {
29 (void)fputs(buf, stderr);
30 }
31
errout_reset(struct gpsd_errout_t * errout)32 void errout_reset(struct gpsd_errout_t *errout)
33 {
34 errout->debug = LOG_SHOUT;
35 errout->report = basic_report;
36 }
37
gpsd_log(int errlevel,const struct gpsd_errout_t * errout UNUSED,const char * fmt,...)38 void gpsd_log(int errlevel,
39 const struct gpsd_errout_t *errout UNUSED,
40 const char *fmt, ... )
41 {
42 char buf[BUFSIZ];
43 PyObject *args;
44 va_list ap;
45
46 if (!report_callback) /* no callback defined, exit early */
47 return;
48
49 if (!PyCallable_Check(report_callback)) {
50 PyErr_SetString(ErrorObject, "Cannot call Python callback function");
51 return;
52 }
53
54 va_start(ap, fmt);
55 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
56 va_end(ap);
57
58 args = Py_BuildValue("(is)", errlevel, buf);
59 if (!args)
60 return;
61
62 PyObject *result = PyObject_Call(report_callback, args, NULL);
63 Py_XDECREF(result);
64 Py_DECREF(args);
65 }
66
67
68 static PyTypeObject Lexer_Type;
69
70 typedef struct {
71 PyObject_HEAD
72 struct gps_lexer_t lexer;
73 } LexerObject;
74
75 static LexerObject *
newLexerObject(PyObject * arg UNUSED)76 newLexerObject(PyObject *arg UNUSED)
77 {
78 LexerObject *self;
79 self = PyObject_New(LexerObject, &Lexer_Type);
80 if (self == NULL)
81 return NULL;
82 memset(&self->lexer, 0, sizeof(struct gps_lexer_t));
83 packet_reset(&self->lexer);
84 return self;
85 }
86
87 /* Lexer methods */
88
89 static int
Lexer_init(LexerObject * self)90 Lexer_init(LexerObject *self)
91 {
92 packet_reset(&self->lexer);
93 return 0;
94 }
95
96 static PyObject *
Lexer_get(LexerObject * self,PyObject * args)97 Lexer_get(LexerObject *self, PyObject *args)
98 {
99 ssize_t len;
100 int fd;
101
102 if (!PyArg_ParseTuple(args, "i;missing or invalid file descriptor argument to gps.packet.get", &fd))
103 return NULL;
104
105 len = packet_get(fd, &self->lexer);
106 if (PyErr_Occurred())
107 return NULL;
108
109 return Py_BuildValue("(i, i, " GPSD_PY_BYTE_FORMAT ", i)",
110 len,
111 self->lexer.type,
112 self->lexer.outbuffer,
113 self->lexer.outbuflen,
114 self->lexer.char_counter);
115 }
116
117 static PyObject *
Lexer_reset(LexerObject * self)118 Lexer_reset(LexerObject *self)
119 {
120 packet_reset(&self->lexer);
121 if (PyErr_Occurred())
122 return NULL;
123 return 0;
124 }
125
126 static void
Lexer_dealloc(LexerObject * self)127 Lexer_dealloc(LexerObject *self)
128 {
129 PyObject_Del(self);
130 }
131
132 static PyMethodDef Lexer_methods[] = {
133 {"get", (PyCFunction)Lexer_get, METH_VARARGS,
134 PyDoc_STR("Get a packet from a file descriptor.")},
135 {"reset", (PyCFunction)Lexer_reset, METH_NOARGS,
136 PyDoc_STR("Reset the packet lexer to ground state.")},
137 {NULL, NULL} /* sentinel */
138 };
139
140 PyDoc_STRVAR(Lexer__doc__,
141 "GPS packet lexer object\n\
142 \n\
143 Fetch a single packet from file descriptor");
144
145 static PyTypeObject Lexer_Type = {
146 /* The ob_type field must be initialized in the module init function
147 * to be portable to Windows without using C++. */
148 PyVarObject_HEAD_INIT(NULL, 0)
149 "gps.packet.lexer", /*tp_name*/
150 sizeof(LexerObject), /*tp_basicsize*/
151 0, /*tp_itemsize*/
152 /* methods */
153 (destructor)Lexer_dealloc, /*tp_dealloc*/
154 0, /*tp_print*/
155 0, /*tp_getattr*/
156 0, /*tp_setattr*/
157 0, /*tp_compare*/
158 0, /*tp_repr*/
159 0, /*tp_as_number*/
160 0, /*tp_as_sequence*/
161 0, /*tp_as_mapping*/
162 0, /*tp_hash*/
163 0, /*tp_call*/
164 0, /*tp_str*/
165 PyObject_GenericGetAttr, /*tp_getattro*/
166 0, /*tp_setattro*/
167 0, /*tp_as_buffer*/
168 Py_TPFLAGS_DEFAULT, /*tp_flags*/
169 Lexer__doc__, /*tp_doc*/
170 0, /*tp_traverse*/
171 0, /*tp_clear*/
172 0, /*tp_richcompare*/
173 0, /*tp_weaklistoffset*/
174 0, /*tp_iter*/
175 0, /*tp_iternext*/
176 Lexer_methods, /*tp_methods*/
177 0, /*tp_members*/
178 0, /*tp_getset*/
179 0, /*tp_base*/
180 0, /*tp_dict*/
181 0, /*tp_descr_get*/
182 0, /*tp_descr_set*/
183 0, /*tp_dictoffset*/
184 (initproc)Lexer_init, /*tp_init*/
185 0, /*tp_alloc*/
186 0, /*tp_new*/
187 0, /*tp_free*/
188 0, /*tp_is_gc*/
189 };
190
191 /* Function of no arguments returning new Lexer object */
192
193 static PyObject *
gpspacket_new(PyObject * self UNUSED,PyObject * args UNUSED)194 gpspacket_new(PyObject *self UNUSED, PyObject *args UNUSED)
195 {
196 LexerObject *rv;
197
198 if (!PyArg_ParseTuple(args, ":new"))
199 return NULL;
200 rv = newLexerObject(args);
201 if (rv == NULL)
202 return NULL;
203 return (PyObject *)rv;
204 }
205
206 PyDoc_STRVAR(register_report__doc__,
207 "register_report(callback)\n\
208 \n\
209 callback must be a callable object expecting a string as parameter.");
210
211 static PyObject *
register_report(LexerObject * self UNUSED,PyObject * args)212 register_report(LexerObject *self UNUSED, PyObject *args)
213 {
214 PyObject *callback = NULL;
215
216 if (!PyArg_ParseTuple(args, "O:register_report", &callback))
217 return NULL;
218
219 if (!PyCallable_Check(callback)) {
220 PyErr_SetString(PyExc_TypeError, "First argument must be callable");
221 return NULL;
222 }
223
224 if (report_callback) {
225 Py_DECREF(report_callback);
226 report_callback = NULL;
227 }
228
229 report_callback = callback;
230 Py_INCREF(report_callback);
231
232 Py_INCREF(Py_None);
233 return Py_None;
234 }
235
236
237 /* List of functions defined in the module */
238
239 static PyMethodDef packet_methods[] = {
240 {"new", gpspacket_new, METH_VARARGS,
241 PyDoc_STR("new() -> new packet-lexer object")},
242 {"register_report", (PyCFunction)register_report, METH_VARARGS,
243 register_report__doc__},
244 {NULL, NULL} /* sentinel */
245 };
246
247 PyDoc_STRVAR(module_doc,
248 "Python binding of the libgpsd module for recognizing GPS packets.\n\
249 The new() function returns a new packet-lexer instance. Lexer instances\n\
250 have two methods:\n\
251 get() takes a file descriptor argument and returns a tuple consisting of\n\
252 the integer packet type and string packet value. On end of file it returns\n\
253 (-1, '').\n\
254 reset() resets the packet-lexer to its initial state.\n\
255 The module also has a register_report() function that accepts a callback\n\
256 for debug message reporting. The callback will get two arguments, the error\n\
257 level of the message and the message itself.\n\
258 ");
259
260 /* banishes a pointless compiler warning */
261 extern PyMODINIT_FUNC initpacket(void);
262
263 // cppcheck-suppress unusedFunction
GPSD_PY_MODULE_INIT(packet)264 GPSD_PY_MODULE_INIT(packet)
265 {
266 PyObject *m;
267
268 /* Create the module and add the functions */
269 GPSD_PY_MODULE_DEF(m, "packet", module_doc, packet_methods)
270
271 if (m == NULL || PyType_Ready(&Lexer_Type) < 0)
272 return GPSD_PY_MODULE_ERROR_VAL;
273
274 PyModule_AddIntConstant(m, "BAD_PACKET", BAD_PACKET);
275 PyModule_AddIntConstant(m, "COMMENT_PACKET", COMMENT_PACKET);
276 PyModule_AddIntConstant(m, "NMEA_PACKET", NMEA_PACKET);
277 PyModule_AddIntConstant(m, "AIVDM_PACKET", AIVDM_PACKET);
278 PyModule_AddIntConstant(m, "GARMINTXT_PACKET", GARMINTXT_PACKET);
279 PyModule_AddIntConstant(m, "SIRF_PACKET", SIRF_PACKET);
280 PyModule_AddIntConstant(m, "ZODIAC_PACKET", ZODIAC_PACKET);
281 PyModule_AddIntConstant(m, "TSIP_PACKET", TSIP_PACKET);
282 PyModule_AddIntConstant(m, "EVERMORE_PACKET", EVERMORE_PACKET);
283 PyModule_AddIntConstant(m, "ITALK_PACKET", ITALK_PACKET);
284 PyModule_AddIntConstant(m, "GARMIN_PACKET", GARMIN_PACKET);
285 PyModule_AddIntConstant(m, "NAVCOM_PACKET", NAVCOM_PACKET);
286 PyModule_AddIntConstant(m, "UBX_PACKET", UBX_PACKET);
287 PyModule_AddIntConstant(m, "SUPERSTAR2_PACKET", SUPERSTAR2_PACKET);
288 PyModule_AddIntConstant(m, "ONCORE_PACKET", ONCORE_PACKET);
289 PyModule_AddIntConstant(m, "GEOSTAR_PACKET", GEOSTAR_PACKET);
290 PyModule_AddIntConstant(m, "RTCM2_PACKET", RTCM2_PACKET);
291 PyModule_AddIntConstant(m, "RTCM3_PACKET", RTCM3_PACKET);
292 PyModule_AddIntConstant(m, "JSON_PACKET", JSON_PACKET);
293 PyModule_AddIntConstant(m, "PACKET_TYPES", PACKET_TYPES);
294
295 PyModule_AddIntConstant(m, "LOG_ERROR", LOG_ERROR);
296 PyModule_AddIntConstant(m, "LOG_SHOUT", LOG_SHOUT);
297 PyModule_AddIntConstant(m, "LOG_WARN", LOG_WARN);
298 PyModule_AddIntConstant(m, "LOG_CLIENT", LOG_CLIENT);
299 PyModule_AddIntConstant(m, "LOG_INF", LOG_INF);
300 PyModule_AddIntConstant(m, "LOG_PROG", LOG_PROG);
301 PyModule_AddIntConstant(m, "LOG_IO", LOG_IO);
302 PyModule_AddIntConstant(m, "LOG_DATA", LOG_DATA);
303 PyModule_AddIntConstant(m, "LOG_SPIN", LOG_SPIN);
304 PyModule_AddIntConstant(m, "LOG_RAW", LOG_RAW);
305
306 return GPSD_PY_MODULE_SUCCESS_VAL(m);
307 }
308