/* 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 #endif #include #include #include #include "globals.h" #include "famwrap.h" #ifdef HAVE_LIBFAM #include #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 */