<filename>gpsd.c</filename> Functions:-This is the main body of the daemon. static void onsig(int sig) Reset a previously modified signal to default handling. 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 unneccessary 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(char *service, char *protocol, 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 possibilty is a successful set of operations which is signalled by returning the socket fd number. 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 bool have_fix(struct subscriber_t *whoami) If the call is made from a client with no associated device, return a false indication.If the status and mode don't agree about having a fix, return a false indication.If both status and mode agree that we have a fix, return a true indication.Finally, the fall through is that there really is no fix, so return a false indication. 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.Trap EBADF and generate a suitable report.Trap EWOULDBLOCK and if the client has not read data for more than a reasonable amount of time, generate a suitable report.For all other errors, generate a general error and call detach_cient().Finally, return the status (-1 in this case). static void notify_watchers(struct gps_device_t *device, 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 void raw_hook(struct gps_data_t *ud, char *sentence, size_t len, int level) For every possible subscriber, check if the incoming level matches the subscriber's raw setting, that the subscriber has a device associated with it and that the gps device indicated in the calling parameter gps_device_t is the one the subscriber is interested in.If all conditions are satisfied, send the data via throttled_write(). static struct gps_device_t *find_device(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 struct gps_device_t *open_device(char *device_name) Check if the incoming device name is a DGPS URL via a call to dgnss_url().If it is, try to open it via a call to dgnss_open().If this worked, add the fd to our list of active fds and housekeep the highest fd number via a call to adjust_max_fd(). Exit by returning the address of the channels array (see the comment in the code!!).For a non-DGPS (normal) device, scan all channels looking for an unallocated one. Exit early on a successful search. If none is found, return a NULL.If one is found, make a call to gpsd_init() and store address of the local raw_hook() code in the selected channel's gpsdata.raw_hook.Try to activate the device via a call to gpsd_activate().If this fails return -1.If it succeeds, add the fd to our list of active fds, housekeep the highest fd number and return the channel number allocated. static bool allocation_policy(struct gps_device_t *channel, struct subscriber_t *user, double most_recent) Test that the caller has an active device and that the timestamp of the proposed channel's last sentence is not older than the caller's most recent data.If the tests fail, return false.Check what type of device is required by the caller.If the caller has no preference, just return true.If the requirement is RTCM and the channel is giving RTCM data, return true.If the user needs gps and the channel is giving good, non-RTCM, packets return true.If there is no match, default to returning false. static bool assign_channel(struct subscriber_t *user) Test if the user has a device assigned.If none is assigned, scan all channels and test each one to see if it is allocated.If it is allocated, test with allocation_policy() to see if we can use it.If we can, link the channel to the user and stash the value of its last sentence.At the end of the loop, we either have a NULL on no allocation or we have allocated once or more, retaining a suitable device which has the most recent data.If we failed to allocate, return false.Test if the channel has an active fd.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.If these conditions are sastified, generate a string and send it to the caller to advise them of the assigned device's name.Finally, if the caller is in watcher mode and did not have a device previously assigned, generate and send a string advising the caller of the time and the assigned device name/subtype from a call to gpsd_id(). static int handle_rtcm_request(struct subscriber_t* sub UNUSED, char *buf UNUSED, int buflen UNUSED) Does nothing except return 0 at the moment. 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 subscriber_t *who) Scan all susbcribers and count all who are connected to the caller's device. If only the one user is connected, return true, otherwise return false. static int handle_gpsd_request(struct subscriber_t* sub, char *buf, int buflen) Start by filling the return buffer with GPSD in readiness.Scan through the input buffer until the end of the string is reached. Act on the first character (it is assumed to be a command character).The commands are checked in a switch statement and either simply generate some output to the buffer or generate output and also cause the input buffer pointer to be advanced over one or more characters as needed (individual command handling will not be analysed here).This technique allows several concatenated parameters to be extracted and handled in any order.After some checks on the output buffer length (which generate an error return on overflow), the output buffer is passed to throttled_write() and its return value is used used as the return value for the function. 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 contol 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.Set some important signals so they are trapped by a local handler. This handler just resets the calling signal to its default behaviour and calls longjmp(). This will bring things back to the setjmp() just mentioned and initiate a clean up and exit.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, an endless for() loop. What follows will repeat over and over until an external termination happens or the select() function fails due to some unexpected reason.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 dgnss_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 approriate 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 avalable 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_cleint(). 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 when there are no clients. However, this is subject to a restriction that a device is not allowed to go idle like this until we have actually discovered what it is. This means we stay active until the packet sniffer has returned the packet type.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 loop, so finally we check for the existance of a control socket or a pid file and delete them. Notes based on $Id: gpsd.c 4302 2007-03-14 02:27:45Z ckuethe $