On Friday, 27 December 2013 at 03:39:58 UTC, Hugo Florentino wrote:
BTW, it it a requirement to use malloc, and if so, when would I need to free the memory allocated by it?

I use a static ubyte array.

I've been using inotify quite a bit and found it to be very good but there are a few things to keep in mind though. First it can block further program execution when watching files. To avoid this use the select function in 'core.sys.posix.sys.select'. Second, if the file doesn't exist or is moved once watched, the inotifiy instance or the watch descriptor will be invalid and need to be re-initialised. Tip: moving can occur if edited with a text editor. As i found out trying to test inotify by changing a file using vim! That was debug pain!

Here is a snippet of code to show how i used it. It's not complete but it shows you how to use the select function and how all the bits fit together. I used an infinite loop because this was part of a daemon that runs forever so you may want to handle that better.

/**
 * Module.
 */
module common.file.watcher.inotifyengine;

/**
 * Imports.
 */
import core.sys.linux.sys.inotify;
import core.sys.posix.sys.select;
import core.sys.posix.unistd;
import common.file.logger;
import common.file.watcher.engine;
import std.string;

/**
 * A class to watch for changes in a file.
 *
 * Uses the linux inotify subsystem.
 */
class INotifyEngine : Engine
{
        /**
         * The event buffer length.
         *
* This gives us for 1024 events which should be more than enough.
         */
private enum eventBufferLength = (inotify_event.sizeof + 16) * 1024;

        /**
         * The notification flags.
         *
* These are what inotify uses to descriminate on which events to nofify us about.
         */
private enum notificationFlags = IN_MODIFY | IN_ATTRIB | IN_DELETE_SELF | IN_MOVE_SELF | IN_IGNORED;

        /**
         * The inotify instance.
         */
        private int _inotifyInstance;

        /**
         * The watch descriptor.
         */
        private int _watchDescriptor;

        /**
         * The file descriptor set for the select call.
         */
        private fd_set _fileDescriptorSet;

        /**
         * The timeout for the select call.
         */
        private timeval _timeout;

        /**
         * Constructor.
         *
         * Params:
         *     logger = The logger object used to log messages.
         *
         * See_Also:
         *     Engine
         */
        public this(Logger logger)
        {
                super(logger);
        }

        /**
         * Initialize inotify.
         *
         * Throws:
         *     Exception if inotify fails to initialize.
         */
        private void initInotify()
        {
                this._inotifyInstance = inotify_init();

                if (this._inotifyInstance < 0)
                {
                        this._logger.error("Inotify failed to initialize.");
                }

this._watchDescriptor = inotify_add_watch(this._inotifyInstance, cast(char*)this._lastFileName.toStringz(), notificationFlags);
        }

        /**
         * Stop inotify.
         */
        private void closeInotify()
        {
                inotify_rm_watch(this._inotifyInstance, this._watchDescriptor);
                close(this._inotifyInstance);
        }

        /**
         * Change the watcher if the file name changes.
         */
        private void checkFileNameChange()
        {
                string currentFileName = this.getFileName();

                if (currentFileName != this._lastFileName)
                {
this._logger.warning("Watched file name changed to '%s'", currentFileName);

                        this.notify(this._lastFileName);
                        this._lastFileName = currentFileName;
                        this.notify(this._lastFileName);

                        this.closeInotify();
                        this.initInotify();

this._logger.info("Starting to watch '%s' for alerts (%d, %d)", this._lastFileName, this._inotifyInstance, this._watchDescriptor);
                }
        }

        /**
* Retry watching if there was a problem e.g. the file doesn't exist yet.
         */
        private void checkWatchStatus()
        {
                if (this._inotifyInstance == -1 || this._watchDescriptor == -1)
                {
this._logger.error("Failed watching '%s' for alerts, retrying...", this._lastFileName);

                        this.closeInotify();
                        this.initInotify();

this._logger.info("Starting to watch '%s' for alerts (%d, %d)", this._lastFileName, this._inotifyInstance, this._watchDescriptor);
                }
        }

        /**
         * Check the file for any changes.
         *
* If changes occur then execute the action if one has been assigned. * We are using the select call to perform non blocking event handling waiting for inotify. * If inotify detects a change in the file, select returns immediately.
         *
         * See_Also:
         *     IEngine
         */
        public void start()
        {
                this._lastFileName = this.getFileName();

                this.notify(this._lastFileName);

                this.initInotify();

this._logger.info("Starting to watch '%s' for alerts (%d, %d)", this._lastFileName, this._inotifyInstance, this._watchDescriptor);

                ubyte[eventBufferLength] eventBuffer;
                void* eventPointer;

                while (true)
                {
                        FD_ZERO(&this._fileDescriptorSet);
                        FD_SET(this._inotifyInstance, &this._fileDescriptorSet);

                        this._timeout.tv_usec = 0;
                        this._timeout.tv_sec  = this._interval;

if (select(FD_SETSIZE, &this._fileDescriptorSet, null, null, &this._timeout))
                        {
auto bytesRead = read(this._inotifyInstance, eventBuffer.ptr, eventBuffer.length);

for (eventPointer = eventBuffer.ptr; eventPointer < eventBuffer.ptr + bytesRead; null)
                                {
                                        inotify_event* event = 
cast(inotify_event*)eventPointer;

                                        if (event.mask & IN_MODIFY || event.mask 
& IN_ATTRIB)
                                        {
                                                this.notify(this._lastFileName);
                                        }

if (event.mask & IN_DELETE_SELF || event.mask & IN_MOVE_SELF || event.mask & IN_IGNORED)
                                        {
                                                this.notify(this._lastFileName);

                                                this.closeInotify();
                                                this.initInotify();

this._logger.warning("Alert log moved or deleted, re-initialized to watch '%s' again (%d, %d)", this._lastFileName, this._inotifyInstance, this._watchDescriptor);
                                        }

                                        eventPointer += inotify_event.sizeof + 
event.len;
                                }
                        }

                        this.checkFileNameChange();
                        this.checkWatchStatus();
                }
        }

        /**
         * Destructor.
         */
        ~this()
        {
                this.closeInotify();
        }
}

Reply via email to