Coverage Report

Created: 2025-08-29 06:34

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