/* PureAdmin
 * Copyright (C) 2003 Isak Savo
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Wrapper functions for FAM (File Alteration Monitor). Used to monitor changes in files and directories.
 *
 * Copyright (C) 2003-2004 Isak Savo
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <gtk/gtk.h>
#include <errno.h>
#include "globals.h"
#include "famwrap.h"

#ifdef HAVE_LIBFAM
#include <fam.h>
#include "globals.h"
#include "helper.h"

FAMConnection *famconn = NULL;
FAMRequest *req = NULL;
gint input_id = 0;
GIOChannel *iochannel = NULL;

void (*event_cb_func)(void) = NULL;

char *fam_event_names[] =
	{
	  "",
	  "FAMChanged", "FAMDeleted", "FAMStartExecuting", 
	  "FAMStopExecuting", "FAMCreated", "FAMMoved", 
	  "FAMAcknowledge", "FAMExists", "FAMEndExist"
	};

static gboolean fam_event_handler (GIOChannel *src, GIOCondition cond, gpointer data);

/* Only for internal use, connects to local FAM-service */
static
gboolean connect_to_fam ()
{
#ifdef HAVE_LIBFAM
	famconn = g_new0 (FAMConnection, 1);
	pur_log_dbg ("Opening FAM connection...");
	if (FAMOpen (famconn) != 0)
	{
		g_free (famconn);
		famconn = NULL;
		pur_log_wrn ("Unable to connect to FAM!");
		return FALSE;
	}
   
	return TRUE;
#else /* HAVE_LIBFAM */
	pur_log_dbg ("Can't connect to FAM: not supported!");
	return FALSE;
#endif /* HAVE_LIBFAM */
}


/*** Public Functions ***/

/* fam_add_watch: Adds a watch to a  file and calls a function when a change is noticed. 
 *  
 *  Args:
 *	fname - The name of the file that should be watched.
 *	func  - A pointer to the function that should be called. Function should be of type "void function (void)"
 *  Returns:
 *	TRUE if watch was added, else FALSE.
 */
gboolean fam_set_watch (const gchar *fname, void (*func))
{

	pur_log_dbg ("Adding watch: %s", fname);
	if (!fname || g_file_test (fname, G_FILE_TEST_EXISTS) == FALSE)
		return FALSE;
	
	/* Connect to FAM if not done before */
	if (famconn == NULL)
	{
		if (!connect_to_fam ())
			return FALSE;
		iochannel = g_io_channel_unix_new (FAMCONNECTION_GETFD(famconn));
		if (!iochannel)
		{
			pur_log_err ("Unable to create iochannel");
			return FALSE;
		}
		input_id = g_io_add_watch (iochannel, G_IO_IN | G_IO_HUP,
					   (GIOFunc) fam_event_handler, NULL);
		pur_log_dbg ("GIOChannel Input watch added, id=%d, fd=%d", input_id,
			     FAMCONNECTION_GETFD(famconn));
		g_io_channel_unref (iochannel); /* Remove our reference (add_watch() has one already) */
	}
	/* First time allocation */
	if (!req)
		req = g_new0 (FAMRequest, 1);
	
	if (FAMMonitorFile (famconn, fname, req, NULL) == -1)
	{
		g_free (req);
		req = NULL;
		pur_log_err ("FAMMonitorFile: %s", g_strerror (errno));
		return FALSE;
	}
	event_cb_func = func;
		
	return TRUE;
}

/* fam_suspend_watch: Suspends (temporary disables) a watch
 *
 *  Returns:
 *	TRUE if suspension was successful, otherwise FALSE.
 */
gboolean fam_suspend_watch (void)
{
	int res;
   
	if (!famconn || !req)
		return FALSE;
	
	res = FAMSuspendMonitor(famconn, req);
	if (res != 0)
		return FALSE; /* Error while suspending */
	
	return TRUE;

}

/* fam_resume_watch: Resumes a previously suspended watch
 *
 *  Returns:
 *	TRUE if the resume was successful, otherwise FALSE.
 */
gboolean fam_resume_watch (void)
{
	int res;

	if (!famconn || !req)
		return FALSE;
	
	res = FAMResumeMonitor(famconn, req);
	
	if (res != 0)
		return FALSE; /* Error while resuming */
	return TRUE;
   
}

/* fam_delete_watch: Cancels a watch. No more notifixations will be recieved for that file!
 *
 *  Returns:
 *	TRUE if the deletion was successful, otherwise FALSE.
 */
gboolean fam_delete_watch (void)
{
	int res;

	if (!famconn || !req)
		return FALSE;
	
	/*    g_print ("Removing watch: %s\n", fname); */
	res = FAMCancelMonitor (famconn, req);
	if (res != 0)
		return FALSE; /* Error while canceling */
	g_free (req);
	req = NULL;
	
	return TRUE;
}

/* fam_terminate: Removes all watches and terminates connection to local FAM service.
 *
 *  Returns:
 *	TRUE if all went well, otherwise FALSE.
 */
gboolean fam_terminate (void)
{
	gboolean ret = TRUE;

	if (!famconn)
		return TRUE; /* We consider termination a success if a connection was never established... */

	g_source_remove (input_id);
	input_id = 0;
	FAMClose (famconn);
	g_free (famconn);
	famconn = NULL;
	
	return ret;
}

/* fam_event_handler: Internal function, handles events and calls the
 * appropiate function depending on the filename */
static gboolean fam_event_handler (GIOChannel *src, GIOCondition cond, gpointer data)
{
	FAMEvent ev;

	while (FAMPending(famconn))
	{
		if (FAMNextEvent(famconn, &ev) != 1)
		{
			/*  Nuts!  An error!  Shut down & clean up.  */
			pur_log_err ("Error while receiving FAM-event! This shouldn't happen");
			fam_terminate ();
			return FALSE;
		}
		switch (ev.code)
		{
			case FAMExists:
			case FAMChanged:
			  /* Only call functions when file has changed or first time
			     watch is enabled */
				pur_log_dbg ("File %s changed, calling handler", ev.filename);
				if (event_cb_func)
					event_cb_func();
				break;
				
			case FAMDeleted:
				fam_delete_watch ();
				pur_log_dbg ("Removing watch [%s] (file deleted)\n", ev.filename);
				break;
			default:
				break;
		}
	}
	return TRUE;
}

#else /* HAVE_LIBFAM */

gboolean fam_add_watch (const gchar *fname, void (*func))
{
	return FALSE;
}

gboolean fam_suspend_watch (void)
{
	return FALSE;
}

gboolean fam_resume_watch (void)
{
	return FALSE;
}

gboolean fam_delete_watch (void)
{
	return TRUE;
}

gboolean fam_terminate (void)
{
	return TRUE;
}

#endif /* HAVE_LIBFAM */


syntax highlighted by Code2HTML, v. 0.9.1