1 /* libgps_sock.c -- client interface library for the gpsd daemon
2 *
3 * This file is Copyright (c) 2010-2018 by the GPSD project
4 * SPDX-License-Identifier: BSD-2-clause
5 */
6
7 #include "gpsd_config.h" /* must be before all includes */
8
9 #include <assert.h>
10 #include <ctype.h>
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <locale.h>
14 #include <math.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/select.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
24 #ifndef USE_QT
25 #ifdef HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
27 #endif /* HAVE_SYS_SOCKET_H */
28 #ifdef HAVE_WINSOCK2_H
29 #include <winsock2.h>
30 #endif /* HAVE_WINSOCK2_H */
31 #else
32 #include <QTcpSocket>
33 #endif /* USE_QT */
34
35 #include "gps.h"
36 #include "gpsd.h"
37 #include "libgps.h"
38 #include "strfuncs.h"
39 #include "timespec.h" /* for NS_IN_SEC */
40 #ifdef SOCKET_EXPORT_ENABLE
41 #include "gps_json.h"
42
43 struct privdata_t
44 {
45 bool newstyle;
46 /* data buffered from the last read */
47 ssize_t waiting;
48 char buffer[GPS_JSON_RESPONSE_MAX * 2];
49 #ifdef LIBGPS_DEBUG
50 int waitcount;
51 #endif /* LIBGPS_DEBUG */
52 };
53
54 #ifdef HAVE_WINSOCK2_H
55 static bool need_init = TRUE;
56 static bool need_finish = TRUE;
57
windows_init(void)58 static bool windows_init(void)
59 /* Ensure socket networking is initialized for Windows. */
60 {
61 WSADATA wsadata;
62 /* request access to Windows Sockets API version 2.2 */
63 int res = WSAStartup(MAKEWORD(2, 2), &wsadata);
64 if (res != 0) {
65 libgps_debug_trace((DEBUG_CALLS, "WSAStartup returns error %d\n", res));
66 }
67 return (res == 0);
68 }
69
windows_finish(void)70 static bool windows_finish(void)
71 /* Shutdown Windows Sockets. */
72 {
73 int res = WSACleanup();
74 if (res != 0) {
75 libgps_debug_trace((DEBUG_CALLS, "WSACleanup returns error %d\n", res));
76 }
77 return (res == 0);
78 }
79 #endif /* HAVE_WINSOCK2_H */
80
gps_sock_open(const char * host,const char * port,struct gps_data_t * gpsdata)81 int gps_sock_open(const char *host, const char *port,
82 struct gps_data_t *gpsdata)
83 {
84 if (!host)
85 host = "localhost";
86 if (!port)
87 port = DEFAULT_GPSD_PORT;
88
89 libgps_debug_trace((DEBUG_CALLS, "gps_sock_open(%s, %s)\n", host, port));
90
91 #ifndef USE_QT
92 #ifdef HAVE_WINSOCK2_H
93 if (need_init) {
94 need_init != windows_init();
95 }
96 #endif
97 if ((gpsdata->gps_fd =
98 netlib_connectsock(AF_UNSPEC, host, port, "tcp")) < 0) {
99 errno = gpsdata->gps_fd;
100 libgps_debug_trace((DEBUG_CALLS,
101 "netlib_connectsock() returns error %d\n",
102 errno));
103 return -1;
104 } else
105 libgps_debug_trace((DEBUG_CALLS,
106 "netlib_connectsock() returns socket on fd %d\n",
107 gpsdata->gps_fd));
108 #else /* HAVE_WINSOCK2_H */
109 QTcpSocket *sock = new QTcpSocket();
110 gpsdata->gps_fd = sock;
111 sock->connectToHost(host, QString(port).toInt());
112 if (!sock->waitForConnected())
113 qDebug() << "libgps::connect error: " << sock->errorString();
114 else
115 qDebug() << "libgps::connected!";
116 #endif /* USE_QT */
117
118 /* set up for line-buffered I/O over the daemon socket */
119 gpsdata->privdata = (void *)malloc(sizeof(struct privdata_t));
120 if (gpsdata->privdata == NULL)
121 return -1;
122 PRIVATE(gpsdata)->newstyle = false;
123 PRIVATE(gpsdata)->waiting = 0;
124 PRIVATE(gpsdata)->buffer[0] = 0;
125
126 #ifdef LIBGPS_DEBUG
127 PRIVATE(gpsdata)->waitcount = 0;
128 #endif /* LIBGPS_DEBUG */
129 return 0;
130 }
131
gps_sock_waiting(const struct gps_data_t * gpsdata,int timeout)132 bool gps_sock_waiting(const struct gps_data_t *gpsdata, int timeout)
133 /* is there input waiting from the GPS? */
134 /* timeout is in uSec */
135 {
136 #ifndef USE_QT
137 libgps_debug_trace((DEBUG_CALLS, "gps_waiting(%d): %d\n",
138 timeout, PRIVATE(gpsdata)->waitcount++));
139 if (PRIVATE(gpsdata)->waiting > 0)
140 return true;
141
142 /* all error conditions return "not waiting" -- crude but effective */
143 return nanowait(gpsdata->gps_fd, timeout * 1000);
144 #else
145 return ((QTcpSocket *) (gpsdata->gps_fd))->waitForReadyRead(timeout / 1000);
146 #endif
147 }
148
gps_sock_close(struct gps_data_t * gpsdata)149 int gps_sock_close(struct gps_data_t *gpsdata)
150 /* close a gpsd connection */
151 {
152 free(PRIVATE(gpsdata));
153 gpsdata->privdata = NULL;
154 #ifndef USE_QT
155 int status;
156 #ifdef HAVE_WINSOCK2_H
157 status = closesocket(gpsdata->gps_fd);
158 if (need_finish) {
159 need_finish != windows_finish();
160 }
161 #else
162 status = close(gpsdata->gps_fd);
163 #endif /* HAVE_WINSOCK2_H */
164 gpsdata->gps_fd = -1;
165 return status;
166 #else
167 QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
168 sock->disconnectFromHost();
169 delete sock;
170 gpsdata->gps_fd = NULL;
171 return 0;
172 #endif
173 }
174
gps_sock_read(struct gps_data_t * gpsdata,char * message,int message_len)175 int gps_sock_read(struct gps_data_t *gpsdata, char *message, int message_len)
176 /* wait for and read data being streamed from the daemon */
177 {
178 char *eol;
179 ssize_t response_length;
180 int status = -1;
181
182 errno = 0;
183 gpsdata->set &= ~PACKET_SET;
184
185 /* scan to find end of message (\n), or end of buffer */
186 for (eol = PRIVATE(gpsdata)->buffer;
187 eol < (PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting);
188 eol++) {
189 if ('\n' == *eol)
190 break;
191 }
192
193 if (*eol != '\n') {
194 /* no full message found, try to fill buffer */
195
196 #ifndef USE_QT
197 /* read data: return -1 if no data waiting or buffered, 0 otherwise */
198 status = (int)recv(gpsdata->gps_fd,
199 PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting,
200 sizeof(PRIVATE(gpsdata)->buffer) - PRIVATE(gpsdata)->waiting, 0);
201 #else
202 status =
203 ((QTcpSocket *) (gpsdata->gps_fd))->read(PRIVATE(gpsdata)->buffer +
204 PRIVATE(gpsdata)->waiting,
205 sizeof(PRIVATE(gpsdata)->buffer) - PRIVATE(gpsdata)->waiting);
206 #endif
207 #ifdef HAVE_WINSOCK2_H
208 int wserr = WSAGetLastError();
209 #endif /* HAVE_WINSOCK2_H */
210
211 #ifdef USE_QT
212 if (status < 0) {
213 /* All negative statuses are error for QT
214 *
215 * read: https://doc.qt.io/qt-5/qiodevice.html#read
216 *
217 * Reads at most maxSize bytes from the device into data,
218 * and returns the number of bytes read.
219 * If an error occurs, such as when attempting to read from
220 * a device opened in WriteOnly mode, this function returns -1.
221 *
222 * 0 is returned when no more data is available for reading.
223 * However, reading past the end of the stream is considered
224 * an error, so this function returns -1 in those cases
225 * (that is, reading on a closed socket or after a process
226 * has died).
227 */
228 return -1;
229 }
230
231 #else /* not USE_QT */
232 if (status <= 0) {
233 /* 0 or negative
234 *
235 * read:
236 * https://pubs.opengroup.org/onlinepubs/007908775/xsh/read.html
237 *
238 * If nbyte is 0, read() will return 0 and have no other results.
239 * ...
240 * When attempting to read a file (other than a pipe or FIFO)
241 * that supports non-blocking reads and has no data currently
242 * available:
243 * - If O_NONBLOCK is set,
244 * read() will return a -1 and set errno to [EAGAIN].
245 * - If O_NONBLOCK is clear,
246 * read() will block the calling thread until some
247 * data becomes available.
248 * - The use of the O_NONBLOCK flag has no effect if there
249 * is some data available.
250 * ...
251 * If a read() is interrupted by a signal before it reads any
252 * data, it will return -1 with errno set to [EINTR].
253 * If a read() is interrupted by a signal after it has
254 * successfully read some data, it will return the number of
255 * bytes read.
256 *
257 * recv:
258 * https://pubs.opengroup.org/onlinepubs/007908775/xns/recv.html
259 *
260 * If no messages are available at the socket and O_NONBLOCK
261 * is not set on the socket's file descriptor, recv() blocks
262 * until a message arrives.
263 * If no messages are available at the socket and O_NONBLOCK
264 * is set on the socket's file descriptor, recv() fails and
265 * sets errno to [EAGAIN] or [EWOULDBLOCK].
266 * ...
267 * Upon successful completion, recv() returns the length of
268 * the message in bytes. If no messages are available to be
269 * received and the peer has performed an orderly shutdown,
270 * recv() returns 0. Otherwise, -1 is returned and errno is
271 * set to indicate the error.
272 *
273 * Summary:
274 * if nbytes 0 and read return 0 -> out of the free buffer
275 * space but still didn't get correct json -> report an error
276 * -> return -1
277 * if read return 0 but requested some bytes to read -> other
278 *side disconnected -> report an error -> return -1
279 * if read return -1 and errno is in [EAGAIN, EINTR, EWOULDBLOCK]
280 * -> not an error, we'll retry later -> return 0
281 * if read return -1 and errno is not in [EAGAIN, EINTR,
282 * EWOULDBLOCK] -> error -> return -1
283 *
284 */
285
286 /*
287 * check for not error cases first: EAGAIN, EINTR, etc
288 */
289 if (status < 0) {
290 #ifdef HAVE_WINSOCK2_H
291 if (wserr == WSAEINTR || wserr == WSAEWOULDBLOCK)
292 return 0;
293 #else
294 if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
295 return 0;
296 #endif /* HAVE_WINSOCK2_H */
297 }
298
299 /* disconnect or error */
300 return -1;
301 }
302 #endif /* USE_QT */
303
304 /* if we just received data from the socket, it's in the buffer */
305 PRIVATE(gpsdata)->waiting += status;
306
307 /* there's new buffered data waiting, check for full message */
308 for (eol = PRIVATE(gpsdata)->buffer;
309 eol < (PRIVATE(gpsdata)->buffer + PRIVATE(gpsdata)->waiting);
310 eol++) {
311 if ('\n' == *eol)
312 break;
313 }
314 if (*eol != '\n')
315 /* still no full message, give up for now */
316 return 0;
317 }
318
319 /* eol now points to trailing \n in a full message */
320 *eol = '\0';
321 if (NULL != message) {
322 strlcpy(message, PRIVATE(gpsdata)->buffer, message_len);
323 }
324 (void)clock_gettime(CLOCK_REALTIME, &gpsdata->online);
325 /* unpack the JSON message */
326 status = gps_unpack(PRIVATE(gpsdata)->buffer, gpsdata);
327
328 /* why the 1? */
329 response_length = eol - PRIVATE(gpsdata)->buffer + 1;
330
331 /* calculate length of good data still in buffer */
332 PRIVATE(gpsdata)->waiting -= response_length;
333
334 if (1 > PRIVATE(gpsdata)->waiting) {
335 /* no waiting data, or overflow, clear the buffer, just in case */
336 *PRIVATE(gpsdata)->buffer = '\0';
337 PRIVATE(gpsdata)->waiting = 0;
338 } else {
339 memmove(PRIVATE(gpsdata)->buffer,
340 PRIVATE(gpsdata)->buffer + response_length,
341 PRIVATE(gpsdata)->waiting);
342 }
343 gpsdata->set |= PACKET_SET;
344
345 return (status == 0) ? (int)response_length : status;
346 }
347
gps_unpack(char * buf,struct gps_data_t * gpsdata)348 int gps_unpack(char *buf, struct gps_data_t *gpsdata)
349 /* unpack a gpsd response into a status structure, buf must be writeable.
350 * gps_unpack() currently returns 0 in all cases, but should it ever need to
351 * return an error status, it must be < 0.
352 */
353 {
354 libgps_debug_trace((DEBUG_CALLS, "gps_unpack(%s)\n", buf));
355
356 /* detect and process a JSON response */
357 if (buf[0] == '{') {
358 const char *jp = buf, **next = &jp;
359 while (next != NULL && *next != NULL && next[0][0] != '\0') {
360 libgps_debug_trace((DEBUG_CALLS,
361 "gps_unpack() segment parse '%s'\n",
362 *next));
363 if (libgps_json_unpack(*next, gpsdata, next) == -1)
364 break;
365 #ifdef LIBGPS_DEBUG
366 if (libgps_debuglevel >= 1)
367 libgps_dump_state(gpsdata);
368 #endif /* LIBGPS_DEBUG */
369
370 }
371 }
372
373 #ifndef USE_QT
374 libgps_debug_trace((DEBUG_CALLS,
375 "final flags: (0x%04x) %s\n",
376 gpsdata->set,gps_maskdump(gpsdata->set)));
377 #endif
378 return 0;
379 }
380
gps_sock_data(const struct gps_data_t * gpsdata)381 const char *gps_sock_data(const struct gps_data_t *gpsdata)
382 /* return the contents of the client data buffer */
383 {
384 /* no length data, so pretty useless... */
385 return PRIVATE(gpsdata)->buffer;
386 }
387
gps_sock_send(struct gps_data_t * gpsdata,const char * buf)388 int gps_sock_send(struct gps_data_t *gpsdata, const char *buf)
389 /* send a command to the gpsd instance */
390 {
391 #ifndef USE_QT
392 #ifdef HAVE_WINSOCK2_H
393 if (send(gpsdata->gps_fd, buf, strlen(buf), 0) == (ssize_t) strlen(buf))
394 #else
395 if (write(gpsdata->gps_fd, buf, strlen(buf)) == (ssize_t) strlen(buf))
396 #endif /* HAVE_WINSOCK2_H */
397 return 0;
398 else
399 return -1;
400 #else
401 QTcpSocket *sock = (QTcpSocket *) gpsdata->gps_fd;
402 sock->write(buf, strlen(buf));
403 if (sock->waitForBytesWritten())
404 return 0;
405 else {
406 qDebug() << "libgps::send error: " << sock->errorString();
407 return -1;
408 }
409 #endif
410 }
411
gps_sock_stream(struct gps_data_t * gpsdata,unsigned int flags,void * d)412 int gps_sock_stream(struct gps_data_t *gpsdata, unsigned int flags, void *d)
413 /* ask gpsd to stream reports at you, hiding the command details */
414 {
415 char buf[GPS_JSON_COMMAND_MAX];
416
417 if ((flags & (WATCH_JSON | WATCH_NMEA | WATCH_RAW)) == 0) {
418 flags |= WATCH_JSON;
419 }
420 if ((flags & WATCH_DISABLE) != 0) {
421 (void)strlcpy(buf, "?WATCH={\"enable\":false,", sizeof(buf));
422 if (flags & WATCH_JSON)
423 (void)strlcat(buf, "\"json\":false,", sizeof(buf));
424 if (flags & WATCH_NMEA)
425 (void)strlcat(buf, "\"nmea\":false,", sizeof(buf));
426 if (flags & WATCH_RAW)
427 (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
428 if (flags & WATCH_RARE)
429 (void)strlcat(buf, "\"raw\":0,", sizeof(buf));
430 if (flags & WATCH_SCALED)
431 (void)strlcat(buf, "\"scaled\":false,", sizeof(buf));
432 if (flags & WATCH_TIMING)
433 (void)strlcat(buf, "\"timing\":false,", sizeof(buf));
434 if (flags & WATCH_SPLIT24)
435 (void)strlcat(buf, "\"split24\":false,", sizeof(buf));
436 if (flags & WATCH_PPS)
437 (void)strlcat(buf, "\"pps\":false,", sizeof(buf));
438 str_rstrip_char(buf, ',');
439 (void)strlcat(buf, "};", sizeof(buf));
440 libgps_debug_trace((DEBUG_CALLS,
441 "gps_stream() disable command: %s\n", buf));
442 return gps_send(gpsdata, buf);
443 } else { /* if ((flags & WATCH_ENABLE) != 0) */
444 (void)strlcpy(buf, "?WATCH={\"enable\":true,", sizeof(buf));
445 if (flags & WATCH_JSON)
446 (void)strlcat(buf, "\"json\":true,", sizeof(buf));
447 if (flags & WATCH_NMEA)
448 (void)strlcat(buf, "\"nmea\":true,", sizeof(buf));
449 if (flags & WATCH_RARE)
450 (void)strlcat(buf, "\"raw\":1,", sizeof(buf));
451 if (flags & WATCH_RAW)
452 (void)strlcat(buf, "\"raw\":2,", sizeof(buf));
453 if (flags & WATCH_SCALED)
454 (void)strlcat(buf, "\"scaled\":true,", sizeof(buf));
455 if (flags & WATCH_TIMING)
456 (void)strlcat(buf, "\"timing\":true,", sizeof(buf));
457 if (flags & WATCH_SPLIT24)
458 (void)strlcat(buf, "\"split24\":true,", sizeof(buf));
459 if (flags & WATCH_PPS)
460 (void)strlcat(buf, "\"pps\":true,", sizeof(buf));
461 if (flags & WATCH_DEVICE)
462 str_appendf(buf, sizeof(buf), "\"device\":\"%s\",", (char *)d);
463 str_rstrip_char(buf, ',');
464 (void)strlcat(buf, "};", sizeof(buf));
465 libgps_debug_trace((DEBUG_CALLS,
466 "gps_stream() enable command: %s\n", buf));
467 return gps_send(gpsdata, buf);
468 }
469 }
470
gps_sock_mainloop(struct gps_data_t * gpsdata,int timeout,void (* hook)(struct gps_data_t * gpsdata))471 int gps_sock_mainloop(struct gps_data_t *gpsdata, int timeout,
472 void (*hook)(struct gps_data_t *gpsdata))
473 /* run a socket main loop with a specified handler */
474 {
475 for (;;) {
476 if (!gps_waiting(gpsdata, timeout)) {
477 return -1;
478 } else {
479 int status = gps_read(gpsdata, NULL, 0);
480
481 if (status == -1)
482 return -1;
483 if (status > 0)
484 (*hook)(gpsdata);
485 }
486 }
487 //return 0;
488 }
489
490 #endif /* SOCKET_EXPORT_ENABLE */
491
492 /* end */
493