Logo Search packages:      
Sourcecode: gamin version File versions  Download package

gam_poll_generic.c

/* Gamin
 * Copyright (C) 2003 James Willcox, Corey Bowers
 * Copyright (C) 2004 Daniel Veillard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "server_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <glib.h>
#include "fam.h"
#include "gam_error.h"
#include "gam_tree.h"
#include "gam_poll_generic.h"
#include "gam_event.h"
#include "gam_server.h"
#include "gam_protocol.h"
#include "gam_event.h"
#include "gam_excludes.h"

//#define VERBOSE_POLL
//#define VERBOSE_POLL2

#define DEFAULT_POLL_TIMEOUT 1

static GamTree *  tree = NULL;
static GList *          missing_resources = NULL;
static GList *          busy_resources = NULL;
static GList *          all_resources = NULL;
static GList *          dead_resources = NULL;
static time_t           current_time = 0;

gboolean
gam_poll_generic_init()
{
      tree = gam_tree_new ();
      gam_poll_generic_update_time ();
      return TRUE;
}

static void
gam_poll_debug_node(GamNode * node, gpointer user_data)
{
    if (node == NULL)
        return;

    GAM_DEBUG(DEBUG_INFO, "dir %d flags %d pflags %d nb subs %d : %s\n", node->is_dir, node->flags, node->pflags, g_list_length(node->subs), node->path);
}

void
gam_poll_generic_debug(void)
{
    if (missing_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll missing resources\n");
        g_list_foreach(missing_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll missing resources\n");
    }

    if (busy_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll busy resources\n");
        g_list_foreach(busy_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll busy resources\n");
    }

    if (all_resources != NULL) {
        GAM_DEBUG(DEBUG_INFO, "Dumping poll all resources\n");
        g_list_foreach(all_resources, (GFunc) gam_poll_debug_node, NULL);
    } else {
        GAM_DEBUG(DEBUG_INFO, "No poll all resources\n");
    }
}

/**
 * gam_poll_generic_add_missing:
 * @node: a missing node
 *
 * Add a missing node to the list for polling its creation.
 */
void
gam_poll_generic_add_missing(GamNode * node)
{
      if (g_list_find(missing_resources, node) == NULL) {
            missing_resources = g_list_prepend(missing_resources, node);
            GAM_DEBUG(DEBUG_INFO, "Poll: adding missing node %s\n", gam_node_get_path(node));
      }
}



/**
 * gam_poll_generic_remove_missing:
 * @node: a missing node
 *
 * Remove a missing node from the list.
 */
void
gam_poll_generic_remove_missing(GamNode * node)
{
      if (g_list_find (missing_resources, node))
      {
            GAM_DEBUG(DEBUG_INFO, "Poll: removing missing node %s\n", gam_node_get_path(node));
            missing_resources = g_list_remove_all(missing_resources, node);
      }
}

/**
 * gam_poll_generic_add_busy:
 * @node: a busy node
 *
 * Add a busy node to the list for polling its creation.
 */
void
gam_poll_generic_add_busy(GamNode * node)
{
      if (g_list_find(busy_resources, node) == NULL) {
            busy_resources = g_list_prepend(busy_resources, node);
            GAM_DEBUG(DEBUG_INFO, "Poll: adding busy node %s\n", gam_node_get_path(node));
      }
}

/**
 * gam_poll_generic_remove_busy:
 * @node: a busy node
 *
 * Remove a busy node from the list.
 */
void
gam_poll_generic_remove_busy(GamNode * node)
{
      if (!g_list_find (busy_resources, node))
            return;

      GAM_DEBUG(DEBUG_INFO, "Poll: removing busy node %s\n", gam_node_get_path(node));
      busy_resources = g_list_remove_all(busy_resources, node);
}

void
gam_poll_generic_add (GamNode * node)
{
      if (g_list_find (all_resources, node) == NULL)
      {
            all_resources = g_list_prepend(all_resources, node);
            GAM_DEBUG(DEBUG_INFO, "Poll: Adding node %s\n", gam_node_get_path (node));
      }
}

