explan_gpsd.c.xml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <sect1 id="gpsd.c"><title><filename>gpsd.c</filename></title>
  2. <informaltable frame='all' pgwide='1'>
  3. <tgroup cols='2'>
  4. <colspec colname='c1'></colspec>
  5. <colspec colname='c2'></colspec>
  6. <spanspec spanname='s1' namest='c1' nameend='c2'></spanspec>
  7. <!-- Not documented: json_devicelist_dump(), rstrip() -->
  8. <thead>
  9. <row>
  10. <entry>Functions:-</entry><entry>This is the main body of the daemon.</entry>
  11. </row>
  12. </thead>
  13. <tfoot>
  14. <row>
  15. <entry spanname='s1' align='left'>Notes based on code as of Mon Apr 5 21:38:06 2010 -0400.</entry>
  16. </row>
  17. </tfoot>
  18. <tbody>
  19. <row>
  20. <entry><function>static void onsig(int sig)</function></entry>
  21. <entry><para>This is a simple catchall routine to trap wanted
  22. signal. Simply store the signal number in a variable to advise the
  23. main loop which signal to handle.</para></entry>
  24. </row>
  25. <row>
  26. <entry><function>static int daemonize(void)</function></entry>
  27. <entry><para>Try to <function>fork()</function> a child process. The
  28. parent will get a return value of either -1 (on a failure to
  29. <function>fork()</function>) or non-zero (the child's PID). The
  30. parent routine will use this information to either return -1 or exit
  31. with an exit code of 0 (i.e. the parent terminates, leaving the
  32. child running).</para><para>The child instance gets a return value
  33. of 0 (on an unsuccessful <function>fork()</function> there is no
  34. child), so this value is used as the trigger to do the following
  35. useful stuff.</para><para>First, the child tries to create a new
  36. session, returning -1 if unable to do so. If succesful, it will have
  37. inherited the exiting parent's session.</para><para>Next switch to
  38. the root directory and try to open <quote>/dev/null</quote>. If that
  39. succeeds, force stdin, stdout and stderr to duplicate the fd of
  40. <quote>/dev/null</quote>. If the fd being used for the operation is
  41. >2, release it.</para><para>Finally, set the flag which indicates
  42. the process is in the background and return a value of
  43. 0.</para></entry>
  44. </row>
  45. <row>
  46. <entry><function>static void usage(void)</function></entry>
  47. <entry><para>Simply print a big list of the invocation parameters to
  48. the default <application>gpsd</application> port (2947, allocated by
  49. IANA).</para></entry>
  50. </row>
  51. <row>
  52. <entry><function>static int passivesock_af(char *service, char *tcp_or_udp, int qlen)</function></entry>
  53. <entry><para>Initialise an Internet socket address structure and
  54. preload the family and address fields to accept Internet Protocol
  55. and any address.</para><para>Test to see if the incoming service and
  56. protocol exist in <function>/etc/services</function>. If they do,
  57. store the port number in the structure (massaging byte order as
  58. needed); if they don't, exit with a -1.</para><para>Test to see if
  59. the protocol is listed in <function>/etc/services</function>,
  60. exiting with -1 if it is not.</para><para>Test if the protocol is
  61. udp or not, setting the type accordingly.</para><para>Try to open a
  62. socket with the accumulated settings, exiting with -1 if it
  63. fails.</para><para>Try to set the socket options correctly, again
  64. exiting with -1 if it fails.</para><para>Try to bind to the open
  65. socket; if it fails exit with -1 as earlier, but give a special
  66. warning if the error indicates that <application>gpsd</application>
  67. may already be active on the socket.</para><para>If we are using a
  68. stream type socket and we are unable to listen to the port we exit
  69. with -1.</para><para>The last possibility is a successful set of
  70. operations which is signalled by returning the socket fd
  71. number.</para></entry>
  72. </row>
  73. <row>
  74. <entry><function>static int passivesocks(char *service, char *tcp_or_udp, int qlen, int socks[])</function></entry>
  75. <entry><para>Open a passive socket for each supported address
  76. family; presently the supported families are IPV4 and IPv6. This
  77. socket will be used to listen for client command
  78. connections. Sockets are left in the final array argument, and the
  79. number successfully opened is returned.</para>></entry>
  80. </row>
  81. <row>
  82. <entry><function>static int filesock(char *filename)</function></entry>
  83. <entry><para>Try and open a socket for Local (UNIX) communications
  84. in streaming mode. If the open fails, return with a
  85. -1.</para><para>If it opens, copy the incoming filename into the
  86. socket control structure, bind to the socket and try to listen on
  87. it.</para><para>Signal a failure by returning -1 and success by
  88. returning the socket fd number.</para></entry>
  89. </row>
  90. <row>
  91. <entry><function>static void adjust_max_fd(int fd, bool on)</function></entry>
  92. <entry><para>If the incoming boolean flag is active, check if the fd
  93. number passed is greater than the highest seen so far. If so, save
  94. it as the new highest value.</para><para>If the boolean is passive
  95. we can take some further action, depending if we are interested in
  96. limiting the maximum number of devices and client fds (set by
  97. compile time options).</para><para>If we are not limiting ourselves,
  98. then we check for the case when we are actually at the highest fd
  99. seen so far. In that case, scan through all fds available to the
  100. system and store the highest active fd number in our allocation set
  101. as the new highest value.</para></entry>
  102. </row>
  103. <row>
  104. <entry><function>static struct subscriber_t* allocate_client(void)</function></entry>
  105. <entry><para>Scan through all the client file descriptors, looking
  106. for one which does not have a device allocated to it.</para><para>On
  107. a match, exit early, returning this fd.</para><para>If none are
  108. available, return a NULL.</para></entry>
  109. </row>
  110. <row>
  111. <entry><function>static void detach_client(struct subscriber_t *sub)</function></entry>
  112. <entry><para>Close the given fd and remove it from our allocation set.</para><para>Make a call to <function>adjust_max_fd()</function> to housekeep the highest fd marker if needed.</para><para>Set important fields in the client's datablock to safe values for the next re-use, then return.</para></entry>
  113. </row>
  114. <row>
  115. <entry><function>static ssize_t throttled_write(struct subscriber_t *sub, char *buf, ssize_t len)</function></entry>
  116. <entry><para>Check if we have a high enough debug level active to warrant printing out the information we are about to send to the client.</para><para>Make the actual <function>write()</function> call and if that was successful, return the counter value from that operation.</para><para>If we have suffered some kind of failure, try to analyse it.</para><para>On a short write, detach the client and return a 0.</para><para>Trap <function>EAGAIN</function> or <function>EINTR</function> and return a 0.</para><para>Trap <function>EBADF</function> or a <function>EWOULDBLOCK</function> where the client has not read data for more than a reasonable amount of time and generate a suitable report.</para><para>For all other errors, generate a general error. In these last several cases, call <function>detach_cient()</function>.</para><para>Finally, return the status (-1 in this case).</para></entry>
  117. </row>
  118. <row>
  119. <entry><function>static void notify_watchers(struct gps_device_t *device, const char *sentence, ...)</function></entry>
  120. <entry><para>For every possible subscriber, check if the subscriber is in watcher mode and is interested in the gps device indicated in the calling parameter <function>gps_device_t</function>.</para><para>If so, send the data via a call to <function>throttled_write()</function>.</para></entry>
  121. </row>
  122. <row>
  123. <entry><function>static struct gps_device_t *find_device(const char *device_name)</function></entry>
  124. <entry><para>For every possible channel, check if the channel is allocated and if the device on the channel is the one passed to us.</para><para>If it is so, exit early and return the channel number.</para><para>If there is no match, return a NULL.</para></entry>
  125. </row>
  126. <row>
  127. <entry><function>static void deactivate_device(struct gps_device_t *device)</function></entry>
  128. <entry><para>Deactivate device, but leave it in the device pool; do
  129. not free it. This means it will be available to be watched on
  130. subsequent client opens.</para></entry>
  131. </row>
  132. <row>
  133. <entry><function>bool open_device(struct gps_device_t *devp)</function></entry>
  134. <entry><para>Try to activate the device via a call to
  135. <function>gpsd_activate()</function>.</para><para>If this fails
  136. return <quote>false</quote>.</para><para>If it succeeds, add the
  137. fd to our list of active fds, housekeep the highest fd number
  138. and return <quote>true</quote>.</para></entry>
  139. </row>
  140. <row>
  141. <entry><function>static bool add_device(const char *device_name)</function></entry>
  142. <entry><para>Add a device to the pool of those available. If in
  143. nowait mode, open it immediately; otherwise initialize it and make
  144. it available for future watches, but don't open it yet.</para></entry>
  145. </row>
  146. <row>
  147. <entry><function>static bool awaken(struct subscriber_t *user, struct gps_device_t *device)</function></entry>
  148. <entry><para>If the device is not initialized, attempt to open the
  149. specified device on behalf of the specified user. If you succeed
  150. and the device has an active fd, you're done. If it does not, make a call to
  151. <function>gpsd_activate()</function>.</para><para>If this fails,
  152. return <quote>false</quote>, if not, add the fd to our list of
  153. active fds and housekeep the highest fd.</para><para>Check if the
  154. user is in watcher mode but not tied to a specific
  155. device.</para></entry>
  156. </row>
  157. <row>
  158. <entry><function>static char *snarfline(char *p, char **out)</function></entry>
  159. <entry><para>Copy the input line into a new buffer stopping at the
  160. first non-printable or whitespace character.</para></entry>
  161. </row>
  162. <row>
  163. <entry><function>static bool privileged_user(struct gps_device_t *device)</function></entry>
  164. <entry><para>Scan all subscribers and count all who are connected to
  165. the device. If only the one user is connected, return
  166. <quote>true</quote>, otherwise return
  167. <quote>false</quote>.</para></entry>
  168. </row>
  169. <row>
  170. <entry><function>static void handle_request(struct subscriber_t* sub, char *buf, const char **after, char *reply, size_t replylen)</function></entry>
  171. <entry><para>Perform a single GPSD JSON command. Accept the command
  172. response into a reply buffer, and update the after pointer to point
  173. just after the parsed JSON object.</para></entry>
  174. </row>
  175. <row>
  176. <entry><function>static int handle_gpsd_request(struct subscriber_t *sub, const char *buf)
  177. </function></entry>
  178. <entry><para>Parse multiple GPSD JSON commands out of a buffer and
  179. perform each. Ship all responses back to the user via
  180. <function>throttled_write()</function>.</para></entry>
  181. </row>
  182. <row>
  183. <entry><function>static void handle_control(int sfd, char *buf)</function></entry>
  184. <entry><para>This code is similar in function to
  185. <function>handle_gpsd_request()</function> in that it parses user
  186. input. It expects the commands to be one per line and despatches
  187. them according to the leading character, which is limited to one of
  188. '-', '+' or '!'.</para><para>In the first case, the body of the
  189. command is assumed to be a device to remove from the search list. If
  190. found, it is removed, any clients are advised and <quote>OK</quote>
  191. is written to the calling socket fd. If the device is not found
  192. <quote>ERROR</quote> is written to the calling socket
  193. fd.</para><para>In the second case, the body of the command is
  194. assumed to be a device to be used by the daemon. If the device is
  195. already known, or does not respond to
  196. <function>open_device()</function>, <quote>ERROR</quote> is written
  197. to the calling socket fd, otherwise <quote>OK</quote> is
  198. written.</para><para>In the third case, the command is assumed to be
  199. a device-specific control string in the form
  200. <quote>!device_name=control_string</quote>. If the string is
  201. ill-formed or the device is not found <quote>ERROR</quote> is
  202. written to the calling socket fd. If all is well, the control string
  203. is written to the device and <quote>OK</quote> is written to the
  204. calling socket fd.</para></entry>
  205. </row>
  206. <row>
  207. <entry><function>int main(int argc, char *argv[])</function></entry>
  208. <entry><para>If the 1PPS function is compiled in, initialise the
  209. local mutex structure for use by the program.</para><para>A
  210. <function>while()</function> loop reads in any command line
  211. arguments which are options and handles the options. Most set an
  212. internal variable to control action when running, either to a fixed
  213. value or to the associated option's parameter.</para><para>Carry out
  214. a series of calls to routines to set things up ready for the main
  215. task (e.g. opening a control socket if one is needed). We also take
  216. care of tasks such as daemonizing when appropriate. The last piece
  217. of preparation is to set the permissions of the default devices
  218. correctly if we are daemonizing and are presently running as
  219. root.</para><para>Switch to the compiled in user name (typically
  220. <quote>nobody</quote>) and the group used by the tty
  221. devices.</para><para>Now we clear important data for all the records
  222. in the subscriber list.</para><para>Use
  223. <function>setjmp()</function> to prepare things for when the daemon
  224. terminates.</para><para>Clear the semaphore variable which will
  225. contain the signal number if one arrives and set some important
  226. signals so they are trapped by the stub handler in
  227. <function>onsig()</function>.</para><para>Add the command and RTCM
  228. sockets (if active) to the list of active fds, housekeeping the
  229. highest fd number and pre-clear the list of control
  230. fds.</para><para>Process the remaining parameter on the command line
  231. which should be the device name and try to open the specified
  232. device.</para><para>Enter the main execution loop, a
  233. <function>while()</function> loop which terminates if a signal sets
  234. the semaphore variable. What follows will repeat over and over until
  235. an external termination happens.</para><para>First we make a working
  236. copy of the active fds and then we make a time-limited (1 second
  237. time limit) call to <function>select()</function> using the working
  238. copy of the fds. This means that when the
  239. <function>select()</function> returns, we will either have returned
  240. on timeout or because some fd became ready to
  241. read.</para><para>First we check if any new clients have come active
  242. and (if we have resources) allocate a subscriber slot to it, doing
  243. housekeeping such as adding it to the main list of active fds and
  244. removing it from the local copy of the list. If RTCM support is
  245. compiled in, the last operation is repeated for any new RTCM
  246. client. The operation is then repeated for any new control socket
  247. clients.</para><para>If we are expecting DGPS reports, make a call
  248. to <function>netgnss_poll()</function> and if there are no ready
  249. reports, clear the fd from the main and local active fd
  250. lists.</para><para>Check if any of the active control sockets has
  251. sent one or more commands.</para><para>For every one which has sent
  252. commands, make calls to <function>handle_control()</function> to
  253. process them and remove the associated fd from the main and control
  254. lists of active fds.</para><para>Poll every active gps device and
  255. send RTCM data to it (if needed), followed by reading its output (if
  256. any). If the device returns an error, disable the device. If it has
  257. gone off-line, disable the device.</para><para>If we get here, we
  258. have something to handle, so we take care of a device which we know
  259. about, but do not have a subtype for.</para><para>We send the
  260. available data to all subscribers who are connected to this
  261. device. If the data is RTCM information, pass it to all GPS devices
  262. that can accept the data.</para><para>Handle any subscribers who are
  263. in watcher mode building up an appropriate set of requests, depending
  264. on the available data and passing the requests to
  265. <function>handle_gpsd_request()</function>.</para><para>If we care
  266. about DBUS, send the fix to the DBUS.</para><para><emphasis>Note
  267. that this small section of code is presently disabled pending
  268. development of the DGNSS function.</emphasis> If DGNSS is available
  269. and we have a fix, we poll a DGNSS report via
  270. <function>dgnss_autoconnect()</function>.</para><para>Loop round all
  271. clients and process active ones. We check for input from them and if
  272. the read fails, the client is released with
  273. <function>detach_client()</function>. If it succeeds, any data is
  274. handled via <function>handle_rtc_request()</function> or
  275. <function>handle_gpsd_request()</function>.</para><para>If the
  276. transaction fails, the client is released with
  277. <function>detach_client()</function>.</para><para>If the client has
  278. timed out with no device assigned, it is released with
  279. <function>detach_client()</function>.</para><para>If the client has
  280. a device, but has timed out on no response (when not in raw or
  281. watcher modes) it is released with
  282. <function>detach_client()</function>.</para><para>If we are not
  283. running in <quote>nowait</quote> mode, we are supposed to go idle
  284. after a timeout when there are no clients.</para><para>If a device
  285. (with a known type) has no active clients, then we can actually make
  286. it idle via <function>gpsd_deactivate()</function>.</para><para>If
  287. we reach here, we are out of the endless while loop. We check if the
  288. signal was <function>SIGHUP</function> and restart the program if it
  289. was. If it is any other signal, we deallocate all channels and wrap
  290. up any devices. Finally we check for the existence of a control
  291. socket or a pid file and delete them.</para></entry>
  292. </row>
  293. </tbody>
  294. </tgroup>
  295. </informaltable>
  296. </sect1>