123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712 |
- /*
- * This is the main sequence of the gpsd daemon. The IO dispatcher, main
- * select loop, and user command handling lives here.
- *
- * This file is Copyright 2010 by the GPSD project
- * SPDX-License-Identifier: BSD-2-clause
- */
- #include "gpsd_config.h" /* must be before all includes */
- #include <arpa/inet.h> /* for htons() and friends */
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <fcntl.h>
- #ifdef HAVE_GETOPT_LONG
- #include <getopt.h>
- #endif
- #include <grp.h> /* for setgroups() */
- #include <math.h>
- #include <netdb.h>
- #include <pthread.h>
- #include <pwd.h>
- #include <setjmp.h>
- #include <signal.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdint.h> /* for uint32_t, etc. */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h> /* for strlcat(), strcpy(), etc. */
- #include <syslog.h>
- #include <sys/param.h> /* for setgroups() */
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/un.h>
- #include <time.h>
- #include <unistd.h> /* for setgroups() */
- #ifndef AF_UNSPEC
- #include <sys/socket.h>
- #endif /* AF_UNSPEC */
- #ifndef INADDR_ANY
- #include <netinet/in.h>
- #endif /* INADDR_ANY */
- #include "gpsd.h"
- #include "gps_json.h" /* needs gpsd.h */
- #include "sockaddr.h"
- #include "strfuncs.h"
- #if defined(SYSTEMD_ENABLE)
- #include "sd_socket.h"
- #endif
- /*
- * The name of a tty device from which to pick up whatever the local
- * owning group for tty devices is. Used when we drop privileges.
- */
- #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
- #define PROTO_TTY "/dev/tty00" /* correct for *BSD */
- #else
- #define PROTO_TTY "/dev/ttyS0" /* correct for Linux */
- #endif
- /*
- * Timeout policy. We can't rely on clients closing connections
- * correctly, so we need timeouts to tell us when it's OK to
- * reclaim client fds. COMMAND_TIMEOUT fends off programs
- * that open connections and just sit there, not issuing a WATCH or
- * doing anything else that triggers a device assignment. Clients
- * in watcher or raw mode that don't read their data will get dropped
- * when throttled_write() fills up the outbound buffers and the
- * NOREAD_TIMEOUT expires.
- *
- * RELEASE_TIMEOUT sets the amount of time we hold a device
- * open after the last subscriber closes it; this is nonzero so a
- * client that does open/query/close will have time to come back and
- * do another single-shot query, if it wants to, before the device is
- * actually closed. The reason this matters is because some Bluetooth
- * GPSes not only shut down the GPS receiver on close to save battery
- * power, they actually shut down the Bluetooth RF stage as well and
- * only re-wake it periodically to see if an attempt to raise the
- * device is in progress. The result is that if you close the device
- * when it's powered up, a re-open can fail with EIO and needs to be
- * tried repeatedly. Better to avoid this...
- *
- * DEVICE_REAWAKE says how long to wait before repolling after a zero-length
- * read. It's there so we avoid spinning forever on an EOF condition.
- *
- * DEVICE_RECONNECT sets interval on retries when (re)connecting to
- * a device.
- */
- #define COMMAND_TIMEOUT 60*15
- #define NOREAD_TIMEOUT 60*3
- #define RELEASE_TIMEOUT 60
- #define DEVICE_REAWAKE 0.01
- #define DEVICE_RECONNECT 2
- #define QLEN 5
- /*
- * If ntpshm is enabled, we renice the process to this priority level.
- * For precise timekeeping increase priority.
- */
- #define NICEVAL -10
- #if (defined(TIMESERVICE_ENABLE) || \
- !defined(SOCKET_EXPORT_ENABLE))
- /*
- * Force nowait in two circumstances:
- *
- * (1) Socket export has been disabled. In this case we have no
- * way to know when client apps are watching the export channels,
- * so we need to be running all the time.
- *
- * (2) timeservice mode where we want the GPS always on for timing.
- */
- #define FORCE_NOWAIT
- #endif /* defined(TIMESERVICE_ENABLE) || !defined(SOCKET_EXPORT_ENABLE) */
- #ifdef SOCKET_EXPORT_ENABLE
- /* IP version used by the program */
- /* AF_UNSPEC: all
- * AF_INET: IPv4 only
- * AF_INET6: IPv6 only
- */
- static const int af_allowed = AF_UNSPEC;
- #endif /* SOCKET_EXPORT_ENABLE */
- #define AFCOUNT 2
- static struct gps_context_t context;
- static fd_set all_fds;
- static int highwater;
- static bool listen_global = false;
- static int maxfd;
- #ifdef FORCE_NOWAIT
- static bool nowait = true;
- #else /* FORCE_NOWAIT */
- static bool nowait = false;
- #endif /* FORCE_NOWAIT */
- static jmp_buf restartbuf;
- #if defined(SYSTEMD_ENABLE)
- static int sd_socket_count = 0;
- #endif
- /* work around the unfinished ipv6 implementation on hurd and OSX <10.6 */
- #ifndef IPV6_TCLASS
- # if defined(__GNU__)
- # define IPV6_TCLASS 61
- # elif defined(__APPLE__)
- # define IPV6_TCLASS 36
- # endif
- #endif
- static volatile sig_atomic_t signalled;
- static void onsig(int sig)
- {
- /* just set a variable, and deal with it in the main loop */
- signalled = (sig_atomic_t) sig;
- }
- static void typelist(void)
- /* list installed drivers and enabled features */
- {
- const struct gps_type_t **dp;
- for (dp = gpsd_drivers; *dp; dp++) {
- if ((*dp)->packet_type == COMMENT_PACKET)
- continue;
- if ((*dp)->mode_switcher != NULL)
- (void)fputs("n\t", stdout);
- else
- (void)fputc('\t', stdout);
- if ((*dp)->speed_switcher != NULL)
- (void)fputs("b\t", stdout);
- else
- (void)fputc('\t', stdout);
- if ((*dp)->rate_switcher != NULL)
- (void)fputs("c\t", stdout);
- else
- (void)fputc('\t', stdout);
- if ((*dp)->packet_type > NMEA_PACKET)
- (void)fputs("*\t", stdout);
- else
- (void)fputc('\t', stdout);
- (void)puts((*dp)->type_name);
- }
- (void)printf("# n: mode switch, b: speed switch, "
- "c: rate switch, *: non-NMEA packet type.\n");
- #if defined(SOCKET_EXPORT_ENABLE)
- (void)printf("# Socket export enabled.\n");
- #endif
- #if defined(SHM_EXPORT_ENABLE)
- (void)printf("# Shared memory export enabled.\n");
- #endif
- #if defined(DBUS_EXPORT_ENABLE)
- (void)printf("# DBUS export enabled\n");
- #endif
- #if defined(CONTROL_SOCKET_ENABLE)
- (void)printf("# Control socket for hotplug notifications enabled\n");
- #endif
- #if defined(MAGIC_HAT_ENABLE)
- (void)printf("# Magic Hat enabled\n");
- #endif
- #if defined(SYSTEMD_ENABLE)
- (void)printf("# systemd socket activation enabled\n");
- #endif
- exit(EXIT_SUCCESS);
- }
- static void usage(void)
- {
- (void)printf("usage: gpsd [OPTIONS] device...\n\n\
- Options include: \n\
- -b, --readonly = bluetooth-safe: open data sources read-only\n\
- -D, --debug integer = set debug level, default 0 \n\
- -F, --sockfile sockfile = specify control socket location, default none\n\
- -f, --framing FRAMING = fix device framing to FRAMING (8N1, 8O1, etc.)\n\
- -G, --listenany = make gpsd listen on INADDR_ANY\n\
- -h, --help = help message\n\
- -l, --drivers = list compiled in drivers, and exit.\n\
- -n, --nowait = don't wait for client connects to poll GPS\n"
- #ifdef FORCE_NOWAIT
- " forced on in this binary\n"
- #endif /* FORCE_NOWAIT */
- " -N, --foreground = don't go into background\n\
- -P, --pidfile pidfile = set file to record process ID\n\
- -p, --passive = do not reconfigure the receiver automatically\n\
- -r, --badtime = use GPS time even if no fix\n\
- -S, --port PORT = set port for daemon, default %s\n\
- -s, --speed SPEED = fix device speed to SPEED, default none\n\
- -V, --version = emit version and exit.\n"
- #ifdef NETFEED_ENABLE
- "\nA device may be a local serial device for GNSS input, plus an optional\n\
- PPS device, or a URL in one of the following forms:\n\
- tcp://host[:port]\n\
- udp://host[:port]\n\
- {dgpsip|ntrip}://[user:passwd@]host[:port][/stream]\n\
- gpsd://host[:port][:/device]\n\
- in which case it specifies an input source for device, DGPS or ntrip data.\n"
- #endif /* NETFEED_ENABLE */
- "\n\
- The following driver types are compiled into this gpsd instance:\n",
- DEFAULT_GPSD_PORT);
- typelist();
- if (8 > sizeof(time_t)) {
- (void)printf("\nWARNING: This system has a 32-bit time_t.\n"
- "WARNING: This gpsd will fail at 2038-01-19T03:14:07Z.\n");
- }
- }
- #ifdef CONTROL_SOCKET_ENABLE
- static socket_t filesock(char *filename)
- {
- struct sockaddr_un addr;
- socket_t sock;
- if (BAD_SOCKET(sock = socket(AF_UNIX, SOCK_STREAM, 0))) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Can't create device-control socket\n");
- return -1;
- }
- (void)strlcpy(addr.sun_path, filename, sizeof(addr.sun_path));
- addr.sun_family = (sa_family_t)AF_UNIX;
- if (bind(sock, (struct sockaddr *)&addr, (socklen_t)sizeof(addr)) < 0) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't bind to local socket %s\n", filename);
- (void)close(sock);
- return -1;
- }
- if (listen(sock, QLEN) == -1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't listen on local socket %s\n", filename);
- (void)close(sock);
- return -1;
- }
- /* coverity[leaked_handle] This is an intentional allocation */
- return sock;
- }
- #endif /* CONTROL_SOCKET_ENABLE */
- #define sub_index(s) (int)((s) - subscribers)
- #define allocated_device(devp) ((devp)->gpsdata.dev.path[0] != '\0')
- #define free_device(devp) (devp)->gpsdata.dev.path[0] = '\0'
- #define initialized_device(devp) ((devp)->context != NULL)
- /*
- * This array fills from the bottom, so as an extreme case you can
- * reduce MAX_DEVICES to 1 in the build recipe.
- */
- static struct gps_device_t devices[MAX_DEVICES];
- static void adjust_max_fd(int fd, bool on)
- /* track the largest fd currently in use */
- {
- if (on) {
- if (fd > maxfd)
- maxfd = fd;
- } else if (fd == maxfd) {
- int tfd;
- for (maxfd = tfd = 0; tfd < (int)FD_SETSIZE; tfd++)
- if (FD_ISSET(tfd, &all_fds))
- maxfd = tfd;
- }
- }
- #ifdef SOCKET_EXPORT_ENABLE
- #ifndef IPTOS_LOWDELAY
- #define IPTOS_LOWDELAY 0x10
- #endif
- /* bind a passive command socket for the daemon */
- static socket_t passivesock_af(int af, char *service, char *tcp_or_udp,
- int qlen)
- {
- volatile socket_t s;
- /*
- * af = address family,
- * service = IANA protocol name or number.
- * tcp_or_udp = TCP or UDP
- * qlen = maximum wait-queue length for connections
- */
- struct servent *pse;
- struct protoent *ppe;
- sockaddr_t sat;
- size_t sin_len = 0;
- int type, proto, one = 1;
- in_port_t port;
- char *af_str = "";
- const int dscp = IPTOS_LOWDELAY; /* Prioritize packet */
- INVALIDATE_SOCKET(s);
- if ((pse = getservbyname(service, tcp_or_udp)))
- port = ntohs((in_port_t) pse->s_port);
- // cppcheck-suppress unreadVariable
- else if ((port = (in_port_t) atoi(service)) == 0) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't get \"%s\" service entry.\n", service);
- return -1;
- }
- ppe = getprotobyname(tcp_or_udp);
- if (strcmp(tcp_or_udp, "udp") == 0) {
- type = SOCK_DGRAM;
- proto = (ppe) ? ppe->p_proto : IPPROTO_UDP;
- } else {
- type = SOCK_STREAM;
- proto = (ppe) ? ppe->p_proto : IPPROTO_TCP;
- }
- switch (af) {
- case AF_INET:
- sin_len = sizeof(sat.sa_in);
- memset((char *)&sat.sa_in, 0, sin_len);
- sat.sa_in.sin_family = (sa_family_t) AF_INET;
- if (!listen_global)
- sat.sa_in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- else
- sat.sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
- sat.sa_in.sin_port = htons(port);
- af_str = "IPv4";
- /* see PF_INET6 case below */
- s = socket(PF_INET, type, proto);
- if (s > -1 ) {
- /* Set packet priority */
- if (setsockopt(s, IPPROTO_IP, IP_TOS, &dscp,
- (socklen_t)sizeof(dscp)) == -1)
- GPSD_LOG(LOG_WARN, &context.errout,
- "Warning: SETSOCKOPT TOS failed\n");
- }
- break;
- case AF_INET6:
- sin_len = sizeof(sat.sa_in6);
- memset((char *)&sat.sa_in6, 0, sin_len);
- sat.sa_in6.sin6_family = (sa_family_t) AF_INET6;
- if (!listen_global)
- sat.sa_in6.sin6_addr = in6addr_loopback;
- else
- sat.sa_in6.sin6_addr = in6addr_any;
- sat.sa_in6.sin6_port = htons(port);
- /*
- * Traditionally BSD uses "communication domains", named by
- * constants starting with PF_ as the first argument for
- * select. In practice PF_INET has the same value as AF_INET
- * (on BSD and Linux, and probably everywhere else). POSIX
- * leaves much of this unspecified, but requires that AF_INET
- * be recognized. We follow tradition here.
- */
- af_str = "IPv6";
- s = socket(PF_INET6, type, proto);
- /*
- * On some network stacks, including Linux's, an IPv6 socket
- * defaults to listening on IPv4 as well. Unless we disable
- * this, trying to listen on in6addr_any will fail with the
- * address-in-use error condition.
- */
- if (s > -1) {
- int on = 1;
- if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on,
- (socklen_t)sizeof(on)) == -1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Error: SETSOCKOPT IPV6_V6ONLY\n");
- (void)close(s);
- return -1;
- }
- #ifdef IPV6_TCLASS
- /* Set packet priority */
- if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &dscp,
- (socklen_t)sizeof(dscp)) == -1)
- GPSD_LOG(LOG_WARN, &context.errout,
- "Warning: SETSOCKOPT TOS failed\n");
- #endif /* IPV6_TCLASS */
- }
- break;
- default:
- GPSD_LOG(LOG_ERROR, &context.errout,
- "unhandled address family %d\n", af);
- return -1;
- }
- GPSD_LOG(LOG_IO, &context.errout,
- "opening %s socket\n", af_str);
- if (BAD_SOCKET(s)) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't create %s socket\n", af_str);
- return -1;
- }
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one,
- (socklen_t)sizeof(one)) == -1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Error: SETSOCKOPT SO_REUSEADDR\n");
- (void)close(s);
- return -1;
- }
- if (bind(s, &sat.sa, (socklen_t)sin_len) < 0) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't bind to %s port %s, %s\n", af_str,
- service, strerror(errno));
- if (errno == EADDRINUSE) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "maybe gpsd is already running!\n");
- }
- (void)close(s);
- return -1;
- }
- if (type == SOCK_STREAM && listen(s, qlen) == -1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't listen on port %s\n", service);
- (void)close(s);
- return -1;
- }
- GPSD_LOG(LOG_SPIN, &context.errout, "passivesock_af() -> %d\n", s);
- return s;
- }
- /* *INDENT-OFF* */
- static int passivesocks(char *service, char *tcp_or_udp,
- int qlen, socket_t socks[])
- {
- int numsocks = AFCOUNT;
- int i;
- for (i = 0; i < AFCOUNT; i++)
- INVALIDATE_SOCKET(socks[i]);
- #if defined(SYSTEMD_ENABLE)
- if (sd_socket_count > 0) {
- for (i = 0; i < AFCOUNT && i < sd_socket_count - 1; i++) {
- socks[i] = SD_SOCKET_FDS_START + i + 1;
- }
- return sd_socket_count - 1;
- }
- #endif
- if (AF_UNSPEC == af_allowed || (AF_INET == af_allowed))
- socks[0] = passivesock_af(AF_INET, service, tcp_or_udp, qlen);
- if (AF_UNSPEC == af_allowed || (AF_INET6 == af_allowed))
- socks[1] = passivesock_af(AF_INET6, service, tcp_or_udp, qlen);
- for (i = 0; i < AFCOUNT; i++)
- if (socks[i] < 0)
- numsocks--;
- /* Return the number of successfully opened sockets
- * The failed ones are identified by negative values */
- return numsocks;
- }
- /* *INDENT-ON* */
- struct subscriber_t
- {
- int fd; /* client file descriptor. -1 if unused */
- time_t active; /* when subscriber last polled for data */
- struct gps_policy_t policy; /* configurable bits */
- pthread_mutex_t mutex; /* serialize access to fd */
- };
- #define subscribed(sub, devp) (sub->policy.watcher && (sub->policy.devpath[0]=='\0' || strcmp(sub->policy.devpath, devp->gpsdata.dev.path)==0))
- /* indexed by client file descriptor */
- static struct subscriber_t subscribers[MAX_CLIENTS];
- static void lock_subscriber(struct subscriber_t *sub)
- {
- (void)pthread_mutex_lock(&sub->mutex);
- }
- static void unlock_subscriber(struct subscriber_t *sub)
- {
- (void)pthread_mutex_unlock(&sub->mutex);
- }
- static struct subscriber_t *allocate_client(void)
- /* return the address of a subscriber structure allocated for a new session */
- {
- int si;
- #if UNALLOCATED_FD == 0
- #error client allocation code will fail horribly
- #endif
- for (si = 0; si < NITEMS(subscribers); si++) {
- if (subscribers[si].fd == UNALLOCATED_FD) {
- subscribers[si].fd = 0; /* mark subscriber as allocated */
- return &subscribers[si];
- }
- }
- return NULL;
- }
- static void detach_client(struct subscriber_t *sub)
- /* detach a client and terminate the session */
- {
- char *c_ip;
- lock_subscriber(sub);
- if (sub->fd == UNALLOCATED_FD) {
- unlock_subscriber(sub);
- return;
- }
- c_ip = netlib_sock2ip(sub->fd);
- (void)shutdown(sub->fd, SHUT_RDWR);
- GPSD_LOG(LOG_SPIN, &context.errout,
- "close(%d) in detach_client()\n",
- sub->fd);
- (void)close(sub->fd);
- GPSD_LOG(LOG_INF, &context.errout,
- "detaching %s (sub %d, fd %d) in detach_client\n",
- c_ip, sub_index(sub), sub->fd);
- FD_CLR(sub->fd, &all_fds);
- adjust_max_fd(sub->fd, false);
- sub->active = 0;
- sub->policy.watcher = false;
- sub->policy.json = false;
- sub->policy.nmea = false;
- sub->policy.raw = 0;
- sub->policy.scaled = false;
- sub->policy.timing = false;
- sub->policy.split24 = false;
- sub->policy.devpath[0] = '\0';
- sub->fd = UNALLOCATED_FD;
- unlock_subscriber(sub);
- }
- /* write to client -- throttle if it's gone or we're close to buffer overrun */
- static ssize_t throttled_write(struct subscriber_t *sub, char *buf,
- size_t len)
- {
- ssize_t status;
- if (context.errout.debug >= LOG_CLIENT) {
- if (isprint((unsigned char) buf[0]))
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "=> client(%d): %s\n", sub_index(sub), buf);
- else {
- char *cp, buf2[MAX_PACKET_LENGTH * 3];
- buf2[0] = '\0';
- for (cp = buf; cp < buf + len; cp++)
- str_appendf(buf2, sizeof(buf2),
- "%02x", (unsigned int)(*cp & 0xff));
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "=> client(%d): =%s\n", sub_index(sub), buf2);
- }
- }
- gpsd_acquire_reporting_lock();
- status = send(sub->fd, buf, len, 0);
- gpsd_release_reporting_lock();
- if (status == (ssize_t) len)
- return status;
- else if (status > -1) {
- GPSD_LOG(LOG_INF, &context.errout,
- "short write disconnecting client(%d)\n",
- sub_index(sub));
- detach_client(sub);
- return 0;
- } else if (errno == EAGAIN || errno == EINTR)
- return 0; /* no data written, and errno says to retry */
- else if (errno == EBADF)
- GPSD_LOG(LOG_WARN, &context.errout,
- "client(%d) has vanished.\n", sub_index(sub));
- else if (errno == EWOULDBLOCK
- && time(NULL) - sub->active > NOREAD_TIMEOUT)
- GPSD_LOG(LOG_INF, &context.errout,
- "client(%d) timed out.\n", sub_index(sub));
- else
- GPSD_LOG(LOG_INF, &context.errout,
- "client(%d) write: %s\n",
- sub_index(sub), strerror(errno));
- detach_client(sub);
- return status;
- }
- static void notify_watchers(struct gps_device_t *device,
- bool onjson, bool onpps,
- const char *sentence, ...)
- /* notify all JSON-watching clients of a given device about an event */
- {
- va_list ap;
- char buf[BUFSIZ];
- struct subscriber_t *sub;
- va_start(ap, sentence);
- (void)vsnprintf(buf, sizeof(buf), sentence, ap);
- va_end(ap);
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++)
- if (sub->active != 0 && subscribed(sub, device)) {
- if ((onjson && sub->policy.json) || (onpps && sub->policy.pps))
- (void)throttled_write(sub, buf, strlen(buf));
- }
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- /* deactivate device, but leave it in the pool (do not free it) */
- static void deactivate_device(struct gps_device_t *device)
- {
- #ifdef SOCKET_EXPORT_ENABLE
- notify_watchers(device, true, false,
- "{\"class\":\"DEVICE\",\"path\":\"%s\","
- "\"activated\":0}\r\n",
- device->gpsdata.dev.path);
- #endif /* SOCKET_EXPORT_ENABLE */
- if (!BAD_SOCKET(device->gpsdata.gps_fd)) {
- FD_CLR(device->gpsdata.gps_fd, &all_fds);
- adjust_max_fd(device->gpsdata.gps_fd, false);
- ntpshm_link_deactivate(device);
- gpsd_deactivate(device);
- }
- }
- #if defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE)
- /* find the device block for an existing device name */
- static struct gps_device_t *find_device(const char *device_name)
- {
- struct gps_device_t *devp;
- if (NULL == device_name) {
- return NULL;
- }
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (allocated_device(devp) &&
- 0 == strcmp(devp->gpsdata.dev.path, device_name)) {
- return devp;
- }
- }
- return NULL;
- }
- #endif /* defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE) */
- /* open the input device
- * return: false on failure
- * true on success
- */
- static bool open_device( struct gps_device_t *device)
- {
- int activated = -1;
- if (NULL == device) {
- return false;
- }
- activated = gpsd_activate(device, O_OPTIMIZE);
- if ((0 > activated) && (PLACEHOLDING_FD != activated)) {
- /* failed to open device, and it is not a /dev/ppsX */
- return false;
- }
- /*
- * Now is the right time to grab the shared memory segment(s)
- * to communicate the navigation message derived and (possibly)
- * 1PPS derived time data to ntpd/chrony.
- */
- ntpshm_link_activate(device);
- GPSD_LOG(LOG_INF, &context.errout,
- "PPS:%s ntpshm_link_activate: %d\n",
- device->gpsdata.dev.path,
- device->shm_clock != NULL);
- GPSD_LOG(LOG_INF, &context.errout,
- "device %s activated\n", device->gpsdata.dev.path);
- if (PLACEHOLDING_FD == activated) {
- /* it is a /dev/ppsX, no need to wait on it */
- return true;
- }
- FD_SET(device->gpsdata.gps_fd, &all_fds);
- adjust_max_fd(device->gpsdata.gps_fd, true);
- ++highwater;
- return true;
- }
- /* add a device to the pool; open it right away if in nowait mode
- * return: false on failure
- * true on success
- */
- bool gpsd_add_device(const char *device_name, bool flag_nowait)
- {
- struct gps_device_t *devp;
- bool ret = false;
- #ifdef SOCKET_EXPORT_ENABLE
- char tbuf[JSON_DATE_MAX+1];
- #endif /* SOCKET_EXPORT_ENABLE */
- /* we can't handle paths longer than GPS_PATH_MAX, so don't try */
- if (strlen(device_name) >= GPS_PATH_MAX) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "ignoring device %s: path length exceeds maximum %d\n",
- device_name, GPS_PATH_MAX);
- return false;
- }
- /* stash devicename away for probing when the first client connects */
- for (devp = devices; devp < devices + MAX_DEVICES; devp++)
- if (!allocated_device(devp)) {
- gpsd_init(devp, &context, device_name);
- ntpshm_session_init(devp);
- GPSD_LOG(LOG_INF, &context.errout,
- "stashing device %s at slot %d\n",
- device_name, (int)(devp - devices));
- if (!flag_nowait) {
- devp->gpsdata.gps_fd = UNALLOCATED_FD;
- ret = true;
- } else {
- ret = open_device(devp);
- }
- #ifdef SOCKET_EXPORT_ENABLE
- notify_watchers(devp, true, false,
- "{\"class\":\"DEVICE\",\"path\":\"%s\","
- "\"activated\":\"%s\"}\r\n",
- devp->gpsdata.dev.path,
- now_to_iso8601(tbuf, sizeof(tbuf)));
- #endif /* SOCKET_EXPORT_ENABLE */
- break;
- }
- return ret;
- }
- #if defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE)
- /* convert hex to binary, write it, unchanged, to GPS */
- static int write_gps(char *device, char *hex) {
- struct gps_device_t *devp;
- size_t len;
- int st;
- if ((devp = find_device(device)) == NULL) {
- GPSD_LOG(LOG_INF, &context.errout, "GPS <=: %s not active\n", device);
- return 1;
- }
- if (devp->context->readonly || (devp->sourcetype <= source_blockdev)) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "GPS <=: attempted to write to a read-only device\n");
- return 1;
- }
- len = strlen(hex);
- /* NOTE: this destroys the original buffer contents */
- st = gpsd_hexpack(hex, hex, len);
- if (st <= 0) {
- GPSD_LOG(LOG_INF, &context.errout,
- "GPS <=: invalid hex string (error %d).\n", st);
- return 1;
- }
- GPSD_LOG(LOG_INF, &context.errout,
- "GPS <=: writing %d bytes fromhex(%s) to %s\n",
- st, hex, device);
- if (write(devp->gpsdata.gps_fd, hex, (size_t) st) <= 0) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "GPS <=: write to device failed\n");
- return 1;
- }
- return 0;
- }
- #endif /* defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE) */
- #ifdef CONTROL_SOCKET_ENABLE
- static char *snarfline(char *p, char **out)
- /* copy the rest of the command line, before CR-LF */
- {
- char *q;
- static char stash[BUFSIZ];
- for (q = p;
- isprint((unsigned char) *p) &&
- !isspace((unsigned char) *p) &&
- (p - q < (ssize_t)(sizeof(stash) - 1));
- p++)
- continue;
- (void)memcpy(stash, q, (size_t) (p - q));
- stash[p - q] = '\0';
- *out = stash;
- return p;
- }
- /* handle privileged commands coming through the control socket */
- static void handle_control(int sfd, char *buf)
- {
- char *stash;
- struct gps_device_t *devp;
- /*
- * The only other place in the code that knows about the format
- * of the + and - commands is the gpsd_control() function in
- * gpsdctl.c. Be careful about keeping them in sync, or
- * hotplugging will have mysterious failures.
- */
- if (buf[0] == '-') {
- /* remove device named after - */
- (void)snarfline(buf + 1, &stash);
- GPSD_LOG(LOG_INF, &context.errout,
- "<= control(%d): removing %s\n", sfd, stash);
- if ((devp = find_device(stash))) {
- deactivate_device(devp);
- free_device(devp);
- ignore_return(write(sfd, "OK\n", 3));
- } else
- ignore_return(write(sfd, "ERROR\n", 6));
- } else if (buf[0] == '+') {
- /* add device named after + */
- (void)snarfline(buf + 1, &stash);
- if (find_device(stash)) {
- GPSD_LOG(LOG_INF, &context.errout,
- "<= control(%d): %s already active \n", sfd,
- stash);
- ignore_return(write(sfd, "ERROR\n", 6));
- } else {
- GPSD_LOG(LOG_INF, &context.errout,
- "<= control(%d): adding %s\n", sfd, stash);
- if (gpsd_add_device(stash, nowait))
- ignore_return(write(sfd, "OK\n", 3));
- else {
- ignore_return(write(sfd, "ERROR\n", 6));
- GPSD_LOG(LOG_INF, &context.errout,
- "control(%d): adding %s failed, too many "
- "devices active\n",
- sfd, stash);
- }
- }
- } else if (buf[0] == '!') {
- /* split line after ! into device=string, send string to device */
- char *eq;
- (void)snarfline(buf + 1, &stash);
- eq = strchr(stash, '=');
- if (eq == NULL) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "<= control(%d): ill-formed command \n",
- sfd);
- ignore_return(write(sfd, "ERROR\n", 6));
- } else {
- *eq++ = '\0';
- if ((devp = find_device(stash))) {
- if (devp->context->readonly ||
- (devp->sourcetype <= source_blockdev)) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "<= control(%d): attempted to write to a "
- "read-only device\n",
- sfd);
- ignore_return(write(sfd, "ERROR\n", 6));
- } else {
- GPSD_LOG(LOG_INF, &context.errout,
- "<= control(%d): writing to %s \n", sfd,
- stash);
- if (write(devp->gpsdata.gps_fd, eq, strlen(eq)) <= 0) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "<= control(%d): write to device failed\n",
- sfd);
- ignore_return(write(sfd, "ERROR\n", 6));
- } else {
- ignore_return(write(sfd, "OK\n", 3));
- }
- }
- } else {
- GPSD_LOG(LOG_INF, &context.errout,
- "<= control(%d): %s not active \n", sfd,
- stash);
- ignore_return(write(sfd, "ERROR\n", 6));
- }
- }
- } else if (buf[0] == '&') {
- /* split line after & into dev=hexdata, send unpacked hexdata to dev */
- char *eq;
- (void)snarfline(buf + 1, &stash);
- eq = strchr(stash, '=');
- if (eq == NULL) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "<= control(%d): ill-formed command\n",
- sfd);
- ignore_return(write(sfd, "ERROR\n", 6));
- } else {
- *eq++ = '\0';
- if (0 == write_gps(stash, eq)) {
- ignore_return(write(sfd, "OK\n", 3));
- } else {
- ignore_return(write(sfd, "ERROR\n", 6));
- }
- }
- } else if (strstr(buf, "?devices")==buf) {
- /* write back devices list followed by OK */
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- char *path = devp->gpsdata.dev.path;
- ignore_return(write(sfd, path, strlen(path)));
- ignore_return(write(sfd, "\n", 1));
- }
- ignore_return(write(sfd, "OK\n", 3));
- } else {
- /* unknown command */
- ignore_return(write(sfd, "ERROR\n", 6));
- }
- }
- #endif /* CONTROL_SOCKET_ENABLE */
- #ifdef SOCKET_EXPORT_ENABLE
- /* awaken a device and notify all watchers*/
- static bool awaken(struct gps_device_t *device)
- {
- /* open that device */
- if ((!initialized_device(device) &&
- !open_device(device))) {
- GPSD_LOG(LOG_WARN, &context.errout, "%s: open failed\n",
- device->gpsdata.dev.path);
- free_device(device);
- return false;
- }
- if (!BAD_SOCKET(device->gpsdata.gps_fd)) {
- GPSD_LOG(LOG_PROG, &context.errout,
- "device %d (fd=%d, path %s) already active.\n",
- (int)(device - devices),
- device->gpsdata.gps_fd, device->gpsdata.dev.path);
- return true;
- }
- if (0 > gpsd_activate(device, O_OPTIMIZE)) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "%s: device activation failed, freeing device.\n",
- device->gpsdata.dev.path);
- /* FIXME: works around a crash bug, but prevents retries */
- free_device(device);
- return false;
- }
- GPSD_LOG(LOG_RAW, &context.errout,
- "flagging descriptor %d in assign_channel()\n",
- device->gpsdata.gps_fd);
- FD_SET(device->gpsdata.gps_fd, &all_fds);
- adjust_max_fd(device->gpsdata.gps_fd, true);
- return true;
- }
- #if __UNUSED_RECONFIGURE__
- /* is this channel privileged to change a device's behavior? */
- static bool privileged_user(struct gps_device_t *device)
- {
- /* grant user privilege if he's the only one listening to the device */
- struct subscriber_t *sub;
- int subcount = 0;
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++) {
- if (subscribed(sub, device))
- subcount++;
- }
- /*
- * Yes, zero subscribers is possible. For example, gpsctl talking
- * to the daemon connects but doesn't necessarily issue a ?WATCH
- * before shipping a request, which means it isn't marked as a
- * subscriber.
- */
- return subcount <= 1;
- }
- #endif /* __UNUSED_RECONFIGURE__ */
- /* set serial parameters for a device from a speed and modestring */
- static void set_serial(struct gps_device_t *device,
- speed_t speed, char *modestring)
- {
- unsigned int stopbits = device->gpsdata.dev.stopbits;
- char parity = device->gpsdata.dev.parity;
- int wordsize = 8;
- struct timespec delay;
- #ifndef __clang_analyzer__
- while (isspace((unsigned char) *modestring))
- modestring++;
- if (*modestring && (NULL != strchr("78", *modestring))) {
- wordsize = (int)(*modestring++ - '0');
- if (*modestring && (NULL != strchr("NOE", *modestring))) {
- parity = *modestring++;
- while (isspace((unsigned char) *modestring))
- modestring++;
- if (*modestring && (NULL != strchr("12", *modestring))) {
- stopbits = (unsigned int)(*modestring - '0');
- }
- }
- }
- #endif /* __clang_analyzer__ */
- GPSD_LOG(LOG_PROG, &context.errout,
- "set_serial(%s,%u,%s) %c%d\n",
- device->gpsdata.dev.path,
- (unsigned int)speed, modestring, parity, stopbits);
- /* no support for other word sizes yet */
- /* *INDENT-OFF* */
- if (wordsize == (int)(9 - stopbits)
- && device->device_type->speed_switcher != NULL) {
- if (device->device_type->speed_switcher(device, speed,
- parity, (int)stopbits)) {
- /*
- * Deep black magic is required here. We have to
- * allow the control string time to register at the
- * GPS before we do the baud rate switch, which
- * effectively trashes the UART's buffer.
- *
- * This definitely fails below 40 milliseconds on a
- * BU-303b. 50ms is also verified by Chris Kuethe on
- * Pharos iGPS360 + GSW 2.3.1ES + prolific
- * Rayming TN-200 + GSW 2.3.1 + ftdi
- * Rayming TN-200 + GSW 2.3.2 + ftdi
- * so it looks pretty solid.
- *
- * The minimum delay time is probably constant
- * across any given type of UART.
- */
- (void)tcdrain(device->gpsdata.gps_fd);
- /* wait 50,000 uSec */
- delay.tv_sec = 0;
- delay.tv_nsec = 50000000L;
- nanosleep(&delay, NULL);
- gpsd_set_speed(device, speed, parity, stopbits);
- }
- }
- /* *INDENT-ON* */
- }
- #ifdef SOCKET_EXPORT_ENABLE
- static void json_devicelist_dump(char *reply, size_t replylen)
- {
- struct gps_device_t *devp;
- (void)strlcpy(reply, "{\"class\":\"DEVICES\",\"devices\":[", replylen);
- for (devp = devices; devp < devices + MAX_DEVICES; devp++)
- if (allocated_device(devp)
- && strlen(reply) + strlen(devp->gpsdata.dev.path) + 3 <
- replylen - 1) {
- char *cp;
- json_device_dump(devp,
- reply + strlen(reply), replylen - strlen(reply));
- cp = reply + strlen(reply);
- *--cp = '\0';
- *--cp = '\0';
- (void)strlcat(reply, ",", replylen);
- }
- str_rstrip_char(reply, ',');
- (void)strlcat(reply, "]}\r\n", replylen);
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- static void rstrip(char *str)
- /* strip trailing \r\n\t\SP from a string */
- {
- char *strend;
- strend = str + strlen(str) - 1;
- while (isspace((unsigned char) *strend)) {
- *strend = '\0';
- --strend;
- }
- }
- static void handle_request(struct subscriber_t *sub,
- const char *buf, const char **after,
- char *reply, size_t replylen)
- {
- struct gps_device_t *devp;
- struct gps_policy_t policy_copy;
- const char *end = NULL;
- char watch_buf[GPS_JSON_RESPONSE_MAX]; // buffer for re-written policy
- if (str_starts_with(buf, "?DEVICES;")) {
- buf += 9;
- json_devicelist_dump(reply, replylen);
- } else if (str_starts_with(buf, "?WATCH")
- && (buf[6] == ';' || buf[6] == '=')) {
- const char *start = buf;
- buf += 6;
- if (*buf == ';') {
- ++buf;
- } else {
- char *host, *port, *device; // for parse_uri_dest()
- int status = json_watch_read(buf + 1, &sub->policy, &end);
- // why force policy.timing to false?
- sub->policy.timing = false;
- if (end == NULL)
- buf += strlen(buf);
- else {
- if (*end == ';')
- ++end;
- buf = end;
- }
- if (status != 0) {
- // failed to parse ?WATCH.
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\",\"message\":"
- "\"Invalid WATCH: %s\"}\r\n",
- json_error_string(status));
- GPSD_LOG(LOG_ERROR, &context.errout, "response: %s\n", reply);
- } else if (sub->policy.watcher) {
- // enable:true
- if (sub->policy.devpath[0] == '\0') {
- // awaken all devices
- for (devp = devices; devp < devices + MAX_DEVICES; devp++)
- if (allocated_device(devp)) {
- (void)awaken(devp);
- if (devp->sourcetype == source_gpsd) {
- // wake all, so no devpath/remote issues
- (void)gpsd_write(devp, start,
- (size_t)(end-start));
- }
- }
- } else {
- // awaken specific device
- #if __UNUSED__
- char outbuf[GPS_JSON_RESPONSE_MAX];
- json_watch_dump(&sub->policy, outbuf, sizeof(outbuf));
- GPSD_LOG(0, &context.errout, "policy: %s\n", outbuf);
- #endif
- devp = find_device(sub->policy.devpath);
- if (devp == NULL) {
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\",\"message\":"
- "\"No such device as %s\"}\r\n",
- sub->policy.devpath);
- GPSD_LOG(LOG_ERROR, &context.errout,
- "response: %s\n", reply);
- goto bailout;
- } else if (awaken(devp)) {
- if (devp->sourcetype == source_gpsd) {
- /* FIXME: the device into this daemon is
- * not the device to pass to the remote daemon.
- * local device = gpsd://host::/device
- * remote device = /device
- */
- policy_copy = sub->policy; // struct copy
- // parse uri, skip the gpsd://
- if (0 == parse_uri_dest(policy_copy.devpath + 7,
- &host, &port, &device) &&
- NULL != device) {
- // remove gpsd://host:port part
- (void)strlcpy(policy_copy.devpath,
- device,
- sizeof(policy_copy.devpath));
- } else {
- // no remote device part
- policy_copy.devpath[0] = '\0';
- }
- // SNARD!
- (void)json_policy_to_watch(&policy_copy,
- watch_buf,
- sizeof(watch_buf));
- (void)gpsd_write(devp, watch_buf, strlen(watch_buf));
- }
- } else {
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\","
- "\"message\":\"Can't assign %s\"}\r\n",
- sub->policy.devpath);
- GPSD_LOG(LOG_ERROR, &context.errout,
- "response: %s\n", reply);
- goto bailout;
- }
- }
- }
- // else enable:false ???
- }
- // return a device list and the user's policy
- json_devicelist_dump(reply + strlen(reply), replylen - strlen(reply));
- json_watch_dump(&sub->policy,
- reply + strlen(reply), replylen - strlen(reply));
- } else if (str_starts_with(buf, "?DEVICE") &&
- (buf[7] == ';' || buf[7] == '=')) {
- struct devconfig_t devconf;
- buf += 7;
- devconf.path[0] = '\0'; /* initially, no device selection */
- if (*buf == ';') {
- ++buf;
- } else {
- struct gps_device_t *device;
- /* first, select a device to operate on */
- int status = json_device_read(buf + 1, &devconf, &end);
- if (end == NULL)
- buf += strlen(buf);
- else {
- if (*end == ';')
- ++end;
- buf = end;
- }
- device = NULL;
- if (status != 0) {
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\","
- "\"message\":\"Invalid DEVICE: \"%s\"}\r\n",
- json_error_string(status));
- GPSD_LOG(LOG_ERROR, &context.errout, "response: %s\n", reply);
- goto bailout;
- } else {
- if (devconf.path[0] != '\0') {
- /* user specified a path, try to assign it */
- device = find_device(devconf.path);
- /* do not optimize away, we need 'device' later on! */
- if (device == NULL || !awaken(device)) {
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\","
- "\"message\":\"Can't open %s.\"}\r\n",
- devconf.path);
- GPSD_LOG(LOG_ERROR, &context.errout,
- "response: %s\n", reply);
- goto bailout;
- }
- } else {
- /* no path specified */
- int devcount = 0;
- for (devp = devices; devp < devices + MAX_DEVICES; devp++)
- if (allocated_device(devp)) {
- device = devp;
- devcount++;
- }
- if (devcount == 0) {
- (void)strlcat(reply,
- "{\"class\":\"ERROR\",\"message\":"
- "\"Can't perform DEVICE configuration, "
- "no devices attached.\"}\r\n",
- replylen);
- GPSD_LOG(LOG_ERROR, &context.errout,
- "response: %s\n", reply);
- goto bailout;
- } else if (devcount > 1) {
- str_appendf(reply, replylen,
- "{\"class\":\"ERROR\",\"message\":"
- "\"No path specified in DEVICE, but "
- "multiple devices are attached.\"}\r\n");
- GPSD_LOG(LOG_ERROR, &context.errout,
- "response: %s\n", reply);
- goto bailout;
- }
- /* we should have exactly one device now */
- }
- if (device->device_type == NULL)
- str_appendf(reply, replylen,
- "{\"class\":\"ERROR\","
- "\"message\":\"Type of %s is unknown.\""
- "}\r\n",
- device->gpsdata.dev.path);
- else {
- timespec_t delta1, delta2;
- const struct gps_type_t *dt = device->device_type;
- bool no_serial_change =
- (devconf.baudrate == DEVDEFAULT_BPS)
- && (devconf.parity == DEVDEFAULT_PARITY)
- && (devconf.stopbits == DEVDEFAULT_STOPBITS);
- /* interpret defaults */
- if (devconf.baudrate == DEVDEFAULT_BPS)
- devconf.baudrate =
- (unsigned int) gpsd_get_speed(device);
- if (devconf.parity == DEVDEFAULT_PARITY)
- devconf.stopbits = device->gpsdata.dev.stopbits;
- if (devconf.stopbits == DEVDEFAULT_STOPBITS)
- devconf.stopbits = device->gpsdata.dev.stopbits;
- if (0 < devconf.cycle.tv_sec ||
- 0 < devconf.cycle.tv_nsec) {
- devconf.cycle = device->gpsdata.dev.cycle;
- }
- /* now that channel is selected, apply changes */
- if (devconf.driver_mode != device->gpsdata.dev.driver_mode
- && devconf.driver_mode != DEVDEFAULT_NATIVE
- && dt->mode_switcher != NULL)
- dt->mode_switcher(device, devconf.driver_mode);
- if (!no_serial_change) {
- char serialmode[3];
- serialmode[0] = devconf.parity;
- serialmode[1] = '0' + devconf.stopbits;
- serialmode[2] = '\0';
- set_serial(device,
- (speed_t) devconf.baudrate, serialmode);
- }
- TS_SUB(&delta1, &devconf.cycle, &device->gpsdata.dev.cycle);
- if (TS_NZ(&delta1)) {
- /* different cycle time than before */
- TS_SUB(&delta2, &devconf.cycle, &dt->min_cycle);
- if (TS_GZ(&delta2) &&
- dt->rate_switcher != NULL) {
- /* longer than minimum cycle time */
- if (dt->rate_switcher(device,
- TSTONS(&devconf.cycle))) {
- device->gpsdata.dev.cycle = devconf.cycle;
- }
- }
- }
- if ('\0' != devconf.hexdata[0]) {
- write_gps(device->gpsdata.dev.path, devconf.hexdata);
- }
- }
- }
- }
- /* dump a response for each selected channel */
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (!allocated_device(devp))
- continue;
- else if (devconf.path[0] != '\0'
- && strcmp(devp->gpsdata.dev.path, devconf.path) != 0)
- continue;
- else {
- json_device_dump(devp,
- reply + strlen(reply),
- replylen - strlen(reply));
- }
- }
- } else if (str_starts_with(buf, "?POLL;")) {
- char tbuf[JSON_DATE_MAX+1];
- int active = 0;
- buf += 6;
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (allocated_device(devp) && subscribed(sub, devp))
- if ((devp->observed & GPS_TYPEMASK) != 0)
- active++;
- }
- (void)snprintf(reply, replylen,
- "{\"class\":\"POLL\",\"time\":\"%s\",\"active\":%d,"
- "\"tpv\":[",
- now_to_iso8601(tbuf, sizeof(tbuf)), active);
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (allocated_device(devp) && subscribed(sub, devp)) {
- if ((devp->observed & GPS_TYPEMASK) != 0) {
- json_tpv_dump(NAVDATA_SET, devp, &sub->policy,
- reply + strlen(reply),
- replylen - strlen(reply));
- rstrip(reply);
- (void)strlcat(reply, ",", replylen);
- }
- }
- }
- str_rstrip_char(reply, ',');
- (void)strlcat(reply, "],\"gst\":[", replylen);
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (allocated_device(devp) && subscribed(sub, devp)) {
- if ((devp->observed & GPS_TYPEMASK) != 0) {
- json_noise_dump(&devp->gpsdata,
- reply + strlen(reply),
- replylen - strlen(reply));
- rstrip(reply);
- (void)strlcat(reply, ",", replylen);
- }
- }
- }
- str_rstrip_char(reply, ',');
- (void)strlcat(reply, "],\"sky\":[", replylen);
- for (devp = devices; devp < devices + MAX_DEVICES; devp++) {
- if (allocated_device(devp) && subscribed(sub, devp)) {
- if ((devp->observed & GPS_TYPEMASK) != 0) {
- json_sky_dump(&devp->gpsdata,
- reply + strlen(reply),
- replylen - strlen(reply));
- rstrip(reply);
- (void)strlcat(reply, ",", replylen);
- }
- }
- }
- str_rstrip_char(reply, ',');
- (void)strlcat(reply, "]}\r\n", replylen);
- } else if (str_starts_with(buf, "?VERSION;")) {
- buf += 9;
- json_version_dump(reply, replylen);
- } else {
- const char *errend;
- // a buffer to put the "quoted" bad json into
- char quoted_error[GPS_JSON_RESPONSE_MAX];
- errend = buf + strlen(buf);
- while (isspace((unsigned char) *errend) && errend > buf)
- --errend;
- (void)snprintf(reply, replylen,
- "{\"class\":\"ERROR\",\"message\":"
- "\"Unrecognized request '%s'\"}\r\n",
- json_quote(buf, quoted_error, errend - buf,
- sizeof(quoted_error)));
- GPSD_LOG(LOG_ERROR, &context.errout, "ERROR response: %s\n", reply);
- buf += strlen(buf);
- }
- bailout:
- *after = buf;
- }
- /* report a raw packet to a subscriber */
- static void raw_report(struct subscriber_t *sub, struct gps_device_t *device)
- {
- /* *INDENT-OFF* */
- /*
- * NMEA and other textual sentences are simply
- * copied to all clients that are in raw or nmea
- * mode.
- */
- if (TEXTUAL_PACKET_TYPE(device->lexer.type)
- && (sub->policy.raw > 0 || sub->policy.nmea)) {
- (void)throttled_write(sub,
- (char *)device->lexer.outbuffer,
- device->lexer.outbuflen);
- return;
- }
- /*
- * Also, simply copy if user has specified
- * super-raw mode.
- */
- if (sub->policy.raw > 1) {
- (void)throttled_write(sub,
- (char *)device->lexer.outbuffer,
- device->lexer.outbuflen);
- return;
- }
- #ifdef BINARY_ENABLE
- /*
- * Maybe the user wants a binary packet hexdumped.
- */
- if (sub->policy.raw == 1) {
- const char *hd =
- gpsd_hexdump(device->msgbuf, sizeof(device->msgbuf),
- (char *)device->lexer.outbuffer,
- device->lexer.outbuflen);
- (void)strlcat((char *)hd, "\r\n", sizeof(device->msgbuf));
- (void)throttled_write(sub, (char *)hd, strlen(hd));
- }
- #endif /* BINARY_ENABLE */
- }
- /* report pseudo-NMEA in appropriate circumstances */
- static void pseudonmea_report(struct subscriber_t *sub,
- gps_mask_t changed,
- struct gps_device_t *device)
- {
- if (GPS_PACKET_TYPE(device->lexer.type)
- && !TEXTUAL_PACKET_TYPE(device->lexer.type)) {
- char buf[MAX_PACKET_LENGTH * 3 + 2];
- if ((changed & REPORT_IS) != 0) {
- nmea_tpv_dump(device, buf, sizeof(buf));
- GPSD_LOG(LOG_IO, &context.errout,
- "<= GPS (binary tpv) %s: %s\n",
- device->gpsdata.dev.path, buf);
- (void)throttled_write(sub, buf, strlen(buf));
- }
- if ((changed & (SATELLITE_SET|USED_IS)) != 0) {
- nmea_sky_dump(device, buf, sizeof(buf));
- GPSD_LOG(LOG_IO, &context.errout,
- "<= GPS (binary sky) %s: %s\n",
- device->gpsdata.dev.path, buf);
- (void)throttled_write(sub, buf, strlen(buf));
- }
- if ((changed & SUBFRAME_SET) != 0) {
- nmea_subframe_dump(device, buf, sizeof(buf));
- GPSD_LOG(LOG_IO, &context.errout,
- "<= GPS (binary subframe) %s: %s\n",
- device->gpsdata.dev.path, buf);
- (void)throttled_write(sub, buf, strlen(buf));
- }
- #ifdef AIVDM_ENABLE
- if ((changed & AIS_SET) != 0) {
- nmea_ais_dump(device, buf, sizeof(buf));
- GPSD_LOG(LOG_IO, &context.errout,
- "<= AIS (binary ais) %s: %s\n",
- device->gpsdata.dev.path, buf);
- (void)throttled_write(sub, buf, strlen(buf));
- }
- #endif /* AIVDM_ENABLE */
- }
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- /* report on the current packet from a specified device */
- static void all_reports(struct gps_device_t *device, gps_mask_t changed)
- {
- #ifdef SOCKET_EXPORT_ENABLE
- struct subscriber_t *sub;
- /* add any just-identified device to watcher lists */
- if ((changed & DRIVER_IS) != 0) {
- bool listeners = false;
- for (sub = subscribers;
- sub < subscribers + MAX_CLIENTS; sub++)
- if (sub->active != 0
- && sub->policy.watcher
- && subscribed(sub, device))
- listeners = true;
- if (listeners) {
- (void)awaken(device);
- }
- }
- /* handle laggy response to a firmware version query */
- if ((changed & (DEVICEID_SET | DRIVER_IS)) != 0) {
- if (device->device_type == NULL)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "internal error - device type of %s not set "
- "when expected\n",
- device->gpsdata.dev.path);
- else
- {
- char id2[GPS_JSON_RESPONSE_MAX];
- json_device_dump(device, id2, sizeof(id2));
- notify_watchers(device, true, false, id2);
- }
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- /*
- * If the device provided an RTCM packet, repeat it to all devices.
- */
- if ((changed & RTCM2_SET) != 0 || (changed & RTCM3_SET) != 0) {
- if ((changed & RTCM2_SET) != 0
- && device->lexer.outbuflen > RTCM_MAX) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "overlong RTCM packet (%zd bytes)\n",
- device->lexer.outbuflen);
- } else if ((changed & RTCM3_SET) != 0
- && device->lexer.outbuflen > RTCM3_MAX) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "overlong RTCM3 packet (%zd bytes)\n",
- device->lexer.outbuflen);
- } else {
- struct gps_device_t *dp;
- for (dp = devices; dp < devices+MAX_DEVICES; dp++) {
- if (allocated_device(dp)) {
- /* *INDENT-OFF* */
- if (NULL != dp->device_type &&
- NULL != dp->device_type->rtcm_writer) {
- // FIXME: don't write back to source
- ssize_t ret = dp->device_type->rtcm_writer(dp,
- (const char *)device->lexer.outbuffer,
- device->lexer.outbuflen);
- if (0 < ret) {
- GPSD_LOG(LOG_IO, &context.errout,
- "<= DGPS: %zd bytes of RTCM relayed.\n",
- device->lexer.outbuflen);
- } else if (0 == ret) {
- // nothing written, probably read_only
- } else {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Write to RTCM sink failed, type %s\n",
- dp->device_type->type_name);
- }
- }
- /* *INDENT-ON* */
- }
- }
- }
- }
- /*
- * Time is eligible for shipping to NTPD if the driver has
- * asserted NTPTIME_IS at any point in the current cycle.
- */
- if ((changed & CLEAR_IS)!=0)
- device->ship_to_ntpd = false;
- if ((changed & NTPTIME_IS)!=0)
- device->ship_to_ntpd = true;
- /*
- * Only update the NTP time if we've seen the leap-seconds data.
- * Else we may be providing GPS time.
- */
- if ((changed & TIME_SET) == 0) {
- //GPSD_LOG(LOG_PROG, &context.errout, "NTP: No time this packet\n");
- } else if (0 >= device->fixcnt && false == context.batteryRTC) {
- /* many GPS spew random times until a valid GPS fix */
- /* allow override with -r option */
- //GPSD_LOG(LOG_PROG, &context.errout, "NTP: no fix\n");
- } else if (0 == device->newdata.time.tv_sec) {
- //GPSD_LOG(LOG_PROG, &context.errout, "NTP: bad new time\n");
- } else if (device->newdata.time.tv_sec <=
- device->pps_thread.fix_in.real.tv_sec) {
- //GPSD_LOG(LOG_PROG, &context.errout, "NTP: Not a new time\n");
- } else if (!device->ship_to_ntpd) {
- //GPSD_LOG(LOG_PROG, &context.errout,
- // "NTP: No precision time report\n");
- } else {
- struct timedelta_t td;
- struct gps_device_t *ppsonly;
- ntp_latch(device, &td);
- /* propagate this in-band-time to all PPS-only devices */
- for (ppsonly = devices; ppsonly < devices + MAX_DEVICES; ppsonly++)
- if (ppsonly->sourcetype == source_pps)
- pps_thread_fixin(&ppsonly->pps_thread, &td);
- if (device->shm_clock != NULL) {
- (void)ntpshm_put(device, device->shm_clock, &td);
- }
- #ifdef SOCKET_EXPORT_ENABLE
- notify_watchers(device, false, true,
- "{\"class\":\"TOFF\",\"device\":\"%s\",\"real_sec\":"
- "%lld, \"real_nsec\":%ld,\"clock_sec\":%lld,"
- "\"clock_nsec\":%ld}\r\n",
- device->gpsdata.dev.path,
- (long long)td.real.tv_sec, td.real.tv_nsec,
- (long long)td.clock.tv_sec, td.clock.tv_nsec);
- #endif /* SOCKET_EXPORT_ENABLE */
- }
- /*
- * If no reliable end of cycle, must report every time
- * a sentence changes position or mode. Likely to
- * cause display jitter.
- */
- if (!device->cycle_end_reliable && (changed & (LATLON_SET | MODE_SET))!=0)
- changed |= REPORT_IS;
- /* a few things are not per-subscriber reports */
- if ((changed & REPORT_IS) != 0) {
- #ifdef NETFEED_ENABLE
- if (device->gpsdata.fix.mode == MODE_3D) {
- struct gps_device_t *dgnss;
- /*
- * Pass the fix to every potential caster, here.
- * netgnss_report() individual caster types get to
- * make filtering decisiona.
- */
- for (dgnss = devices; dgnss < devices + MAX_DEVICES; dgnss++)
- if (dgnss != device)
- netgnss_report(&context, device, dgnss);
- }
- #endif /* NETFEED_ENABLE */
- #if defined(DBUS_EXPORT_ENABLE)
- if (device->gpsdata.fix.mode > MODE_NO_FIX)
- send_dbus_fix(device);
- #endif /* defined(DBUS_EXPORT_ENABLE) */
- }
- #ifdef SHM_EXPORT_ENABLE
- if ((changed & (REPORT_IS|GST_SET|SATELLITE_SET|SUBFRAME_SET|
- ATTITUDE_SET|RTCM2_SET|RTCM3_SET|AIS_SET)) != 0)
- shm_update(&context, &device->gpsdata);
- #endif /* SHM_EXPORT_ENABLE */
- #ifdef SOCKET_EXPORT_ENABLE
- /* update all subscribers associated with this device */
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++) {
- if (sub == NULL || sub->active == 0 || !subscribed(sub, device))
- continue;
- #ifdef PASSTHROUGH_ENABLE
- /* this is for passing through JSON packets */
- if ((changed & PASSTHROUGH_IS) != 0) {
- (void)strlcat((char *)device->lexer.outbuffer,
- "\r\n",
- sizeof(device->lexer.outbuffer));
- (void)throttled_write(sub,
- (char *)device->lexer.outbuffer,
- device->lexer.outbuflen+2);
- continue;
- }
- #endif /* PASSTHROUGH_ENABLE */
- /* report raw packets to users subscribed to those */
- raw_report(sub, device);
- /* some listeners may be in watcher mode */
- if (sub->policy.watcher) {
- if (changed & DATA_IS) {
- GPSD_LOG(LOG_PROG, &context.errout,
- "Changed mask: %s with %sreliable "
- "cycle detection\n",
- gps_maskdump(changed),
- device->cycle_end_reliable ? "" : "un");
- if ((changed & REPORT_IS) != 0)
- GPSD_LOG(LOG_PROG, &context.errout,
- "time to report a fix\n");
- if (sub->policy.nmea)
- pseudonmea_report(sub, changed, device);
- if (sub->policy.json) {
- char buf[GPS_JSON_RESPONSE_MAX * 4];
- if ((changed & AIS_SET) != 0)
- if (device->gpsdata.ais.type == 24
- && device->gpsdata.ais.type24.part != both
- && !sub->policy.split24)
- continue;
- json_data_report(changed, device, &sub->policy,
- buf, sizeof(buf));
- if (buf[0] != '\0')
- (void)throttled_write(sub, buf, strlen(buf));
- }
- }
- }
- } /* subscribers */
- #endif /* SOCKET_EXPORT_ENABLE */
- }
- #ifdef SOCKET_EXPORT_ENABLE
- /* Execute GPSD requests (?POLL, ?WATCH, etc.) from a buffer.
- * The entire request must be in the buffer.
- */
- static int handle_gpsd_request(struct subscriber_t *sub, const char *buf)
- {
- char reply[GPS_JSON_RESPONSE_MAX + 1];
- reply[0] = '\0';
- if (buf[0] == '?') {
- const char *end;
- for (end = buf; *buf != '\0'; buf = end)
- if (isspace((unsigned char) *buf))
- end = buf + 1;
- else
- handle_request(sub, buf, &end,
- reply + strlen(reply),
- sizeof(reply) - strlen(reply));
- }
- return (int)throttled_write(sub, reply, strlen(reply));
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- #if defined(CONTROL_SOCKET_ENABLE) && defined(SOCKET_EXPORT_ENABLE)
- /* on PPS interrupt, ship a message to all clients */
- static void ship_pps_message(struct gps_device_t *session,
- struct timedelta_t *td)
- {
- int precision = -20;
- char buf[GPS_JSON_RESPONSE_MAX];
- char ts_str[TIMESPEC_LEN];
- if ( source_usb == session->sourcetype) {
- /* PPS over USB not so good */
- precision = -10;
- }
- GPSD_LOG(LOG_DATA, &session->context->errout,
- "ship_pps: qErr_time %s qErr %ld, pps.tv_sec %lld\n",
- timespec_str(&session->gpsdata.qErr_time, ts_str, sizeof(ts_str)),
- session->gpsdata.qErr,
- (long long)td->real.tv_sec);
- /* real_XXX - the time the GPS thinks it is at the PPS edge */
- /* clock_XXX - the time the system clock thinks it is at the PPS edge */
- (void)snprintf(buf, sizeof(buf),
- "{\"class\":\"PPS\",\"device\":\"%s\",\"real_sec\":%lld,"
- "\"real_nsec\":%ld,\"clock_sec\":%lld,\"clock_nsec\":%ld,"
- "\"precision\":%d",
- session->gpsdata.dev.path,
- (long long)td->real.tv_sec, td->real.tv_nsec,
- (long long)td->clock.tv_sec, td->clock.tv_nsec,
- precision);
- // output qErr if timestamps line up
- if (td->real.tv_sec == session->gpsdata.qErr_time.tv_sec) {
- str_appendf(buf, sizeof(buf), ",\"qErr\":%ld",
- session->gpsdata.qErr);
- }
- (void)strlcat(buf, "}\r\n", sizeof(buf));
- notify_watchers(session, true, true, buf);
- /*
- * PPS receipt resets the device's timeout. This keeps PPS-only
- * devices, which never deliver in-band data, from timing out.
- */
- (void)clock_gettime(CLOCK_REALTIME, &session->gpsdata.online);
- }
- #endif
- #ifdef __UNUSED_AUTOCONNECT__
- #define DGPS_THRESHOLD 1600000 /* max. useful dist. from DGPS server (m) */
- #define SERVER_SAMPLE 12 /* # of servers within threshold to check */
- struct dgps_server_t
- {
- double lat, lon;
- char server[257];
- double dist;
- };
- static int srvcmp(const void *s, const void *t)
- {
- /* fixes: warning: cast discards qualifiers from pointer target type */
- return (int)(((const struct dgps_server_t *)s)->dist -
- ((const struct dgps_server_t *)t)->dist);
- }
- /* tell the library to talk to the nearest DGPSIP server */
- static void netgnss_autoconnect(struct gps_context_t *context,
- double lat, double lon, const char *serverlist)
- {
- struct dgps_server_t keep[SERVER_SAMPLE], hold, *sp, *tp;
- char buf[BUFSIZ];
- FILE *sfp = fopen(serverlist, "r");
- if (sfp == NULL) {
- GPSD_LOG(LOG_ERROR, &context.errout, "no DGPS server list found.\n");
- return;
- }
- for (sp = keep; sp < keep + SERVER_SAMPLE; sp++) {
- sp->dist = DGPS_THRESHOLD;
- sp->server[0] = '\0';
- }
- hold.lat = hold.lon = 0;
- while (fgets(buf, (int)sizeof(buf), sfp)) {
- char *cp = strchr(buf, '#');
- if (cp != NULL)
- *cp = '\0';
- if (sscanf(buf, "%32lf %32lf %256s",
- &hold.lat, &hold.lon, hold.server) == 3) {
- hold.dist = earth_distance(lat, lon, hold.lat, hold.lon);
- tp = NULL;
- /*
- * The idea here is to look for a server in the sample array
- * that is (a) closer than the one we're checking, and (b)
- * furtherest away of all those that are closer. Replace it.
- * In this way we end up with the closest possible set.
- */
- for (sp = keep; sp < keep + SERVER_SAMPLE; sp++)
- if (hold.dist < sp->dist
- && (tp == NULL || hold.dist > tp->dist))
- tp = sp;
- if (tp != NULL)
- *tp = hold;
- }
- }
- (void)fclose(sfp);
- if (keep[0].server[0] == '\0') {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "no DGPS servers within %dm.\n",
- (int)(DGPS_THRESHOLD / 1000));
- return;
- }
- /* sort them and try the closest first */
- qsort((void *)keep, SERVER_SAMPLE, sizeof(struct dgps_server_t), srvcmp);
- for (sp = keep; sp < keep + SERVER_SAMPLE; sp++) {
- if (sp->server[0] != '\0') {
- GPSD_LOG(LOG_INF, &context.errout,
- "%s is %dkm away.\n", sp->server,
- (int)(sp->dist / 1000));
- if (dgpsip_open(context, sp->server) >= 0)
- break;
- }
- }
- }
- #endif /* __UNUSED_AUTOCONNECT__ */
- /* finish cleanly, reverting device configuration */
- static void gpsd_terminate(struct gps_context_t *context)
- {
- int dfd;
- for (dfd = 0; dfd < MAX_DEVICES; dfd++) {
- if (allocated_device(&devices[dfd])) {
- (void)gpsd_wrap(&devices[dfd]);
- }
- }
- context->pps_hook = NULL; /* tell any PPS-watcher thread to die */
- }
- int main(int argc, char *argv[])
- {
- /* some of these statics suppress -W warnings due to longjmp() */
- #ifdef SOCKET_EXPORT_ENABLE
- static char *gpsd_service = NULL;
- struct subscriber_t *sub;
- #endif /* SOCKET_EXPORT_ENABLE */
- fd_set rfds;
- #ifdef CONTROL_SOCKET_ENABLE
- fd_set control_fds;
- #endif /* CONTROL_SOCKET_ENABLE */
- #ifdef CONTROL_SOCKET_ENABLE
- static socket_t csock;
- socket_t cfd;
- static char *control_socket = NULL;
- #endif /* CONTROL_SOCKET_ENABLE */
- #if defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE)
- sockaddr_t fsin;
- #endif /* defined(SOCKET_EXPORT_ENABLE) || defined(CONTROL_SOCKET_ENABLE) */
- static char *pid_file = NULL;
- struct gps_device_t *device;
- int i;
- int msocks[2] = {-1, -1};
- bool device_opened = false;
- bool go_background = true;
- volatile bool in_restart;
- gps_context_init(&context, "gpsd");
- #ifdef CONTROL_SOCKET_ENABLE
- INVALIDATE_SOCKET(csock);
- #if defined(SOCKET_EXPORT_ENABLE)
- context.pps_hook = ship_pps_message;
- #endif /* SOCKET_EXPORT_ENABLE */
- #endif /* CONTROL_SOCKET_ENABLE */
- while (1) {
- const char *optstring = "bD:F:f:GhlNnpP:rS:s:V";
- int ch;
- #ifdef HAVE_GETOPT_LONG
- int option_index = 0;
- static struct option long_options[] = {
- {"badtime", no_argument, NULL, 'r'},
- {"debug", required_argument, NULL, 'D'},
- {"drivers", no_argument, NULL, 'l'},
- {"foreground", no_argument, NULL, 'N'},
- {"framing", required_argument, NULL, 'f'},
- {"help", required_argument, NULL, 'h'},
- {"listenany", no_argument, NULL, 'G' },
- {"nowait", no_argument, NULL, 'n' },
- {"readonly", no_argument, NULL, 'b'},
- {"passive", no_argument, NULL, 'p'},
- {"pidfile", no_argument, NULL, 'P'},
- {"port", no_argument, NULL, 'S'},
- {"sockfile", required_argument, NULL, 'F'},
- {"speed", required_argument, NULL, 's'},
- {"version", no_argument, NULL, 'V' },
- {NULL, 0, NULL, 0},
- };
- ch = getopt_long(argc, argv, optstring, long_options, &option_index);
- #else
- ch = getopt(argc, argv, optstring);
- #endif
- if (ch == -1) {
- break;
- }
- switch (ch) {
- case 'b':
- context.readonly = true;
- break;
- case 'D':
- // accept decimal, octal and hex
- context.errout.debug = (int)strtol(optarg, 0, 0);
- gps_enable_debug(context.errout.debug, stderr);
- break;
- #ifdef CONTROL_SOCKET_ENABLE
- case 'F':
- control_socket = optarg;
- break;
- #endif /* CONTROL_SOCKET_ENABLE */
- case 'f':
- // framing
- if (3 == strlen(optarg) &&
- ('7' == optarg[0] || '8' == optarg[0]) &&
- ('E' == optarg[1] || 'N' == optarg[1] ||
- 'O' == optarg[1]) &&
- ('0' <= optarg[2] && '2' >= optarg[2])) {
- // [78][ENO][012]
- (void)strlcpy(context.fixed_port_framing, optarg,
- sizeof(context.fixed_port_framing));
- } else {
- // invalid framing
- GPSD_LOG(LOG_ERROR, &context.errout,
- "-f has invalid framing %s\n", optarg);
- exit(1);
- }
- break;
- case 'G':
- listen_global = true;
- break;
- case 'l': /* list known device types and exit */
- typelist();
- break;
- case 'N':
- go_background = false;
- break;
- case 'n':
- nowait = true;
- break;
- case 'p':
- context.passive = true;
- break;
- case 'P':
- pid_file = optarg;
- break;
- case 'r':
- // -r, --badtime, remove fix checks for good time. DANGEROUS
- context.batteryRTC = true;
- break;
- case 'S':
- #ifdef SOCKET_EXPORT_ENABLE
- gpsd_service = optarg;
- #endif /* SOCKET_EXPORT_ENABLE */
- break;
- case 's':
- {
- // accept decimal, octal and hex
- long speed = strtol(optarg, 0, 0);
- if (0 < speed) {
- // allow weird speeds
- context.fixed_port_speed = (speed_t)speed;
- } else {
- // invalid speed
- GPSD_LOG(LOG_ERROR, &context.errout,
- "-s has invalid speed %ld\n", speed);
- exit(1);
- }
- }
- break;
- case 'V':
- (void)printf("%s: %s (revision %s)\n", argv[0], VERSION, REVISION);
- exit(EXIT_SUCCESS);
- case 'h':
- FALLTHROUGH
- case '?':
- FALLTHROUGH
- default:
- usage();
- exit(EXIT_SUCCESS);
- }
- }
- /* sanity check */
- if (argc - optind > MAX_DEVICES) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Too many devices on command line.\n");
- exit(1);
- }
- if (8 > sizeof(time_t)) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "This system has a 32-bit time_t. "
- "This gpsd will fail at 2038-01-19T03:14:07Z.\n");
- }
- #if defined(SYSTEMD_ENABLE) && defined(CONTROL_SOCKET_ENABLE)
- sd_socket_count = sd_get_socket_count();
- if (sd_socket_count > 0 && control_socket != NULL) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "control socket passed on command line ignored\n");
- control_socket = NULL;
- }
- #endif
- #if defined(CONTROL_SOCKET_ENABLE) || defined(SYSTEMD_ENABLE)
- if (
- #ifdef CONTROL_SOCKET_ENABLE
- control_socket == NULL
- #endif
- #if defined(CONTROL_SOCKET_ENABLE) && defined(SYSTEMD_ENABLE)
- &&
- #endif
- #ifdef SYSTEMD_ENABLE
- sd_socket_count <= 0
- #endif
- && optind >= argc) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't run with neither control socket nor devices\n");
- exit(EXIT_FAILURE);
- }
- /*
- * Control socket has to be created before we go background in order to
- * avoid a race condition in which hotplug scripts can try opening
- * the socket before it's created.
- */
- #if defined(SYSTEMD_ENABLE) && defined(CONTROL_SOCKET_ENABLE)
- if (sd_socket_count > 0) {
- csock = SD_SOCKET_FDS_START;
- FD_SET(csock, &all_fds);
- adjust_max_fd(csock, true);
- }
- #endif
- #ifdef CONTROL_SOCKET_ENABLE
- if (control_socket) {
- (void)unlink(control_socket);
- if (BAD_SOCKET(csock = filesock(control_socket))) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "control socket create failed, netlib error %d\n",
- csock);
- exit(EXIT_FAILURE);
- } else
- GPSD_LOG(LOG_SPIN, &context.errout,
- "control socket %s is fd %d\n",
- control_socket, csock);
- FD_SET(csock, &all_fds);
- adjust_max_fd(csock, true);
- GPSD_LOG(LOG_PROG, &context.errout,
- "control socket opened at %s\n",
- control_socket);
- }
- #endif /* CONTROL_SOCKET_ENABLE */
- #else
- if (optind >= argc) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't run with no devices specified\n");
- exit(EXIT_FAILURE);
- }
- #endif /* defined(CONTROL_SOCKET_ENABLE) || defined(SYSTEMD_ENABLE) */
- // TODO, a dump of all options here at LOG_xx would be nice.
- /* might be time to daemonize */
- if (go_background) {
- /* not SuS/POSIX portable, but we have our own fallback version */
- if (os_daemon(0, 0) != 0)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "daemonization failed: %s\n",strerror(errno));
- }
- if (pid_file != NULL) {
- FILE *fp;
- if ((fp = fopen(pid_file, "w")) != NULL) {
- (void)fprintf(fp, "%u\n", (unsigned int)getpid());
- (void)fclose(fp);
- } else {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Cannot create PID file: %s.\n", pid_file);
- }
- }
- openlog("gpsd", LOG_PID, LOG_USER);
- GPSD_LOG(LOG_INF, &context.errout, "launching (Version %s)\n", VERSION);
- #ifdef SOCKET_EXPORT_ENABLE
- if (!gpsd_service)
- gpsd_service =
- getservbyname("gpsd", "tcp") ? "gpsd" : DEFAULT_GPSD_PORT;
- if (passivesocks(gpsd_service, "tcp", QLEN, msocks) < 1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "command sockets creation failed, netlib errors %d, %d\n",
- msocks[0], msocks[1]);
- if (pid_file != NULL)
- (void)unlink(pid_file);
- exit(EXIT_FAILURE);
- }
- GPSD_LOG(LOG_INF, &context.errout, "listening on port %s\n",
- gpsd_service);
- #endif /* SOCKET_EXPORT_ENABLE */
- if (getuid() == 0) {
- errno = 0;
- // nice() can ONLY succeed when run as root!
- // do not even bother as non-root
- if (nice(NICEVAL) == -1 && errno != 0)
- GPSD_LOG(LOG_WARN, &context.errout,
- "PPS: o=priority setting failed. Time accuracy "
- "will be degraded\n");
- }
- /*
- * By initializing before we drop privileges, we guarantee that even
- * hotplugged devices added *after* we drop privileges will be able
- * to use segments 0 and 1.
- */
- (void)ntpshm_context_init(&context);
- #if defined(DBUS_EXPORT_ENABLE)
- /* we need to connect to dbus as root */
- if (initialize_dbus_connection()) {
- /* the connection could not be started */
- GPSD_LOG(LOG_ERROR, &context.errout,
- "unable to connect to the DBUS system bus\n");
- } else
- GPSD_LOG(LOG_PROG, &context.errout,
- "successfully connected to the DBUS system bus\n");
- #endif /* defined(DBUS_EXPORT_ENABLE) */
- #ifdef SHM_EXPORT_ENABLE
- /* create the shared segment as root so readers can't mess with it */
- (void)shm_acquire(&context);
- #endif /* SHM_EXPORT_ENABLE */
- /*
- * We open devices specified on the command line *before* dropping
- * privileges in case one of them is a serial device with PPS support
- * and we need to set the line discipline, which requires root.
- */
- in_restart = false;
- for (i = optind; i < argc; i++) {
- if (!gpsd_add_device(argv[i], nowait)) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "initial GPS device %s open failed\n",
- argv[i]);
- } else {
- device_opened = true;
- }
- }
- if (
- #ifdef CONTROL_SOCKET_ENABLE
- control_socket == NULL &&
- #endif
- #ifdef SYSTEMD_ENABLE
- sd_socket_count <= 0 &&
- #endif
- !device_opened) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "can't run with neither control socket nor devices open\n");
- exit(EXIT_FAILURE);
- }
- /* drop privileges */
- if (0 == getuid()) {
- struct passwd *pw;
- struct stat stb;
- /* Make default devices accessible even after we drop privileges.
- * Modifying file system permissions! */
- for (i = optind; i < argc; i++) {
- if (GPS_PATH_MAX < strlen(argv[i])) {
- // pacify coverity
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Over long device path %s\n", argv[i]);
- }
- if (stat(argv[i], &stb) == 0) {
- /* This fails if not running as root, or have group
- * access to the file. */
- (void)chmod(argv[i], stb.st_mode | S_IRGRP | S_IWGRP);
- }
- }
- /*
- * Drop privileges. Up to now we've been running as root.
- * Instead, set the user ID to 'nobody' (or whatever the gpsd
- * user set by the build is) and the group ID to the owning
- * group of a prototypical TTY device. This limits the scope
- * of any compromises in the code. It requires that all GPS
- * devices have their group read/write permissions set.
- */
- if (setgroups(0, NULL) != 0)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "setgroups() failed, errno %s\n",
- strerror(errno));
- #ifdef GPSD_GROUP
- {
- struct group *grp = getgrnam(GPSD_GROUP);
- if (grp)
- if (setgid(grp->gr_gid) != 0)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "setgid() failed, errno %s\n",
- strerror(errno));
- }
- #else
- if ((optind < argc && stat(argv[optind], &stb) == 0)
- || stat(PROTO_TTY, &stb) == 0) {
- GPSD_LOG(LOG_PROG, &context.errout,
- "changing to group %d\n", stb.st_gid);
- if (setgid(stb.st_gid) != 0)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "setgid() failed, errno %s\n",
- strerror(errno));
- }
- #endif
- pw = getpwnam(GPSD_USER);
- if (pw)
- if (setuid(pw->pw_uid) != 0)
- GPSD_LOG(LOG_ERROR, &context.errout,
- "setuid() failed, errno %s\n",
- strerror(errno));
- }
- // sometimes getegid() and geteuid() are longs
- GPSD_LOG(LOG_INF, &context.errout,
- "running with effective group ID %ld\n", (long)getegid());
- GPSD_LOG(LOG_INF, &context.errout,
- "running with effective user ID %ld\n", (long)geteuid());
- #ifdef SOCKET_EXPORT_ENABLE
- for (i = 0; i < NITEMS(subscribers); i++) {
- subscribers[i].fd = UNALLOCATED_FD;
- (void)pthread_mutex_init(&subscribers[i].mutex, NULL);
- }
- #endif /* SOCKET_EXPORT_ENABLE*/
- {
- struct sigaction sa;
- sa.sa_flags = 0;
- #ifdef __COVERITY__
- /*
- * Obsolete and unused. We're only doing this to pacify Coverity
- * which otherwise throws an UNINIT event here. Don't swap with the
- * handler initialization, they're unioned on some architectures.
- */
- sa.sa_restorer = NULL;
- #endif /* __COVERITY__ */
- sa.sa_handler = onsig;
- (void)sigfillset(&sa.sa_mask);
- (void)sigaction(SIGHUP, &sa, NULL);
- (void)sigaction(SIGINT, &sa, NULL);
- (void)sigaction(SIGTERM, &sa, NULL);
- (void)sigaction(SIGQUIT, &sa, NULL);
- (void)signal(SIGPIPE, SIG_IGN);
- }
- /* daemon got termination or interrupt signal */
- if (setjmp(restartbuf) > 0) {
- gpsd_terminate(&context);
- in_restart = true;
- GPSD_LOG(LOG_WARN, &context.errout, "gpsd restarted by SIGHUP\n");
- }
- signalled = 0;
- for (i = 0; i < AFCOUNT; i++)
- if (msocks[i] >= 0) {
- FD_SET(msocks[i], &all_fds);
- adjust_max_fd(msocks[i], true);
- }
- #ifdef CONTROL_SOCKET_ENABLE
- FD_ZERO(&control_fds);
- #endif /* CONTROL_SOCKET_ENABLE */
- /* initialize the GPS context's time fields */
- gpsd_time_init(&context, time(NULL));
- /*
- * If we got here via SIGINT, reopen any command-line devices. PPS
- * through these won't work, as we've dropped privileges and can
- * no longer change line disciplines.
- */
- if (in_restart)
- for (i = optind; i < argc; i++) {
- if (!gpsd_add_device(argv[i], nowait)) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "GPS device %s open failed\n",
- argv[i]);
- }
- }
- while (0 == signalled) {
- fd_set efds;
- GPSD_LOG(LOG_RAW + 1, &context.errout, "await data\n");
- switch(gpsd_await_data(&rfds, &efds, maxfd, &all_fds, &context.errout))
- {
- case AWAIT_GOT_INPUT:
- break;
- case AWAIT_NOT_READY:
- for (device = devices; device < devices + MAX_DEVICES; device++)
- /*
- * The file descriptor validity check is required on some ARM
- * platforms to prevent a core dump. This may be due to an
- * implementation error in FD_ISSET().
- */
- if (allocated_device(device) &&
- (0 <= device->gpsdata.gps_fd &&
- device->gpsdata.gps_fd < (socket_t)FD_SETSIZE) &&
- FD_ISSET(device->gpsdata.gps_fd, &efds)) {
- deactivate_device(device);
- free_device(device);
- }
- continue;
- case AWAIT_FAILED:
- exit(EXIT_FAILURE);
- }
- #ifdef SOCKET_EXPORT_ENABLE
- /* always be open to new client connections */
- for (i = 0; i < AFCOUNT; i++) {
- if (msocks[i] >= 0 && FD_ISSET(msocks[i], &rfds)) {
- socklen_t alen = (socklen_t) sizeof(fsin);
- socket_t ssock =
- accept(msocks[i], (struct sockaddr *)&fsin, &alen);
- if (BAD_SOCKET(ssock))
- GPSD_LOG(LOG_ERROR, &context.errout,
- "accept: fail: %s\n", strerror(errno));
- else {
- struct subscriber_t *client = NULL;
- int opts = fcntl(ssock, F_GETFL);
- static struct linger linger = { 1, RELEASE_TIMEOUT };
- char *c_ip;
- if (opts >= 0)
- (void)fcntl(ssock, F_SETFL, opts | O_NONBLOCK);
- c_ip = netlib_sock2ip(ssock);
- client = allocate_client();
- if (client == NULL) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Client %s connect on fd %d -"
- "no subscriber slots available\n", c_ip,
- ssock);
- (void)close(ssock);
- } else
- if (setsockopt
- (ssock, SOL_SOCKET, SO_LINGER, (char *)&linger,
- (int)sizeof(struct linger)) == -1) {
- GPSD_LOG(LOG_ERROR, &context.errout,
- "Error: SETSOCKOPT SO_LINGER\n");
- (void)close(ssock);
- } else {
- char announce[GPS_JSON_RESPONSE_MAX];
- FD_SET(ssock, &all_fds);
- adjust_max_fd(ssock, true);
- client->fd = ssock;
- client->active = time(NULL);
- GPSD_LOG(LOG_SPIN, &context.errout,
- "client %s (%d) connect on fd %d\n", c_ip,
- sub_index(client), ssock);
- json_version_dump(announce, sizeof(announce));
- (void)throttled_write(client, announce,
- strlen(announce));
- }
- }
- FD_CLR(msocks[i], &rfds);
- }
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- #ifdef CONTROL_SOCKET_ENABLE
- /* also be open to new control-socket connections */
- if (csock > -1 && FD_ISSET(csock, &rfds)) {
- socklen_t alen = (socklen_t) sizeof(fsin);
- socket_t ssock = accept(csock, (struct sockaddr *)&fsin, &alen);
- if (BAD_SOCKET(ssock))
- GPSD_LOG(LOG_ERROR, &context.errout,
- "accept: %s\n", strerror(errno));
- else {
- GPSD_LOG(LOG_INF, &context.errout,
- "control socket connect on fd %d\n",
- ssock);
- FD_SET(ssock, &all_fds);
- FD_SET(ssock, &control_fds);
- adjust_max_fd(ssock, true);
- }
- FD_CLR(csock, &rfds);
- }
- /* read any commands that came in over the control socket */
- GPSD_LOG(LOG_RAW + 1, &context.errout, "read control commands");
- for (cfd = 0; cfd < (int)FD_SETSIZE; cfd++)
- if (FD_ISSET(cfd, &control_fds)) {
- char buf[BUFSIZ];
- ssize_t rd;
- while ((rd = read(cfd, buf, sizeof(buf) - 1)) > 0) {
- buf[rd] = '\0';
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "<= control(%d): %s\n", cfd, buf);
- /* coverity[tainted_data] Safe, never handed to exec */
- handle_control(cfd, buf);
- }
- GPSD_LOG(LOG_SPIN, &context.errout,
- "close(%d) of control socket\n", cfd);
- (void)close(cfd);
- FD_CLR(cfd, &all_fds);
- FD_CLR(cfd, &control_fds);
- adjust_max_fd(cfd, false);
- }
- #endif /* CONTROL_SOCKET_ENABLE */
- /* poll all active devices */
- GPSD_LOG(LOG_RAW + 1, &context.errout, "poll active devices\n");
- for (device = devices; device < devices + MAX_DEVICES; device++)
- if (allocated_device(device) && device->gpsdata.gps_fd > 0)
- switch (gpsd_multipoll(FD_ISSET(device->gpsdata.gps_fd, &rfds),
- device, all_reports, DEVICE_REAWAKE))
- {
- case DEVICE_READY:
- FD_SET(device->gpsdata.gps_fd, &all_fds);
- adjust_max_fd(device->gpsdata.gps_fd, true);
- break;
- case DEVICE_UNREADY:
- FD_CLR(device->gpsdata.gps_fd, &all_fds);
- adjust_max_fd(device->gpsdata.gps_fd, false);
- break;
- case DEVICE_ERROR:
- case DEVICE_EOF:
- deactivate_device(device);
- break;
- default:
- break;
- }
- #ifdef __UNUSED_AUTOCONNECT__
- if (context.fixcnt > 0 && !context.autconnect) {
- for (device = devices; device < devices + MAX_DEVICES; device++) {
- if (device->gpsdata.fix.mode > MODE_NO_FIX) {
- netgnss_autoconnect(&context,
- device->gpsdata.fix.latitude,
- device->gpsdata.fix.longitude);
- context.autconnect = True;
- break;
- }
- }
- }
- #endif /* __UNUSED_AUTOCONNECT__ */
- #ifdef SOCKET_EXPORT_ENABLE
- /* accept and execute commands for all clients */
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++) {
- if (sub->active == 0)
- continue;
- lock_subscriber(sub);
- if (FD_ISSET(sub->fd, &rfds)) {
- char buf[BUFSIZ];
- ssize_t buflen;
- unlock_subscriber(sub);
- GPSD_LOG(LOG_PROG, &context.errout,
- "checking client(%d)\n",
- sub_index(sub));
- buflen = recv(sub->fd, buf, sizeof(buf) - 1, 0);
- if (0 > buflen) {
- // recv() error, give up.
- detach_client(sub);
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "<= client(%d): error read\n", sub_index(sub));
- } else if (0 == buflen) {
- /* Ugh, "man recv" says recv() returns 0 on disconnect!
- * So we have to disconnect client.
- * But somehow, dormant serial connections also
- * return 0. Should FD_ISSET() have prevented getting
- * here in that case?
- */
- detach_client(sub);
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "<= client(%d): eof read\n", sub_index(sub));
- } else {
- if (buf[buflen - 1] != '\n')
- buf[buflen++] = '\n';
- buf[buflen] = '\0';
- GPSD_LOG(LOG_CLIENT, &context.errout,
- "<= client(%d): %s\n", sub_index(sub), buf);
- /*
- * When a command comes in, update subscriber.active to
- * timestamp() so we don't close the connection
- * after COMMAND_TIMEOUT seconds. This makes
- * COMMAND_TIMEOUT useful.
- */
- sub->active = time(NULL);
- if (handle_gpsd_request(sub, buf) < 0)
- detach_client(sub);
- }
- } else {
- unlock_subscriber(sub);
- if (!sub->policy.watcher
- && time(NULL) - sub->active > COMMAND_TIMEOUT) {
- GPSD_LOG(LOG_WARN, &context.errout,
- "client(%d) timed out on command wait.\n",
- sub_index(sub));
- detach_client(sub);
- }
- }
- }
- /*
- * Mark devices with an identified packet type but no
- * remaining subscribers to be closed in RELEASE_TIME seconds.
- * See the explanation of RELEASE_TIME for the reasoning.
- *
- * Re-poll devices that are disconnected, but have potential
- * subscribers in the same cycle.
- */
- for (device = devices; device < devices + MAX_DEVICES; device++) {
- bool device_needed = nowait;
- if (!allocated_device(device))
- continue;
- if (!device_needed)
- for (sub=subscribers; sub<subscribers+MAX_CLIENTS; sub++) {
- if (sub->active == 0)
- continue;
- device_needed = subscribed(sub, device);
- if (device_needed)
- break;
- }
- if (!device_needed && device->gpsdata.gps_fd > -1 &&
- device->lexer.type != BAD_PACKET) {
- if (device->releasetime == 0) {
- device->releasetime = time(NULL);
- GPSD_LOG(LOG_PROG, &context.errout,
- "device %d (fd %d) released\n",
- (int)(device - devices),
- device->gpsdata.gps_fd);
- } else if (time(NULL) - device->releasetime > RELEASE_TIMEOUT) {
- GPSD_LOG(LOG_PROG, &context.errout,
- "device %d closed\n",
- (int)(device - devices));
- GPSD_LOG(LOG_RAW, &context.errout,
- "unflagging descriptor %d\n",
- device->gpsdata.gps_fd);
- deactivate_device(device);
- }
- }
- if (device_needed && BAD_SOCKET(device->gpsdata.gps_fd) &&
- (device->opentime == 0 ||
- time(NULL) - device->opentime > DEVICE_RECONNECT)) {
- device->opentime = time(NULL);
- GPSD_LOG(LOG_INF, &context.errout,
- "reconnection attempt on device %d\n",
- (int)(device - devices));
- (void)awaken(device);
- }
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- /*
- * Might be time for graceful shutdown if no command-line
- * devices were specified, there are no subscribers, there are
- * no active devices, and there *have been* active
- * devices. The goal is to go away and free up text space when
- * the daemon was hotplug-activated but there are no
- * subscribers and the last GPS has unplugged, and the point
- * of the last check is to prevent shutdown when the daemon
- * has been launched but not yet received its first device
- * over the socket.
- */
- if (argc == optind && highwater > 0) {
- int subcount = 0, devcount = 0;
- #ifdef SOCKET_EXPORT_ENABLE
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++)
- if (sub->active != 0)
- ++subcount;
- #endif /* SOCKET_EXPORT_ENABLE */
- for (device = devices; device < devices + MAX_DEVICES; device++)
- if (allocated_device(device))
- ++devcount;
- if (subcount == 0 && devcount == 0) {
- GPSD_LOG(LOG_SHOUT, &context.errout,
- "no subscribers or devices, shutting down.\n");
- goto shutdown;
- }
- }
- }
- /* if we make it here, we got a signal... deal with it */
- /* restart on SIGHUP, clean up and exit otherwise */
- if (SIGHUP == (int)signalled)
- longjmp(restartbuf, 1);
- GPSD_LOG(LOG_WARN, &context.errout,
- "received terminating signal %d.\n", (int)signalled);
- shutdown:
- gpsd_terminate(&context);
- GPSD_LOG(LOG_WARN, &context.errout, "exiting.\n");
- #ifdef SOCKET_EXPORT_ENABLE
- /*
- * A linger option was set on each client socket when it was
- * created. Now, shut them down gracefully, letting I/O drain.
- * This is an attempt to avoid the sporadic race errors at the ends
- * of our regression tests.
- */
- for (sub = subscribers; sub < subscribers + MAX_CLIENTS; sub++) {
- if (sub->active != 0)
- detach_client(sub);
- }
- #endif /* SOCKET_EXPORT_ENABLE */
- #ifdef SHM_EXPORT_ENABLE
- shm_release(&context);
- #endif /* SHM_EXPORT_ENABLE */
- #ifdef CONTROL_SOCKET_ENABLE
- if (control_socket)
- (void)unlink(control_socket);
- #endif /* CONTROL_SOCKET_ENABLE */
- if (pid_file)
- (void)unlink(pid_file);
- return 0;
- }
- // vim: set expandtab shiftwidth=4
|