Coverage Report

Created: 2026-06-13 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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