void
gam_poll_generic_remove (GamNode * node)
{
      g_assert (g_list_find (all_resources, node));
      GAM_DEBUG(DEBUG_INFO, "Poll: removing node %s\n", gam_node_get_path(node));
      all_resources = g_list_remove_all(all_resources, node);
}

time_t
gam_poll_generic_get_time()
{
      return current_time;
}

void
gam_poll_generic_update_time()
{
      current_time = time (NULL);
}

time_t
gam_poll_generic_get_delta_time(time_t pt)
{
      if (current_time >= pt)
            return current_time - pt;
      /* FIXME: We have wrapped */
      return 0;
}

static void
gam_poll_generic_trigger_file_handler (const char *path, pollHandlerMode mode, GamNode *node)
{
    if (node->mon_type != GFS_MT_KERNEL)
        return;

      if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
            if (gam_node_is_dir(node)) {
                  gam_kernel_file_handler (path, mode);
            } else {
                  const char *dir = NULL;
                  GamNode *parent = gam_node_parent(node);

                  if (!parent)
                        return;

                  dir = parent->path;
                  switch (mode) {
                  case GAMIN_ACTIVATE:
                        GAM_DEBUG(DEBUG_INFO, "poll: Activating kernel monitoring on %s\n", dir);
                        gam_kernel_dir_handler (dir, mode);
                  break;
                  case GAMIN_DEACTIVATE:
                        GAM_DEBUG(DEBUG_INFO, "poll: Deactivating kernel monitoring on %s\n", dir);
                        gam_kernel_dir_handler (dir, mode);
                  break;
                  case GAMIN_FLOWCONTROLSTART:
                        if (!gam_node_has_pflag (parent, MON_BUSY)) {
                              GAM_DEBUG(DEBUG_INFO, "poll: marking busy on %s\n", dir);
                              gam_kernel_dir_handler (dir, mode);
                              gam_poll_generic_add_busy(parent);
                              gam_node_set_pflag (parent, MON_BUSY);
                        }
                  break;
                  case GAMIN_FLOWCONTROLSTOP:
                        if (gam_node_has_pflag (parent, MON_BUSY)) {
                              GAM_DEBUG(DEBUG_INFO, "poll: unmarking busy on %s\n", dir);
                              gam_kernel_dir_handler (dir, mode);
                              gam_poll_generic_remove_busy(parent);
                              gam_node_unset_pflag (parent, MON_BUSY);
                        }
                  break;
                  }
            }
      } else {
            gam_kernel_file_handler (path, mode);
      }
}


static void
gam_poll_generic_trigger_dir_handler (const char *path, pollHandlerMode mode, GamNode *node)
{
      if (node->mon_type != GFS_MT_KERNEL)
            return;

      if (gam_server_get_kernel_handler() == GAMIN_K_DNOTIFY || gam_server_get_kernel_handler() == GAMIN_K_INOTIFY) {
            if (gam_node_is_dir(node)) {
                  gam_kernel_dir_handler (path, mode);
            } else {
                  gam_poll_generic_trigger_file_handler(path, mode, node);
            }
      } else {
            gam_kernel_dir_handler (path, mode);
      }
}


void
gam_poll_generic_trigger_handler(const char *path, pollHandlerMode mode, GamNode *node)
{
      if (gam_node_is_dir(node))
            gam_poll_generic_trigger_dir_handler(node->path, mode, node);
      else
            gam_poll_generic_trigger_file_handler(node->path, mode, node);
}

