Coverage Report

Created: 2025-03-14 06:43

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