# `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.

`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.