void
gam_poll_generic_scan_directory_internal (GamNode *dir_node)
{
      GDir *dir = NULL;
      const char *name = NULL, *dpath = NULL;
      char *path = NULL;
      GamNode *node = NULL;
      GaminEventType event = 0, fevent;
      GList *children = NULL, *l = NULL;
      unsigned int exists = 0;
      int is_dir_node;

      if (dir_node == NULL)
            return;

      dpath = gam_node_get_path(dir_node);

      if (dpath == NULL)
            return;

      if (!gam_node_get_subscriptions(dir_node))
            goto scan_files;

      if (dir_node->lasttime && gam_poll_generic_get_delta_time (dir_node->lasttime) < dir_node->poll_time)
            return;

      GAM_DEBUG(DEBUG_INFO, "poll-generic: scanning directory %s\n", dpath);

      event = gam_poll_file(dir_node);

      if (event != 0)
            gam_node_emit_event (dir_node, event);

      dir = g_dir_open(dpath, 0, NULL);

      if (dir == NULL) {
#ifdef VERBOSE_POLL
            GAM_DEBUG(DEBUG_INFO, "Poll: directory %s is not readable or missing\n", dpath);
#endif
            return;
      }

      exists = 1;

#ifdef VERBOSE_POLL
      GAM_DEBUG(DEBUG_INFO, "Poll: scanning directory %s\n", dpath);
#endif
      while ((name = g_dir_read_name(dir)) != NULL) {
            path = g_build_filename(gam_node_get_path(dir_node), name, NULL);
            node = gam_tree_get_at_path(tree, path);
            GAM_DEBUG(DEBUG_INFO, "poll-generic: scan dir - checking %s\n", dpath);

            if (!node) {
                  if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
                        node = gam_node_new(path, NULL, FALSE);
                        gam_tree_add(tree, dir_node, node);
                        gam_node_set_flag(node, FLAG_NEW_NODE);
                  } else {
                        node = gam_node_new(path, NULL, TRUE);
                        gam_tree_add(tree, dir_node, node);
                        gam_node_set_flag(node, FLAG_NEW_NODE);
                  }
            }

            g_free(path);
      }

      g_dir_close(dir);

scan_files:
      /* FIXME: Shouldn't is_dir_node be assigned inside the loop? */
      children = gam_tree_get_children(tree, dir_node);
      for (l = children; l; l = l->next) {
            node = (GamNode *) l->data;
            is_dir_node = gam_node_is_dir(dir_node);

            fevent = gam_poll_file(node);

            if (gam_node_has_flag(node, FLAG_NEW_NODE)) {
                  if (is_dir_node && gam_node_get_subscriptions(node)) {
                        gam_node_unset_flag(node, FLAG_NEW_NODE);
                        gam_poll_generic_scan_directory_internal(node);
                  } else {
                        gam_node_unset_flag(node, FLAG_NEW_NODE);
                        fevent = GAMIN_EVENT_CREATED;
                  }
            }

            if (fevent != 0) {
                  gam_node_emit_event (node, fevent);
            } else {
                  /* just send the EXIST events if the node exists */

                  if (!gam_node_has_pflag (node, MON_MISSING))
                  {
                        gam_server_emit_event(gam_node_get_path(node),
                        gam_node_is_dir(node),
                        GAMIN_EVENT_EXISTS, NULL, 0);
                  }
            }
      }

      g_list_free(children);
}

/**
 * Scans a directory for changes, and emits events if needed.
 *
 * @param path the path to the directory to be scanned
 */
void
gam_poll_generic_scan_directory(const char *path)
{
      GamNode *node;

      gam_poll_generic_update_time ();

      node = gam_tree_get_at_path(tree, path);
      if (node == NULL)
            node = gam_tree_add_at_path(tree, path, TRUE);

      if (node == NULL) {
            gam_error(DEBUG_INFO, "gam_tree_add_at_path(%s) returned NULL\n", path);
            return;
      }

      gam_poll_generic_scan_directory_internal(node);
}

/**
 * First dir scanning on a new subscription, generates the Exists EndExists
 * events.
 */
