pup_event_ipc-README.htm 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
  2. <html><head>
  3. <meta http-equiv="content-type" content="text/html; charset=windows-1252">
  4. <title>pup_event_ipc</title>
  5. <meta content="Barry Kauler" name="author">
  6. <meta content="IPC mechanism for Puppy Linux" name="description">
  7. </head>
  8. <body>
  9. <table align="center" border="0" cellpadding="2" cellspacing="2" width="600">
  10. <tbody>
  11. <tr>
  12. <td valign="top">
  13. <h1>Puppy's InterProcess Communication</h1>
  14. <font color="#990000">Page updated June 29, 2013</font>&nbsp; <br>
  15. <b>pup_event</b> is the generic name that I have given to the underlying
  16. ...how to describe it... code that does everything "under the bonnet",
  17. in the background, that makes your pup run nice. <br>
  18. <br>
  19. This page describes an implementation of <b>InterProcess Communication</b>
  20. in Puppy, that falls under the pup_event umbrella. So, this can be
  21. known as the "pup_event IPC" technique, to give it a name when
  22. referenced or discussed elsewhere.<br>
  23. <br>
  24. The method chosen is server-less, that is, works peer-to-peer.
  25. Applications send and receive messages between themselves directly,
  26. without an intermediate server or daemon. However, some applications can
  27. provide information that other applications request, and I use the word
  28. <b>server</b> in reference to them. I also refer to applications as <b>clients</b> of the IPC mechanism.<br>
  29. <br>
  30. Puppy's IPC mechanism offers various services to client applications:<br>
  31. <ol>
  32. <li>IPC by synchronous message swapping.</li>
  33. <li>IPC by mailbox (asynchronous) message passing.</li>
  34. <li>Block hotplug events from the kernel.</li>
  35. <li>...more coming...<br>
  36. </li>
  37. </ol>
  38. There is a CLI
  39. (commandline) utility supporting IPC in Puppy, named 'pup_event_ipc', that can be used in
  40. shell scripts. Documentation is provided below to show how binary (compiled)
  41. applications can use IPC by means of library calls.<br>
  42. <br>
  43. <hr size="2" width="100%">
  44. <h2>pup_event_ipc</h2>
  45. This is a CLI (commandline) application named 'pup_event_ipc'. It is
  46. designed to be used in shell scripts, acting as an intermediary to the pup_event IPC mechanism,
  47. enabling scripts to pass messages between each other. It is located at /sbin/pup_event_ipc.<br>
  48. <br>
  49. Usage of pup_event_ipc is shown by examples in the rest of this
  50. document. You can also run it like this to see some usage information:<br>
  51. <pre># pup_event_ipc --help</pre>
  52. The source code is at /usr/local/pup_event/pup_event_ipc.bac in the
  53. latest Puppy, also reproduced below. It is also available online in the
  54. Woof Fossil VCS repository (also this document) after anonymous login,
  55. at: <a href="http://bkhome.org/fossil/woof2.cgi/index">http://bkhome.org/fossil/woof2.cgi/index</a> <br>
  56. <br>
  57. The code can be examined to see how any other application can access the IPC mechanism.<br>
  58. <br>
  59. The commandline format of pup_event_ipc is this:<br>
  60. <pre># pup_event_ipc "request:client[:message]" [-t &lt;n&gt;]</pre>
  61. <blockquote><b>-t</b> &lt;n&gt;: Specifies timeout in milliseconds. Timeout will return with value=1.<br>
  62. <b>request</b>: Name of another application to send the message to<b>*</b>. Or,<br>
  63. <b>request</b>: Keyword to request information from the server. <br>
  64. &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp;
  65. &nbsp; Supported keywords:&nbsp;&nbsp; block mailbox waitmail<br>
  66. <b>client</b>:&nbsp; Any single-word to identify client (usually sender) application<b>*</b>.<br>
  67. <b>message</b>: Any text string (that other side understands), up to 4000 bytes.<br>
  68. </blockquote>
  69. <b><big>*</big></b>The names for clients need to be unique. Choose
  70. anything you want, perhaps prefixing all names with your initials, for
  71. example John Smith could prefix with "js_".<br>
  72. <br>
  73. <hr size="2" width="100%">
  74. <h2>Message formats</h2>
  75. This section describes message formats. This is already introduced above, as:<br>
  76. <pre>"request:client[:message]"</pre>
  77. The various permutations of this are described below.<br>
  78. <h3>Synchronous message-swapping</h3>
  79. Demonstrating synchronous message-swap between two different applications.<br>
  80. In one terminal window I type this:<br>
  81. <pre># pup_event_ipc "js_app1:js_app2:some stuff from app2"</pre>
  82. In another terminal window I type this:<br>
  83. <pre># pup_event_ipc "js_app2:js_app1:data from app1"</pre>
  84. After launching both of them:<br>
  85. First window:<br>
  86. <pre># pup_event_ipc "js_app1:js_app2:some stuff from app2"<br>data from app1</pre>
  87. Second window:<br>
  88. <pre># pup_event_ipc "js_app2:js_app1:data from app1"<br>some stuff from app2</pre>
  89. This is a very simple mechanism that can be use by two applications to
  90. perform a synchronization, or to pass messages between each other.<br>
  91. <br>
  92. It is a simple swap, and both sides must post to the other. If only one
  93. side posts, the message will remain in the "mailbox" until the other side
  94. posts.<br>
  95. The utility 'pup_event_ipc' blocks, that is, waits, until the swap is completed, before exiting.<br>
  96. <br>
  97. Note, the CLI utility 'pup_event_ipc' does have a timeout option (-t n),
  98. so either side can terminate before the swap has completed.<br>
  99. <h3>Mailbox (asynchronous) message passing</h3>
  100. When a client posts a message to another client, it goes first into the
  101. IPC "mailbox", later it gets delivered to the other client.<br>
  102. <br>
  103. With asynchronous message passing, you don't send the message to the
  104. other client, you send it to the mailbox only, then the sending-client
  105. is free to do other stuff.<br>
  106. The other client, to which you would like your message to go, can at any
  107. time query "is there a message waiting for me?" and if so,
  108. fetch it.<br>
  109. <br>
  110. The format of messages is:<br>
  111. <pre>"mailbox:client[:message]"</pre>
  112. If a client posts a message with this format, with the third-field
  113. message-string, it gets posted, and the server acknowledges. For
  114. example:<br>
  115. <pre># pup_event_ipc "mailbox:js_app1:message string posted to app1"<br>Mailbox acknowledge</pre>
  116. A client, any client, can ask if there is mail waiting for "js_app1".
  117. This is done by having no third field. For example:<br>
  118. <pre># pup_event_ipc "mailbox:js_app1"<br>message string posted to app1</pre>
  119. However, if this is done again:<br>
  120. <pre># pup_event_ipc "mailbox:js_app1"<br>Mailbox empty</pre>
  121. That last example returns immediately, but if you want to wait until a message arrives at the mailbox, use this variant:<br>
  122. <pre># pup_event_ipc "waitmail:js_app1"</pre>
  123. The utility will not exit until there is a message delivered.<br>
  124. <br>
  125. But of course, the pup_event_ipc utility does support a timeout:<br>
  126. <pre># pup_event_ipc "waitmail:js_app1:" -t 4000</pre>
  127. Synchronous message-swapping and asynchronous mailbox message-passing,
  128. are the two peer-to-peer messaging and synchronization mechanisms.<br>
  129. There is another group, that is a bit more like a server-client
  130. arrangement, in which the 'request' field of the message requests
  131. something from another application. That other application will be
  132. serving a certain kind of information. Although I don't really want to
  133. consider these as servers, as they are just equal clients in the IPC
  134. mailbox system, for the sake of putting a name on them, I am referring
  135. to this group of applications as "server applications".<br>
  136. <br>
  137. Server applications are documented below. Currently only <b>block</b> server is offered.<br>
  138. <h3>Block hotplug events</h3>
  139. A client application may query the "kernel-event server application" for
  140. various information, such as detecting plugin/removal of USB or
  141. card drives.<br>
  142. <br>
  143. For now, only <b>block</b> is supported. For example:<br>
  144. <pre># pup_event_ipc "block:js_app1"<br>add:sdc add:sdc1</pre>
  145. The utility waits until a drive hotplug event occurs, such as a USB Flash stick plugged in, then returns with the information.<br>
  146. <br>
  147. The 'pup-event_ipc' utility is only designed as a one-shot query, so exits after detecting the events.<br>
  148. However, hotplug events are not lost. The first "block" request has
  149. registered "js_app1" (from above example) and hotplug events will
  150. accumulate.<br>
  151. <br>
  152. So, by having a loop, to receive and process the hotplug events, no events will be missed. Ex shell script:<br>
  153. <pre>while true; do<br>&nbsp;EVENTS="`pup_event_ipc "block:js_app1"`"<br>&nbsp;...<i>process</i>...<br>done</pre>
  154. What needs to be understood about the above, is how simple the
  155. implementation is. The client application just creates a file (if it
  156. does not exist) named "/tmp/pup_event_ipc/block_js_app1" and waits for
  157. the server-application to put something into it. This inotify IPC
  158. technique is described in the next section.<br>
  159. <br>
  160. <hr size="2" width="100%">
  161. <h2>The inotify backend</h2>
  162. The IPC technique used is more than a little unusual. The established
  163. techniques such as D-Bus, named pipes, message queues and Unix Domain
  164. Sockets, were investigated and rejected for one reason or another.<br>
  165. <br>
  166. The inotify mechanism supported by the Linux kernel is not intended to
  167. be used for IPC, at least not to the extent that I have done. As it has
  168. turned out, however, it "ticks all the boxes" for what I want for Puppy
  169. -- it is very fast, low resources, extremely simple, and remarkably
  170. amenable to the particular uses that I wanted to put it to.<br>
  171. <br>
  172. The References Section below has links that introduce inotify, read those if you need to understand the fundamentals. <br>
  173. <br>
  174. There is a directory /tmp/pup_event_ipc, the "mailbox", that
  175. contains the files that have the messages to be transferred. When a file
  176. pertaining to a particular client changes, the client "wakes up" and
  177. handles it. Basically the steps are:<br>
  178. <table border="1" cellpadding="2" cellspacing="0" width="100%">
  179. <tbody>
  180. <tr>
  181. <td valign="top"><b>1</b><br>
  182. </td>
  183. <td valign="top"><b>2</b><br>
  184. </td>
  185. <td valign="top"><b>3</b><br>
  186. </td>
  187. </tr>
  188. <tr>
  189. <td valign="top">ClientA is waiting on fileA<br>
  190. </td>
  191. <td valign="top">ClientB writes to fileA</td>
  192. <td valign="top">ClientA wakes up and reads file<br>
  193. </td>
  194. </tr>
  195. </tbody>
  196. </table>
  197. <br>
  198. That is really all it is. From those three steps, a complete IPC system can be devised.<br>
  199. <br>
  200. Of course, there all kinds of caveats and gotchas in this mechanism,
  201. such as guaranteeing a complete message is written to a file before it
  202. is read, and that files do not contain garbled messages if two or more
  203. clients write to them simultaneously.<br>
  204. <br>
  205. The way that I have implemented it, does, I think, avoid these pitfalls.<br>
  206. Atomicity of read and write is important for this to work, and from
  207. reading online I got two different figures for this for Linux: 1KB or
  208. 4KB (kilobytes). I was optimistic and used the higher figure.<br>
  209. <br>
  210. A communication is between two clients, using two files, for each
  211. direction of transfer. As long as they choose unique names, there is not
  212. going to be a clash with other transfers. There will not occur the
  213. problem of two different clients simultaneously writing to the same
  214. file.<br>
  215. <br>
  216. Anyway, even if two or more clients were allowed to write to the same
  217. file "simultaneously", atomicity and the O_APPEND write mode (see
  218. below), will ensure that individual messages remain intact.<br>
  219. <br>
  220. The CLI utility is the first implementation of inotify-based IPC for
  221. Puppy Linux, though, it is generic and will work for all Linux
  222. distributions. The easiest way I think to explain how it all "hangs
  223. together" is to show the code for this utility.<br>
  224. <br>
  225. The utility 'pup_event_ipc' is written in BaCon (see: <a href="http://bkhome.org/bacon/">http://bkhome.org/bacon</a>) with embedded C, so is a binary executable, small and fast. This is the code as at June 29, 2013:<br>
  226. <table border="1" cellpadding="2" cellspacing="0" width="100%">
  227. <tbody>
  228. <tr>
  229. <td valign="top">
  230. <pre>REM (c) Copyright Barry Kauler, June 2013, bkhome.org<br>REM License GPL3 (refer: /usr/share/doc/legal)<br>REM InterProcess Communication for Puppy Linux, CLI utility using inotify.<br>REM 130628 first version.<br><br>DECLARE helpflg,milliseconds TYPE int<br><br>'parse the commandline...<br>helpflg=0<br>milliseconds=0<br>sendstr$=""<br>IF argc==1 THEN helpflg=1<br>FOR x=1 TO argc-1<br> arg$=argv[x]<br> IF arg$=="--help" THEN<br> helpflg=1<br> CONTINUE<br> ENDIF<br> IF arg$=="-h" THEN<br> helpflg=1<br> CONTINUE<br> ENDIF<br> IF arg$=="-t" THEN<br> INCR x<br> milliseconds=VAL(argv[x])<br> CONTINUE<br> END IF<br> sendstr$=arg$<br>NEXT<br><br>IF helpflg==1 THEN<br> PRINT "(c) Copyright Barry Kauler, June 2013, license GPL3 (/usr/share/doc/legal)"<br> PRINT "This is a CLI utility that may be used in scripts. Run like this:"<br> PRINT " pup_event_ipc \"request:client[:message]\""<br> PRINT " request: name of another application to send the message to*. Or,"<br> PRINT " request: keyword to request information. Supported keywords:"<br> PRINT " block mailbox waitmail"<br> PRINT " client: any single-word to identify client (usually sender) application*."<br> PRINT " message: any text string (that other side understands), up to 4000 bytes."<br> PRINT " any characters allowed, including \":\""<br> PRINT "ex: pup_event_ipc \"otherapp:myapp:some data for otherapp\""<br> PRINT "ex: pup_event_ipc \"mailbox:otherapp:some data for otherapp\""<br> PRINT "ex: pup_event_ipc \"mailbox:myapp\"" <br> PRINT "ex: pup_event_ipc \"waitmail:myapp\""<br> PRINT "ex: pup_event_ipc \"block:myapp\""<br> PRINT "pup_event_ipc has an optional timeout (milliseconds),"<br> PRINT "ex: pup_event_ipc -t 1000 \"otherapp:myapp:data for you\""<br> PRINT "Will exit with non-zero value if an error:"<br> PRINT " 1=timeout. 2=wrong msg format. 3-7=message-passing failure. "<br> PRINT "*Any unique names that both clients agree on."<br> PRINT "PLEASE SEE /usr/local/pup_event/pup_event_ipc-README.htm for more details."<br> END 0<br>END IF<br><br>PROTO close, ftruncate, inotify_init, inotify_add_watch, inotify_rm_watch<br>PROTO lseek, open, poll, read, sizeof, strlen, write<br>DECLARE fd1,len1,leni,size_buf,thiswatchfile,otherwatchfile TYPE int<br>DECLARE arr1,thisapp,otherapp TYPE char*<br>DECLARE thisdescr,otherdescr TYPE int<br>size_char=sizeof(char)<br>size_inbuf=size_char*4096<br>DECLARE inbuf,bufinotify TYPE char ARRAY size_inbuf<br><br>SUB exitsub(NUMBER i)<br> IF watchdir&gt;0 THEN <b>inotify_rm_watch</b>(fd1,watchdir)<br> IF fd1&gt;0 THEN <b>close</b>(fd1)<br> IF otherdescr&gt;0 THEN <b>close</b>(otherdescr)<br> IF thisdescr&gt;0 THEN <b>close</b>(thisdescr)<br> END i<br>END SUB<br><br>'struct pollfd, defined in asm-generic/poll.h...<br>RECORD pfd<br> LOCAL fd TYPE int<br> LOCAL events TYPE short<br> LOCAL revents TYPE short<br>END RECORD<br>'linux/poll.h, only includes asm/poll.h, which only includes asm-generic/poll.h...<br>CONST POLLIN=1<br>pfd.events = POLLIN<br><br>SUB waitsub()<br> 'there is very slight possibility of deadlocking, if reply between lseek &amp; read/poll...<br> offt=<b>lseek</b>(thisdescr,0,SEEK_END)<br> IF offt==0 THEN<br> IF milliseconds==0 THEN<br> 'this will block until the file is modified...<br> leni=<b>read</b>(fd1,bufinotify,size_inbuf)<br> ELSE<br> pfd.fd=fd1<br> eventstatus=<b>poll</b>(&amp;pfd.fd,1,milliseconds)<br> IF eventstatus==0 THEN exitsub(1)<br> IF eventstatus&lt;0 THEN exitsub(7)<br> ENDIF<br> ENDIF<br> offt=<b>lseek</b>(thisdescr,0,SEEK_SET)<br> numr=<b>read</b>(thisdescr,inbuf,size_inbuf)<br> ft=<b>ftruncate</b>(thisdescr,0)<br> IF numr&gt;1 THEN<br> 'get rid of carriage-return char...<br> inbuf[numr-1]=0<br> inbuf$=inbuf<br> PRINT inbuf$<br> ENDIF<br>END SUB<br><br>'parse the message...<br>field1$=""<br>field2$=""<br>field3$=""<br>field4$=""<br>message$=""<br>arr1=sendstr$<br>off1=INSTR(arr1,":")<br>IF off1=0 THEN exitsub(2)<br>arr1[off1-1]=0<br>field1$=arr1<br>off2=INSTR(arr1+off1,":")<br>arr1[off1+off2-1]=0<br>field2$=arr1+off1<br>IF off2&gt;0 THEN<br> off3=INSTR(arr1+off1+off2,":")<br> 'the message-part can have ":" chars in it...<br> message$=arr1+off1+off2<br> arr1[off1+off2+off3-1]=0<br> field3$=arr1+off1+off2<br> IF off3&gt;0 THEN<br> off4=INSTR(arr1+off1+off2+off3,":")<br> arr1[off1+off2+off3+off4-1]=0<br> field4$=arr1+off1+off2+off3<br> ENDIF<br>ENDIF<br>'need to extract names of this-app and other-app...<br>otherapp=0<br>thisapp=0<br>thiscreateflag=1<br>SELECT field1$<br>CASE "mailbox"<br> IF message$!="" THEN<br> otherapp=field2$<br> ELSE<br> thisapp=field2$<br> ENDIF<br>CASE "waitmail"<br> thisapp=field2$<br>CASE "block"<br> thisapp$=CONCAT$("block_",field2$)<br> thisapp=thisapp$<br> 'thisfile$ is not created here, it is created by daemon that monitors kernel uevents...<br> thiscreateflag=0<br>DEFAULT<br> otherapp=field1$<br> thisapp=field2$<br>END SELECT<br><br>'create the files...<br>otherdescr=0<br>thisdescr=0<br>watchdir=0<br>IF FILEEXISTS("/tmp/pup_event_ipc")==0 THEN MAKEDIR "/tmp/pup_event_ipc"<br>IF otherapp!=0 THEN<br> otherfile$=CONCAT$("/tmp/pup_event_ipc/",otherapp)<br> USEC<br> /*defined in /usr/include/asm-generic/fcntl.h &amp; i386-linux-gnu/bits/fcntl-linux.h...*/<br> #define O_WRONLY 00000001<br> #define O_CREAT 00000100<br> # define O_SYNC 04010000<br> #define O_FSYNC O_SYNC<br> #define O_APPEND 00002000<br> otherdescr=<b>open</b>(otherfile$, O_WRONLY | O_CREAT | O_APPEND);<br> END USEC<br> IF otherdescr&lt;=0 THEN exitsub(3)<br>ENDIF<br>IF thisapp!=0 THEN<br> closedflag=0<br> thisfile$=CONCAT$("/tmp/pup_event_ipc/",thisapp)<br> IF thiscreateflag=1 THEN<br> USEC<br> #define O_RDONLY 00000000<br> #define O_CREAT 00000100<br> #define O_RDWR 00000002<br> thisdescr=<b>open</b>(thisfile$,O_RDWR | O_CREAT);<br> END USEC<br> ELSE<br> IF FILEEXISTS(thisfile$)==1 THEN<br> USEC<br> #define O_RDONLY 00000000<br> #define O_RDWR 00000002<br> thisdescr=<b>open</b>(thisfile$,O_RDWR);<br> END USEC<br> ELSE<br> closedflag=1<br> ENDIF<br> ENDIF<br> IF closedflag==0 THEN<br> IF thisdescr&lt;=0 THEN exitsub(4)<br> fd1=<b>inotify_init</b>()<br> IF fd1&lt;=0 THEN exitsub(5)<br> CONST IN_MODIFY=2<br> watchdir=<b>inotify_add_watch</b>(fd1,thisfile$,IN_MODIFY)<br> IF watchdir&lt;=0 THEN exitsub(6)<br> ENDIF<br>ENDIF<br><br>'now decide what to do...<br>SELECT field1$<br>CASE "mailbox"<br> IF otherapp!=0 THEN<br> 'need to post message to otherapp... format: "mailbox:otherapp:message"<br> outstr$=CONCAT$(message$,"\n")<br> wr=<b>write</b>(otherdescr,outstr$,strlen(outstr$))<br> PRINT "Mailbox acknowledge"<br> ELSE<br> 'need to check if waiting mail for thisapp...<br> offt=<b>lseek</b>(thisdescr,0,SEEK_END)<br> IF offt==0 THEN<br> PRINT "Mailbox empty"<br> ELSE<br> offt=<b>lseek</b>(thisdescr,0,SEEK_SET)<br> numr=<b>read</b>(thisdescr,inbuf,size_inbuf)<br> ft=<b>ftruncate</b>(thisdescr,0)<br> IF numr&gt;1 THEN<br> 'get rid of carriage-return char...<br> inbuf[numr-1]=0<br> inbuf$=inbuf<br> PRINT inbuf$<br> ENDIF<br> ENDIF<br> ENDIF<br>CASE "waitmail"<br> 'need to wait for mail for thisapp... format: "waitmail:thisapp"<br> waitsub()<br>CASE "block"<br> 'need to wait for block-drive hotplug event, for thisapp... format: "block:thisapp"<br> IF FILEEXISTS(thisfile$)==0 THEN<br> PRINT "Not implemented"<br> ELSE<br> waitsub()<br> ENDIF<br>DEFAULT<br> 'post msg to otherapp, wait for reply to thisapp... format: "otherapp:thisapp:message"<br> outstr$=CONCAT$(message$,"\n")<br> wr=<b>write</b>(otherdescr,outstr$,strlen(outstr$))<br> waitsub()<br>END SELECT<br><br>exitsub(0)<br></pre>
  231. </td>
  232. </tr>
  233. </tbody>
  234. </table>
  235. <br>
  236. This is a remarkably small program to implement a complete IPC system. Essentially, it does this:<br>
  237. <ol>
  238. <li>Parses the message that has been passed on the commandline.</li>
  239. <li>Creates one or two files in /tmp/pup_event_ipc, and opens them.</li>
  240. <li>Based on the passed message, data will be written to one file, or data read from the other.</li>
  241. <li>Reading may or may not block, depending on the message.<br>
  242. </li>
  243. </ol>
  244. When a file is opened for writing, it is done with the 'O_APPEND' mode,
  245. meaning that writes to it always append to the end of the file, totally
  246. ignoring the file-pointer.<br>
  247. Messages are line-based, that is, one message per line. This allows
  248. messages to accumulate, while waiting to be read -- hence, most
  249. important, no messages get lost.<br>
  250. <br>
  251. Take the message "block:myapp" as an example. The name "myapp" is an
  252. arbitrary choice, anything unique is suitable. If this is posted by the
  253. CLI utility:<br>
  254. <pre># pup_event_ipc "block:myapp"</pre>
  255. If you were to trace through the above code, you would see that if file
  256. "/tmp/pup_event_ipc/block_myapp" is opened (or created if it does not
  257. exist), then there is a block on waiting for something to be written to
  258. the file.<br>
  259. When something is written to the file, it is read, and the file zeroised. The read contents are printed on stdout.<br>
  260. <br>
  261. The above scenario means that some other application must write to /tmp/pup_event_ipc/block_myapp. <br>
  262. In the very latest builds of Puppy, we do have a an application that
  263. detects drive hotplug activity, a daemon named <b>pup_event_frontend_d</b>, that performs this information server function.<br>
  264. <br>
  265. The function that such a server would have to perform can be show by a shell script:<br>
  266. <pre>for ONEFILE in `ls /tmp/pup_event_ipc/block_*`<br>do<br>&nbsp;echo $BLOCKINFO &gt;&gt; $ONEFILE<br>done</pre>
  267. That's it, done. All clients that are waiting on block-drive hotplug
  268. notification, will "wake up" and read the data. As mentioned before, it
  269. does not matter if a client is in a loop, doing other stuff:<br>
  270. <pre>while true; do<br>&nbsp;EVENTS="`pup_event_ipc "block:myapp"`"<br>&nbsp;...<i>other stuff</i>...<br>done</pre>
  271. While executing "other stuff", any new block-drive events will append to
  272. /tmp/pup_event_ipc/block_myapp (that is, queue )until it is next read.<br>
  273. <br>
  274. Note: For the above example, the code in pup_event_ipc does not
  275. automatically create /tmp/pup_event_ipc/block_myapp. This is for usage
  276. in older puppies and other distros. In latest experimental Puppy builds
  277. however, the line "thiscreateflag=0" is commented out, we have the
  278. pup_event_frontend_d daemon that feeds kernel block-drive events (as
  279. described above).<br>
  280. pup_event_frontend_d is described further in the next section.<br>
  281. <br>
  282. There is a lot that can be said about the flexibility of this
  283. inotify-based IPC mechanism. Just one example: although the
  284. pup_event_ipc utility does have a timeout option, you can cause a
  285. timeout whenever you want -- say, for example that a shell script has
  286. executed "pup_event_ipc 'block:myapp'" and is blocked, waiting on a
  287. modification to file /tmp/pup_event_ipc/block_myapp. Well, if you just
  288. execute "touch /tmp/pup_event_ipc/block_myapp", the read() function that
  289. is waiting will return, with zero data of course -- but voila, a simple
  290. way to cause a timeout.<br>
  291. <br>
  292. <hr size="2" width="100%"><h2>
  293. Example code blocks</h2>
  294. I will add to this section. For now, I have included code to show how
  295. the "server" can be implemented to serve block-drive event
  296. notifications.<br>
  297. <br>
  298. <h3>Block-drive event notification</h3>
  299. It has been explained above, that a client application may request
  300. block-drive event notification by posting a message of format
  301. "block:myapp" to the IPC mailbox. <br>
  302. <br>
  303. Puppy Linux has a daemon, named <b>pup_event_frontend_d</b>, at
  304. /usr/local/pup_event (as well as the BaCon source file,
  305. pup_event_frontend_d.bac, and there is a wrapper script at /sbin). This
  306. daemon is started from /root/.xinitrc when X starts up. It does a lot of
  307. background stuff, such as manage the desktop drive icons. For that
  308. latter reason, it does have information about drive hotplug events.<br>
  309. <br>
  310. It was an easy matter to add a bit of extra code to pup_even_frontend_d,
  311. to post this information to clients that want it, via the IPC
  312. mechanism. Example shell script code is shown in the above section, how
  313. this can be done.<br>
  314. <br>
  315. The actual code inserted into pup_event_frontend_d is shown below.
  316. Basically, it looks for any files named /tmp/pup_event_ipc/block_*, then
  317. writes the drive hotplug information to them (string devevents$):<br>
  318. <table border="1" cellpadding="2" cellspacing="0" width="100%">
  319. <tbody>
  320. <tr>
  321. <td valign="top">
  322. <pre>PROTO opendir, readdir<br>DECLARE dir1 TYPE DIR*<br>DECLARE ent1 TYPE struct dirent*<br><br>dir1=<b>opendir</b>("/tmp/pup_event_ipc")<br>IF dir1!=NULL THEN<br> WHILE TRUE DO<br> ent1=<b>readdir</b>(dir1)<br> IF ent1==NULL THEN BREAK<br> off1=<b>INSTR</b>((*ent1).d_name,"block_")<br> IF off1!=0 THEN<br> clientfile$=<b>CONCAT$</b>("/tmp/pup_event_ipc/",(*ent1).d_name)<br> outmsg$=<b>CONCAT$</b>(devevents$,"\n")<br> USEC<br> #define O_WRONLY 00000001<br> #define O_APPEND 00002000<br> int clientdescr;<br> clientdescr=<b>open</b>(clientfile$, O_WRONLY | O_APPEND);<br> if (clientdescr&gt;0) {<br> int wr=<b>write</b>(clientdescr,outmsg$,strlen(outmsg$));<br> close(clientdescr);<br> }<br> END USEC<br> ENDIF<br> WEND<br><br>ENDIF<br></pre>
  323. </td>
  324. </tr>
  325. </tbody>
  326. </table>
  327. <br>
  328. Note, BaCon is just a translator to C, which is then compiled using the
  329. Gnu C compiler. It is really just a thin translation layer, so many
  330. C-isms can be put into the BaCon code, and pure C can be embedded. I use
  331. BaCon as I prefer the syntax, plus it has many higher-level features,
  332. such as better string handling, and associative arrays. It is easy to read the above and translate to all-C if required.<br>
  333. <br>
  334. ...<i>more coming</i>...<br>
  335. <br>
  336. <br>
  337. <hr size="2" width="100%"><h2>Feedback welcome</h2>
  338. I will be posting about progress on pup_event to my blog, <a href="http://bkhome.org/blog2/">http://bkhome.org/blog2/</a>&nbsp; <br>
  339. <br>
  340. Feedback will be welcome. If you can think of anything that might
  341. compromise the integrity of data transfer with this technique, or code
  342. modifications that will add improvements (while still keeping everything
  343. simple, including simple and small code).<br>
  344. <br>
  345. Regards,<br>
  346. Barry Kauler<br>
  347. <br>
  348. <hr size="2" width="100%"><h2>References</h2>
  349. Reference on libc functions:<br>
  350. <a href="http://www.gnu.org/software/libc/manual/html_node/Function-Index.html">http://www.gnu.org/software/libc/manual/html_node/Function-Index.html</a> <br>
  351. Wikipedia:<br>
  352. <a href="http://en.wikipedia.org/wiki/Inotify">http://en.wikipedia.org/wiki/Inotify</a>&nbsp; <br>
  353. Intro to inotify:<br>
  354. <a href="http://www.linuxjournal.com/article/8478">http://www.linuxjournal.com/article/8478</a>&nbsp; <br>
  355. Monitor file system activity with inotify:<br>
  356. <a href="http://www.ibm.com/developerworks/library/l-ubuntu-inotify/">http://www.ibm.com/developerworks/library/l-ubuntu-inotify/</a>&nbsp; <br>
  357. Monitor Linux File System Events with Inotify:<br>
  358. <a href="http://www.developertutorials.com/tutorials/linux/monitor-linux-inotify-050531-1133/">http://www.developertutorials.com/tutorials/linux/monitor-linux-inotify-050531-1133/</a>&nbsp; <br>
  359. The inotify API:<br>
  360. <a href="http://linux.die.net/man/7/inotify">http://linux.die.net/man/7/inotify</a>&nbsp; <br>
  361. <br>
  362. <br>
  363. </td>
  364. </tr>
  365. <tr>
  366. <td valign="top"><small>(c) Copyright Barry Kauler, 2013. All reproduction rights of this page are reserved.</small> <br>
  367. </td>
  368. </tr>
  369. </tbody>
  370. </table>
  371. <br>
  372. <br>
  373. </body></html>