lsyncd/inotify.c

571 lines
10 KiB
C
Raw Permalink Normal View History

/*
| inotify.c from Lsyncd - Live (Mirror) Syncing Demon
|
| License: GPLv2 (see COPYING) or any later version
|
| Authors: Axel Kittenberger <axkibe@gmail.com>
|
| -----------------------------------------------------------------------
|
| Event interface for Lsyncd to Linux´ inotify.
*/
2010-11-22 21:17:08 +00:00
#include "lsyncd.h"
#include <sys/stat.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/inotify.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
/*
| Event types.
*/
static const char * ATTRIB = "Attrib";
static const char * MODIFY = "Modify";
static const char * CREATE = "Create";
static const char * DELETE = "Delete";
static const char * MOVE = "Move";
2010-11-24 18:19:13 +00:00
/*
* The inotify file descriptor.
*/
2010-11-22 21:17:08 +00:00
static int inotify_fd = -1;
/*
| Standard inotify events to listen to.
*/
2011-11-23 10:22:10 +00:00
static const uint32_t standard_event_mask =
IN_ATTRIB |
IN_CLOSE_WRITE |
IN_CREATE |
IN_DELETE |
IN_DELETE_SELF |
IN_MOVED_FROM |
IN_MOVED_TO |
IN_DONT_FOLLOW |
IN_ONLYDIR;
2011-06-11 16:38:47 +00:00
/*
| Adds an inotify watch
|
| param dir (Lua stack) path to directory
| param inotifyMode (Lua stack) which inotify event to react upon
| "CloseWrite", "CloseWrite or Modify"
|
| returns (Lua stack) numeric watch descriptor
*/
static int
l_addwatch( lua_State *L )
{
const char *path = luaL_checkstring( L, 1 );
const char *imode = luaL_checkstring( L, 2 );
2011-06-11 16:38:47 +00:00
uint32_t mask = standard_event_mask;
// checks the desired inotify reaction mode
if (*imode)
{
if ( !strcmp( imode, "Modify" ) )
{
// acts on modify instead of closeWrite
mask |= IN_MODIFY;
2011-06-11 16:38:47 +00:00
mask &= ~IN_CLOSE_WRITE;
}
else if ( !strcmp( imode, "CloseWrite" ) )
{
// thats default
}
else if ( !strcmp( imode, "CloseWrite or Modify" ) )
{
2012-02-16 15:49:34 +00:00
// acts on modify and closeWrite
2011-06-11 16:38:47 +00:00
mask |= IN_MODIFY;
}
else if ( ! strcmp( imode, "CloseWrite after Modify") )
{
2012-02-16 15:49:34 +00:00
// might be done in future
printlogf(
L, "Error",
"'CloseWrite after Modify' not implemented."
);
exit(-1);
}
else
{
printlogf(
L, "Error",
"'%s' not a valid inotfiyMode.",
imode
);
exit(-1);
2011-06-11 16:38:47 +00:00
}
}
// kernel call to create the inotify watch
int wd = inotify_add_watch( inotify_fd, path, mask );
2011-06-11 16:38:47 +00:00
if( wd < 0 )
{
if( errno == ENOSPC )
{
printlogf(
L, "Error",
"%s\n%s",
"Terminating since out of inotify watches.",
"Consider increasing /proc/sys/fs/inotify/max_user_watches"
);
exit(-1); // ERRNO.
}
printlogf(
L, "Inotify",
"addwatch( %s )-> %d; err= %d : %s",
path, wd, errno, strerror( errno )
);
}
else
{
printlogf(L, "Inotify", "addwatch( %s )-> %d ", path, wd );
}
lua_pushinteger( L, wd );
return 1;
}
/*
* Removes an inotify watch.
*
* param dir (Lua stack) numeric watch descriptor
*
* return nil
*/
static int
l_rmwatch( lua_State *L )
{
int wd = luaL_checkinteger( L, 1 );
inotify_rm_watch( inotify_fd, wd );
printlogf( L, "Inotify", "rmwatch()<-%d", wd );
return 0;
}
/*
| Lsyncd's core's inotify functions.
*/
static const luaL_Reg linotfylib[ ] =
{
{ "addwatch", l_addwatch },
{ "rmwatch", l_rmwatch },
{ NULL, NULL}
};
/*
| Buffer for MOVE_FROM events.
| Lsyncd buffers MOVE_FROM events to check if
| they are followed by MOVE_TO events with identical cookie
| then they are condensed into one move event to be sent to the
| runner
*/
static struct inotify_event * move_event_buf = NULL;
/*
| Memory allocated for move_event_buf
*/
static size_t move_event_buf_size = 0;
/*
| True if the buffer is used.
*/
static bool move_event = false;
/*
| Handles an inotify event.
*/
2011-11-23 10:22:10 +00:00
static void
handle_event(
lua_State *L,
struct inotify_event *event
)
{
2010-11-24 18:19:13 +00:00
const char *event_type = NULL;
2012-02-16 15:49:34 +00:00
// used to execute two events in case of unmatched MOVE_FROM buffer
struct inotify_event *after_buf = NULL;
if( event && ( IN_Q_OVERFLOW & event->mask ) )
{
// and overflow happened, tells the runner
load_runner_func( L, "overflow" );
2018-03-09 14:41:16 +00:00
if( lua_pcall( L, 0, 0, -2 ) ) exit( -1 );
lua_pop( L, 1 );
hup = 1;
return;
}
2012-02-16 15:49:34 +00:00
// cancel on ignored or resetting
if( event && ( IN_IGNORED & event->mask ) )
{
return;
}
if( event && event->len == 0 )
{
2012-02-16 15:49:34 +00:00
// sometimes inotify sends such strange events,
// (e.g. when touching a dir
return;
}
if( event == NULL )
{
2012-02-16 15:49:34 +00:00
// a buffered MOVE_FROM is not followed by anything,
// thus it is unary
event = move_event_buf;
2010-11-24 18:19:13 +00:00
event_type = "Delete";
move_event = false;
}
else if(
move_event
&& (
!( IN_MOVED_TO & event->mask )
|| event->cookie != move_event_buf->cookie
)
)
{
2012-02-16 15:49:34 +00:00
// there is a MOVE_FROM event in the buffer and this is not the match
// continue in this function iteration to handle the buffer instead */
logstring(
"Inotify",
"icore, changing unary MOVE_FROM into DELETE"
);
after_buf = event;
event = move_event_buf;
2010-11-24 18:19:13 +00:00
event_type = "Delete";
move_event = false;
}
else if(
move_event
&& ( IN_MOVED_TO & event->mask )
&& event->cookie == move_event_buf->cookie
)
{
2012-02-16 15:49:34 +00:00
// this is indeed a matched move */
2010-11-24 18:19:13 +00:00
event_type = "Move";
move_event = false;
}
else if( IN_MOVED_FROM & event->mask )
{
2012-02-16 15:49:34 +00:00
// just the MOVE_FROM, buffers this event, and wait if next event is
// a matching MOVED_TO of this was an unary move out of the watched
// tree.
size_t el = sizeof( struct inotify_event ) + event->len;
if( move_event_buf_size < el )
{
move_event_buf_size = el;
move_event_buf = s_realloc( move_event_buf, el );
}
memcpy( move_event_buf, event, el );
move_event = true;
return;
}
else if( IN_MOVED_TO & event->mask )
{
2012-02-16 15:49:34 +00:00
// must be an unary move-to
event_type = CREATE;
}
else if( IN_ATTRIB & event->mask )
{
2012-02-16 15:49:34 +00:00
// just attrib change
event_type = ATTRIB;
}
else if( ( IN_CLOSE_WRITE | IN_MODIFY) & event->mask )
{
2012-03-16 15:30:55 +00:00
// modify, or closed after written something
// the event type received depends settings.inotifyMode
event_type = MODIFY;
}
else if( IN_CREATE & event->mask )
{
2012-02-16 15:49:34 +00:00
// a new file
event_type = CREATE;
}
else if( IN_DELETE & event->mask )
{
2012-02-16 15:49:34 +00:00
// rm'ed
event_type = DELETE;
}
else
{
logstring(
"Inotify",
"skipped some inotify event."
);
return;
}
// hands the event over to the runner
load_runner_func( L, "inotifyEvent" );
if( !event_type )
{
logstring(
"Error",
"internal failure: unknown event in handle_event()"
);
exit( -1 );
}
lua_pushstring( L, event_type );
if( event_type != MOVE )
{
lua_pushnumber( L, event->wd );
}
else
{
lua_pushnumber( L, move_event_buf->wd );
}
lua_pushboolean( L, ( event->mask & IN_ISDIR ) != 0 );
l_now( L );
if( event_type == MOVE )
{
lua_pushstring( L, move_event_buf->name );
lua_pushnumber( L, event->wd );
lua_pushstring( L, event->name );
}
else
{
lua_pushstring( L, event->name );
lua_pushnil( L );
lua_pushnil( L );
}
2018-03-09 14:41:16 +00:00
if( lua_pcall( L, 7, 0, -9 ) ) exit( -1 );
lua_pop( L, 1 );
2010-11-23 23:19:01 +00:00
2012-02-16 15:49:34 +00:00
// if there is a buffered event, executes it
if (after_buf) {
2010-11-24 18:19:13 +00:00
logstring("Inotify", "icore, handling buffered event.");
handle_event(L, after_buf);
}
}
/*
| buffer to read inotify events into
*/
2010-11-22 21:17:08 +00:00
static size_t readbuf_size = 2048;
2010-11-22 21:17:08 +00:00
static char * readbuf = NULL;
/*
| Called when the inotify file descriptor became ready.
| Reads it contents and forwards all received events
| to the runner.
*/
static void
inotify_ready(
lua_State *L,
struct observance *obs
)
{
// sanity check
if( obs->fd != inotify_fd )
{
2018-03-09 14:41:16 +00:00
logstring( "Error", "internal failure, inotify_fd != obs->fd" );
exit( -1 );
2010-11-28 10:47:57 +00:00
}
while( true )
{
2011-11-23 10:22:10 +00:00
ptrdiff_t len;
2010-11-23 23:19:01 +00:00
int err;
do {
len = read( inotify_fd, readbuf, readbuf_size );
2010-11-23 23:19:01 +00:00
err = errno;
if( len < 0 && err == EINVAL )
{
2012-02-16 15:49:34 +00:00
// kernel > 2.6.21 indicates that way that way that
// the buffer was too small to fit a filename.
// double its size and try again. When using a lower
// kernel and a filename > 2KB appears lsyncd
// will fail. (but does a 2KB filename really happen?)
//
readbuf_size *= 2;
readbuf = s_realloc(readbuf, readbuf_size);
}
} while( len < 0 && err == EINVAL );
if( len == 0 )
{
// no more inotify events
break;
}
if (len < 0)
{
2010-11-23 23:19:01 +00:00
if (err == EAGAIN) {
2012-02-16 15:49:34 +00:00
// nothing more inotify
break;
}
else
{
printlogf(
L, "Error",
"Read fail on inotify"
);
exit( -1 );
}
}
2010-11-22 21:17:08 +00:00
{
int i = 0;
while( i < len && !hup && !term )
{
2011-11-23 10:22:10 +00:00
struct inotify_event *event =
( struct inotify_event * )
(readbuf + i);
handle_event( L, event );
i += sizeof( struct inotify_event ) + event->len;
2010-11-22 21:17:08 +00:00
}
}
if( !move_event )
{
2012-02-16 15:49:34 +00:00
// give it a pause if not endangering splitting a move
break;
}
2010-11-22 21:17:08 +00:00
}
2012-02-16 15:49:34 +00:00
// checks if there is an unary MOVE_FROM left in the buffer
if( move_event )
{
logstring(
"Inotify",
"handling unary move from."
);
handle_event( L, NULL );
}
}
/*
| Registers the inotify functions.
*/
extern void
register_inotify( lua_State *L )
2010-12-01 16:12:15 +00:00
{
2016-11-24 14:44:08 +00:00
lua_compat_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib );
}
/*
| Cleans up the inotify handling.
*/
static void
inotify_tidy( struct observance *obs )
2010-12-01 16:12:15 +00:00
{
if( obs->fd != inotify_fd )
{
logstring(
"Error",
"internal failure: inotify_fd != ob->fd"
);
exit( -1 );
}
close( inotify_fd );
free( readbuf );
readbuf = NULL;
}
/*
| Initalizes inotify handling
*/
extern void
open_inotify( lua_State *L )
2010-12-01 16:12:15 +00:00
{
if( readbuf )
{
logstring(
"Error",
"internal failure, inotify readbuf != NULL in open_inotify()"
)
exit(-1);
2010-11-22 21:17:08 +00:00
}
readbuf = s_malloc( readbuf_size );
inotify_fd = inotify_init( );
if( inotify_fd < 0 )
{
printlogf(
L,
"Error",
"Cannot access inotify monitor! ( %d : %s )",
errno, strerror(errno)
);
exit( -1 );
}
printlogf(
L, "Inotify",
"inotify fd = %d",
inotify_fd
);
close_exec_fd( inotify_fd );
non_block_fd( inotify_fd );
2018-03-09 14:41:16 +00:00
observe_fd( inotify_fd, inotify_ready, NULL, inotify_tidy, NULL );
}