<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Inotify on Roberto Selbach</title><link>https://rselbach.com/tags/inotify/</link><description>Recent content in Inotify on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 16 Sep 2005 20:05:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/inotify/index.xml" rel="self" type="application/rss+xml"/><item><title>Replacing the Directory Poll With inotify</title><link>https://rselbach.com/replacing-the-directory-poll-with-inotify/</link><pubDate>Fri, 16 Sep 2005 20:05:00 +0000</pubDate><guid>https://rselbach.com/replacing-the-directory-poll-with-inotify/</guid><description>&lt;p&gt;Now that Linux 2.6.13 includes inotify, I replaced a timer in a small file-watching utility. The timer scanned a directory every second, which was simple, portable, and wasteful. It also introduced the charming choice between delayed updates and more frequent useless work.&lt;/p&gt;
&lt;p&gt;My first inotify version assumed each &lt;code&gt;read()&lt;/code&gt; returned one complete event. It worked during gentle testing and became nonsense when several files changed quickly. The interface returns a byte stream containing one or more variable-length &lt;code&gt;struct inotify_event&lt;/code&gt; records. Correct code walks the buffer by each fixed header plus its &lt;code&gt;len&lt;/code&gt; field.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;union&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event event;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;char&lt;/span&gt; bytes[&lt;span style="color:#ae81ff"&gt;4096&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} buffer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ssize_t&lt;/span&gt; count;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;read&lt;/span&gt;(fd, buffer.bytes, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(buffer.bytes));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (count &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; errno &lt;span style="color:#f92672"&gt;==&lt;/span&gt; EINTR);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (count &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;perror&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;read&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (count &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;inotify descriptor reached EOF&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (offset &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event &lt;span style="color:#f92672"&gt;*&lt;/span&gt;event;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt; event_size;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ((&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count &lt;span style="color:#f92672"&gt;-&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;event)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;short inotify event header&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; inotify_event &lt;span style="color:#f92672"&gt;*&lt;/span&gt;)(buffer.bytes &lt;span style="color:#f92672"&gt;+&lt;/span&gt; offset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; event_size &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;event) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; event&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (event_size &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;size_t&lt;/span&gt;)count &lt;span style="color:#f92672"&gt;-&lt;/span&gt; offset) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;fprintf&lt;/span&gt;(stderr, &lt;span style="color:#e6db74"&gt;&amp;#34;short inotify event name&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;exit&lt;/span&gt;(EXIT_FAILURE);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;handle_event&lt;/span&gt;(event);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; offset &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; event_size;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The union gives the byte storage suitable alignment for &lt;code&gt;struct inotify_event&lt;/code&gt;; a plain character array doesn&amp;rsquo;t promise that. The code also deals with interruption, read failure, EOF, and truncated records before treating bytes as an event. A watch identifies a pathname to the kernel, and events report changes associated with a watch descriptor.&lt;/p&gt;
&lt;p&gt;The largest conceptual correction was learning that notifications are hints about state changes, not a private journal guaranteed to preserve my worldview. Events can arrive in bursts. A queue can overflow. A watched object can move or disappear. On overflow, the safe response is to rescan and rebuild known state rather than guess what was missed.&lt;/p&gt;
&lt;p&gt;Renames deserve similar care. Related move events carry a cookie that can help pair them, but the application still needs sensible behavior when only one side is visible. Watching a directory also does not automatically mean recursively watching every directory below it.&lt;/p&gt;
&lt;p&gt;I kept one function that performs a complete scan and made event handling update the same internal model. That gives the program a recovery path and keeps startup behavior consistent with overflow recovery. The event loop is an optimization over known state, not the only source of truth.&lt;/p&gt;
&lt;p&gt;Compared with polling, inotify is immediate and avoids repeated directory walks when nothing changes. It also exposes more edge cases because the kernel reports actual operations rather than letting a periodic snapshot blur them together.&lt;/p&gt;
&lt;p&gt;I prefer the new mechanism, with one reservation: event-driven doesn&amp;rsquo;t mean infallible. Read complete buffers, parse every record, expect overflow, and keep a rescan path. The kernel says something happened; deciding what the application should now believe is still our job.&lt;/p&gt;</description></item></channel></rss>