inotify file write completion - golang & Postfix

89 views Asked by At

I'm using Postfix on a server to receive emails. When an email is received I want to trigger some golang code. I'm using the inotify package, and am monitoring the Maildir/new folder for new incoming emails.

I'm monitoring only the IN_CLOSE_WRITE event on the folder, but I see it's never being triggered. Only IN_CREATE events are being triggered.

I assume that as I'm monitoring the folder, and not the file, it sees just the create file events (as they effect the folder), not what happens later to the file (close write).

However I don't know the name of the file in advance, it's a temp file being created by Postfix outside of my control, and I don't want just to sleep for let's say 1 second as it may take longer for Postfix to finish writing, and then on the other hand if I try to set up a watch quickly on that file I may be on a race condition with Postfix, as it may finish writing the email much faster that I would have even established the initial watch, and I would not be able to distinguish between file created, written and closed, and write in progress, as events are not being triggered during that time.

How would you have elegantly resolved this issue? I don't want to sleep, as it's not elegant and I cannot know for sure for how long, and I want it to be as short as possible, and I don't want to be in any race condition with Postfix.

2

There are 2 answers

1
tripleee On

I believe Postfix easily lets you hook into the actual delivery event. Look at how Procmail does it, or even just create a Procmail rule to run your script when something is delivered.

In very brief, in your .procmailrc, put

:0c
| /path/to/your/binary

to pipe a copy of each incoming message to your binary.

1
Vitaliy On

OK, to anyone that encounters this I understand what's going on :) The whole sequence of events is:

"/home/server/Maildir/tmp/1702916546.P795095.server": 0x100 == IN_CREATE
"/home/server/Maildir/tmp/1702916546.P795095.server": 0x20 == IN_OPEN
"/home/server/Maildir/tmp/1702916546.P795095.server": 0x2 == IN_MODIFY
"/home/server/Maildir/tmp/1702916546.P795095.server": 0x8 == IN_CLOSE_WRITE
"/home/server/Maildir/new/1702916546.V802I24257M499995.server": 0x100 == IN_CREATE
"/home/server/Maildir/tmp/1702916546.P795095.server": 0x200 == IN_DELETE

So as you can see, the email file is of course being created in the tmp dir first, where the long seeked for IN_CLOSE_WRITE is actually being triggered, and after the file is completely written, it is being moved to the new directory, and deleted from the tmp directory. That's why the new directory only sees the IN_CREATE.

So what happens here is simply an mv from one dir to another, in the same filesystem, so it seems safe to just monitor the new dir for IN_CREATE events.

@triplee's approach also looks great when the binary needs to be triggered from clean - i.e the binary is completely independent, there is no logic that runs before the trigger. In my case it was more convenient to stay in the context of the server.