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