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