void
gam_poll_generic_first_scan_dir (GamSubscription * sub, GamNode * dir_node, const char *dpath)
{
      GDir *dir;
      char *path;
      GList *subs;
      int with_exists = 1;
      const char *name;
      GamNode *node;

      GAM_DEBUG(DEBUG_INFO, "Looking for existing files in: %s\n", dpath);

      if (gam_subscription_has_option(sub, GAM_OPT_NOEXISTS))
      {
            with_exists = 0;
            GAM_DEBUG(DEBUG_INFO, "   Exists not wanted\n");
      }

      subs = g_list_prepend(NULL, sub);

      if (!g_file_test(dpath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
            GAM_DEBUG(DEBUG_INFO, "Monitoring missing dir: %s\n", dpath);

            gam_server_emit_event(dpath, 1, GAMIN_EVENT_DELETED, subs, 1);

            stat(dir_node->path, &(dir_node->sbuf));
            dir_node->lasttime = gam_poll_generic_get_time ();

            if (g_file_test(dpath, G_FILE_TEST_EXISTS)) {
                  gam_node_set_pflags (dir_node, MON_WRONG_TYPE);
                  dir_node->is_dir = 0;
            } else {
                  gam_node_set_pflags (dir_node, MON_MISSING);
                  gam_poll_generic_add_missing(dir_node);
            }
            goto done;
      }

      if (dir_node->lasttime == 0)
            gam_poll_file(dir_node);

      if (with_exists)
            gam_server_emit_event(dpath, 1, GAMIN_EVENT_EXISTS, subs, 1);


      dir = g_dir_open(dpath, 0, NULL);

      if (dir == NULL) {
            goto done;
      }

      while ((name = g_dir_read_name(dir)) != NULL)
      {
            path = g_build_filename(dpath, name, NULL);

            node = gam_tree_get_at_path(tree, path);

            if (!node)
            {
                  GAM_DEBUG(DEBUG_INFO, "Unregistered node %s\n", path);
                  if (!g_file_test(path, G_FILE_TEST_IS_DIR)) {
                        node = gam_node_new(path, NULL, FALSE);
                  } else {
                        node = gam_node_new(path, NULL, TRUE);
                  }
                  stat(node->path, &(node->sbuf));
                  gam_node_set_is_dir(node, (S_ISDIR(node->sbuf.st_mode) != 0));

                  if (gam_exclude_check(path) || gam_fs_get_mon_type(path) != GFS_MT_KERNEL)
                        gam_node_set_pflag (node, MON_NOKERNEL);

                  node->lasttime = gam_poll_generic_get_time ();
                  gam_tree_add(tree, dir_node, node);
            }

            if (with_exists)
                  gam_server_emit_event(name, 1, GAMIN_EVENT_EXISTS, subs, 1);

            g_free(path);
      }

      g_dir_close(dir);

done:
      if (with_exists)
            gam_server_emit_event(dpath, 1, GAMIN_EVENT_ENDEXISTS, subs, 1);

      g_list_free(subs);

      GAM_DEBUG(DEBUG_INFO, "Done scanning %s\n", dpath);
}

GamTree *
gam_poll_generic_get_tree()
{
      return tree;
}

GList *
gam_poll_generic_get_missing_list (void)
{
      return missing_resources;
}

GList *
gam_poll_generic_get_busy_list (void)
{
      return busy_resources;
}

GList *
gam_poll_generic_get_all_list (void)
{
      return all_resources;
}

GList *
gam_poll_generic_get_dead_list (void)
{
      return dead_resources;
}

void
gam_poll_generic_unregister_node (GamNode * node)
{
      if (missing_resources != NULL) {
            gam_poll_generic_remove_missing(node);
      }

      if (busy_resources != NULL) {
            gam_poll_generic_remove_busy(node);
      }

      if (all_resources != NULL) {
            all_resources = g_list_remove(all_resources, node);
      }
}

void
gam_poll_generic_prune_tree(GamNode * node)
{
      /* don't prune the root */
      if (gam_node_parent(node) == NULL)
            return;

      if (!gam_tree_has_children(tree, node) && !gam_node_get_subscriptions(node)) 
      {
            GamNode *parent;

            GAM_DEBUG(DEBUG_INFO, "prune_tree: %s\n", gam_node_get_path(node));

            parent = gam_node_parent(node);
            gam_poll_generic_unregister_node(node);
            gam_tree_remove(tree, node);
            gam_poll_generic_prune_tree(parent);
      }
}


Generated by  Doxygen 1.6.0   Back to index