gpsd.c
Functions:-This is the main body of the daemon.
Notes based on code as of Mon Apr 5 21:38:06 2010 -0400.
static void onsig(int sig)
This is a simple catchall routine to trap wanted
signal. Simply store the signal number in a variable to advise the
main loop which signal to handle.
static int daemonize(void)
Try to fork() a child process. The
parent will get a return value of either -1 (on a failure to
fork()) or non-zero (the child's PID). The
parent routine will use this information to either return -1 or exit
with an exit code of 0 (i.e. the parent terminates, leaving the
child running).The child instance gets a return value
of 0 (on an unsuccessful fork() there is no
child), so this value is used as the trigger to do the following
useful stuff.First, the child tries to create a new
session, returning -1 if unable to do so. If succesful, it will have
inherited the exiting parent's session.Next switch to
the root directory and try to open /dev/null
. If that
succeeds, force stdin, stdout and stderr to duplicate the fd of
/dev/null
. If the fd being used for the operation is
>2, release it.Finally, set the flag which indicates
the process is in the background and return a value of
0.
void gpsd_report(int errlevel, const char *fmt, ... )
This code is used for error reporting, but is dependant
on SQUELCH_DISABLE so that embedded systems (for example) are not
burdened with unnecessary noise. The first thing to check is if the
error level offered is high enough to be of interest (controlled by
the debug level we are running at).If we are
interested, the first step is to protect the code with a mutex if we
are using the 1PPS input.Now we build a message buffer
which has a fixed header (gpsd:
) and the incoming
data. The output buffer is prepared (load the start with a NULL) and
then the input buffer is scanned, byte-by-byte, up to its
terminating NULL. The scanned data is transferred on the fly to the
output buffer subject to the following tests:-If the
character is printable, it passes through unchanged.If
it is a space and either of the next two bytes is NULL it will also
pass through unchanged.In any other case, it is copied
across as a hexadecimal string like
x09
.The completed output buffer is then
either sent to the system logger if we are in background mode
(daemon) or to the stderr file if we are in foreground
mode.
static void usage(void)
Simply print a big list of the invocation parameters to
the default gpsd port (2947, allocated by
IANA).
static int passivesock_af(char *service, char *tcp_or_udp, int qlen)
Initialise an Internet socket address structure and
preload the family and address fields to accept Internet Protocol
and any address.Test to see if the incoming service and
protocol exist in /etc/services. If they do,
store the port number in the structure (massaging byte order as
needed); if they don't, exit with a -1.Test to see if
the protocol is listed in /etc/services,
exiting with -1 if it is not.Test if the protocol is
udp or not, setting the type accordingly.Try to open a
socket with the accumulated settings, exiting with -1 if it
fails.Try to set the socket options correctly, again
exiting with -1 if it fails.Try to bind to the open
socket; if it fails exit with -1 as earlier, but give a special
warning if the error indicates that gpsd
may already be active on the socket.If we are using a
stream type socket and we are unable to listen to the port we exit
with -1.The last possibility is a successful set of
operations which is signalled by returning the socket fd
number.
static int passivesocks(char *service, char *tcp_or_udp, int qlen, int socks[])
Open a passive socket for each supported address
family; presently the supported families are IPV4 and IPv6. This
socket will be used to listen for client command
connections. Sockets are left in the final array argument, and the
number successfully opened is returned.>
static int filesock(char *filename)
Try and open a socket for Local (UNIX) communications
in streaming mode. If the open fails, return with a
-1.If it opens, copy the incoming filename into the
socket control structure, bind to the socket and try to listen on
it.Signal a failure by returning -1 and success by
returning the socket fd number.
static void adjust_max_fd(int fd, bool on)
If the incoming boolean flag is active, check if the fd
number passed is greater than the highest seen so far. If so, save
it as the new highest value.If the boolean is passive
we can take some further action, depending if we are interested in
limiting the maximum number of devices and client fds (set by
compile time options).If we are not limiting ourselves,
then we check for the case when we are actually at the highest fd
seen so far. In that case, scan through all fds available to the
system and store the highest active fd number in our allocation set
as the new highest value.
static struct subscriber_t* allocate_client(void)
Scan through all the client file descriptors, looking
for one which does not have a device allocated to it.On
a match, exit early, returning this fd.If none are
available, return a NULL.
static void detach_client(struct subscriber_t *sub)
Close the given fd and remove it from our allocation set.Make a call to adjust_max_fd() to housekeep the highest fd marker if needed.Set important fields in the client's datablock to safe values for the next re-use, then return.
static ssize_t throttled_write(struct subscriber_t *sub, char *buf, ssize_t len)
Check if we have a high enough debug level active to warrant printing out the information we are about to send to the client.Make the actual write() call and if that was successful, return the counter value from that operation.If we have suffered some kind of failure, try to analyse it.On a short write, detach the client and return a 0.Trap EAGAIN or EINTR and return a 0.Trap EBADF or a EWOULDBLOCK where the client has not read data for more than a reasonable amount of time and generate a suitable report.For all other errors, generate a general error. In these last several cases, call detach_cient().Finally, return the status (-1 in this case).
static void notify_watchers(struct gps_device_t *device, const char *sentence, ...)
For every possible subscriber, check if the subscriber is in watcher mode and is interested in the gps device indicated in the calling parameter gps_device_t.If so, send the data via a call to throttled_write().
static struct gps_device_t *find_device(const char *device_name)
For every possible channel, check if the channel is allocated and if the device on the channel is the one passed to us.If it is so, exit early and return the channel number.If there is no match, return a NULL.
static void deactivate_device(struct gps_device_t *device)
Deactivate device, but leave it in the device pool; do
not free it. This means it will be available to be watched on
subsequent client opens.
bool open_device(struct gps_device_t *devp)
Try to activate the device via a call to
gpsd_activate().If this fails
return false
.If it succeeds, add the
fd to our list of active fds, housekeep the highest fd number
and return true
.
static bool add_device(const char *device_name)
Add a device to the pool of those available. If in
nowait mode, open it immediately; otherwise initialize it and make
it available for future watches, but don't open it yet.
static bool awaken(struct subscriber_t *user, struct gps_device_t *device)
If the device is not initialized, attempt to open the
specified device on behalf of the specified user. If you succeed
and the device has an active fd, you're done. If it does not, make a call to
gpsd_activate().If this fails,
return false
, if not, add the fd to our list of
active fds and housekeep the highest fd.Check if the
user is in watcher mode but not tied to a specific
device.
static char *snarfline(char *p, char **out)
Copy the input line into a new buffer stopping at the
first non-printable or whitespace character.
static bool privileged_user(struct gps_device_t *device)
Scan all subscribers and count all who are connected to
the device. If only the one user is connected, return
true
, otherwise return
false
.
static void handle_request(struct subscriber_t* sub, char *buf, const char **after, char *reply, size_t replylen)
Perform a single GPSD JSON command. Accept the command
response into a reply buffer, and update the after pointer to point
just after the parsed JSON object.
static int handle_gpsd_request(struct subscriber_t *sub, const char *buf)
Parse multiple GPSD JSON commands out of a buffer and
perform each. Ship all responses back to the user via
throttled_write().
static void handle_control(int sfd, char *buf)
This code is similar in function to
handle_gpsd_request() in that it parses user
input. It expects the commands to be one per line and despatches
them according to the leading character, which is limited to one of
'-', '+' or '!'.In the first case, the body of the
command is assumed to be a device to remove from the search list. If
found, it is removed, any clients are advised and OK
is written to the calling socket fd. If the device is not found
ERROR
is written to the calling socket
fd.In the second case, the body of the command is
assumed to be a device to be used by the daemon. If the device is
already known, or does not respond to
open_device(), ERROR
is written
to the calling socket fd, otherwise OK
is
written.In the third case, the command is assumed to be
a device-specific control string in the form
!device_name=control_string
. If the string is
ill-formed or the device is not found ERROR
is
written to the calling socket fd. If all is well, the control string
is written to the device and OK
is written to the
calling socket fd.
int main(int argc, char *argv[])
If the 1PPS function is compiled in, initialise the
local mutex structure for use by the program.A
while() loop reads in any command line
arguments which are options and handles the options. Most set an
internal variable to control action when running, either to a fixed
value or to the associated option's parameter.Carry out
a series of calls to routines to set things up ready for the main
task (e.g. opening a control socket if one is needed). We also take
care of tasks such as daemonizing when appropriate. The last piece
of preparation is to set the permissions of the default devices
correctly if we are daemonizing and are presently running as
root.Switch to the compiled in user name (typically
nobody
) and the group used by the tty
devices.Now we clear important data for all the records
in the subscriber list.Use
setjmp() to prepare things for when the daemon
terminates.Clear the semaphore variable which will
contain the signal number if one arrives and set some important
signals so they are trapped by the stub handler in
onsig().Add the command and RTCM
sockets (if active) to the list of active fds, housekeeping the
highest fd number and pre-clear the list of control
fds.Process the remaining parameter on the command line
which should be the device name and try to open the specified
device.Enter the main execution loop, a
while() loop which terminates if a signal sets
the semaphore variable. What follows will repeat over and over until
an external termination happens.First we make a working
copy of the active fds and then we make a time-limited (1 second
time limit) call to select() using the working
copy of the fds. This means that when the
select() returns, we will either have returned
on timeout or because some fd became ready to
read.First we check if any new clients have come active
and (if we have resources) allocate a subscriber slot to it, doing
housekeeping such as adding it to the main list of active fds and
removing it from the local copy of the list. If RTCM support is
compiled in, the last operation is repeated for any new RTCM
client. The operation is then repeated for any new control socket
clients.If we are expecting DGPS reports, make a call
to netgnss_poll() and if there are no ready
reports, clear the fd from the main and local active fd
lists.Check if any of the active control sockets has
sent one or more commands.For every one which has sent
commands, make calls to handle_control() to
process them and remove the associated fd from the main and control
lists of active fds.Poll every active gps device and
send RTCM data to it (if needed), followed by reading its output (if
any). If the device returns an error, disable the device. If it has
gone off-line, disable the device.If we get here, we
have something to handle, so we take care of a device which we know
about, but do not have a subtype for.We send the
available data to all subscribers who are connected to this
device. If the data is RTCM information, pass it to all GPS devices
that can accept the data.Handle any subscribers who are
in watcher mode building up an appropriate set of requests, depending
on the available data and passing the requests to
handle_gpsd_request().If we care
about DBUS, send the fix to the DBUS.Note
that this small section of code is presently disabled pending
development of the DGNSS function. If DGNSS is available
and we have a fix, we poll a DGNSS report via
dgnss_autoconnect().Loop round all
clients and process active ones. We check for input from them and if
the read fails, the client is released with
detach_client(). If it succeeds, any data is
handled via handle_rtc_request() or
handle_gpsd_request().If the
transaction fails, the client is released with
detach_client().If the client has
timed out with no device assigned, it is released with
detach_client().If the client has
a device, but has timed out on no response (when not in raw or
watcher modes) it is released with
detach_client().If we are not
running in nowait
mode, we are supposed to go idle
after a timeout when there are no clients.If a device
(with a known type) has no active clients, then we can actually make
it idle via gpsd_deactivate().If
we reach here, we are out of the endless while loop. We check if the
signal was SIGHUP and restart the program if it
was. If it is any other signal, we deallocate all channels and wrap
up any devices. Finally we check for the existence of a control
socket or a pid file and delete them.