#include <unistd.h>
#include <string.h>
#include <sys/inotify.h>
#include <glib.h>
#include <signal.h>
#include <glib-object.h>


static int ifd = -1, wd = -1;

static gboolean
stuff_changed (GIOChannel *channel, GIOCondition cond, gpointer user_data)
{
	const char *path = (const char *) user_data;
	struct inotify_event evt;

	/* read the notifications from the watch descriptor */
	while (g_io_channel_read_chars (channel, (gchar *) &evt, sizeof (struct inotify_event), NULL, NULL) == G_IO_STATUS_NORMAL) {
		gchar filename[PATH_MAX + 1];

		filename[0] = '\0';
		if (evt.len > 0) {
			g_io_channel_read_chars (channel,
			                        filename,
			                        evt.len > PATH_MAX ? PATH_MAX : evt.len,
			                        NULL, NULL);
		}

		g_message ("Event: wd %d, filename %s, mask 0x%X", evt.wd, strlen (filename) ? filename : path, evt.mask);
	}

	return TRUE;
}

static gboolean
init_inotify (const char *path)
{
	GIOChannel *channel;
	guint source_id;

	ifd = inotify_init ();
	if (ifd == -1) {
		g_warning ("Couldn't initialize inotify");
		return FALSE;
	}

	wd = inotify_add_watch (ifd, path, IN_ALL_EVENTS);
	if (wd == -1) {
		g_warning ("Couldn't watch %s", path);
		close (ifd);
		return FALSE;
	}

	/* Watch the inotify descriptor for file/directory change events */
	channel = g_io_channel_unix_new (ifd);
	if (!channel) {
		g_warning ("Couldn't create new GIOChannel to watch %s", path);
		close (ifd);
		return FALSE;
	}
	g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
	g_io_channel_set_encoding (channel, NULL, NULL); 

	source_id = g_io_add_watch (channel,
	                            G_IO_IN | G_IO_ERR,
	                            (GIOFunc) stuff_changed,
	                            (gpointer) path);
	g_io_channel_unref (channel);

	return TRUE;
}

static GMainLoop *loop = NULL;

static void
signal_handler (int signo)
{
	if (signo == SIGINT || signo == SIGTERM) {
		g_message ("Caught signal %d, shutting down...", signo);
		g_main_loop_quit (loop);
	}
}

static void
setup_signals (void)
{
	struct sigaction action;
	sigset_t mask;

	sigemptyset (&mask);
	action.sa_handler = signal_handler;
	action.sa_mask = mask;
	action.sa_flags = 0;
	sigaction (SIGTERM,  &action, NULL);
	sigaction (SIGINT,  &action, NULL);
}

int main (int argc, char **argv)
{
	g_type_init ();

	if (argc != 2) {
		g_warning ("Bad arguments: need a file to watch");
		return 1;
	}

	if (!init_inotify (argv[1]))
		return 1;

	loop = g_main_loop_new (NULL, FALSE);
	setup_signals ();
	g_main_loop_run (loop);

	close (ifd);
	return 0;
}

