|
|
static GamTree *tree = NULL; | static GamTree *tree = NULL; |
static GList *new_subs = NULL; | static GList *new_subs = NULL; |
static GList *missing_resources = NULL; | static GList *missing_resources = NULL; |
|
static GList *busy_resources = NULL; |
static GList *all_resources = NULL; | static GList *all_resources = NULL; |
static GamPollHandler dir_handler = NULL; | static GamPollHandler dir_handler = NULL; |
static GamPollHandler file_handler = NULL; | static GamPollHandler file_handler = NULL; |
|
|
return(errno); | return(errno); |
} | } |
| |
|
/** |
|
* gam_poll_add_missing: |
|
* @node: a missing node |
|
* |
|
* Add a missing node to the list for polling its creation. |
|
*/ |
|
static void |
|
gam_poll_add_missing(GamNode *node) { |
|
GAM_DEBUG(DEBUG_INFO, "Poll adding missing node %s\n", |
|
gam_node_get_path(node)); |
|
if (g_list_find(missing_resources, node) == NULL) { |
|
missing_resources = g_list_prepend(missing_resources, node); |
|
} else { |
|
GAM_DEBUG(DEBUG_INFO, " already registered\n"); |
|
} |
|
} |
|
|
|
/** |
|
* gam_poll_remove_missing: |
|
* @node: a missing node |
|
* |
|
* Remove a missing node from the list. |
|
*/ |
|
static void |
|
gam_poll_remove_missing(GamNode *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_add_busy: |
|
* @node: a busy node |
|
* |
|
* Add a busy node to the list for polling its creation. |
|
*/ |
|
static void |
|
gam_poll_add_busy(GamNode *node) { |
|
GAM_DEBUG(DEBUG_INFO, "Poll adding busy node %s\n", |
|
gam_node_get_path(node)); |
|
if (g_list_find(busy_resources, node) == NULL) { |
|
busy_resources = g_list_prepend(busy_resources, node); |
|
} else { |
|
GAM_DEBUG(DEBUG_INFO, " already registered\n"); |
|
} |
|
} |
|
|
|
/** |
|
* gam_poll_remove_busy: |
|
* @node: a busy node |
|
* |
|
* Remove a busy node from the list. |
|
*/ |
|
static void |
|
gam_poll_remove_busy(GamNode *node) { |
|
GAM_DEBUG(DEBUG_INFO, "Poll removing busy node %s\n", |
|
gam_node_get_path(node)); |
|
busy_resources = g_list_remove_all(busy_resources, node); |
|
} |
|
|
|
/** |
|
* trigger_dir_handler: |
|
* @path: path to the directory |
|
* @mode: type of kernel monitoring action |
|
* |
|
* Interface to the kernel monitoring layer for directories |
|
*/ |
static void | static void |
trigger_dir_handler(const char *path, gboolean added) |
trigger_dir_handler(const char *path, pollHandlerMode mode) |
{ | { |
if (dir_handler != NULL) | if (dir_handler != NULL) |
(*dir_handler) (path, added); |
(*dir_handler) (path, mode); |
} | } |
| |
|
/** |
|
* trigger_file_handler: |
|
* @path: path to the file |
|
* @mode: type of kernel monitoring action |
|
* |
|
* Interface to the kernel monitoring layer for files |
|
*/ |
static void | static void |
trigger_file_handler(const char *path, gboolean added) |
trigger_file_handler(const char *path, pollHandlerMode mode) |
{ | { |
if (file_handler != NULL) | if (file_handler != NULL) |
(*file_handler) (path, added); |
(*file_handler) (path, mode); |
} | } |
| |
|
/** |
|
* node_add_subscription: |
|
* @node: the node tree pointer |
|
* @sub: the pointer to the subscription |
|
* |
|
* register a subscription for this node |
|
* |
|
* Returns 0 in case of success and -1 in case of failure |
|
*/ |
|
|
static int | static int |
node_add_subscription(GamNode * node, GamSubscription * sub) | node_add_subscription(GamNode * node, GamSubscription * sub) |
{ | { |
|
|
} | } |
| |
if (gam_node_is_dir(node)) | if (gam_node_is_dir(node)) |
trigger_dir_handler(node->path, TRUE); |
trigger_dir_handler(node->path, GAMIN_ACTIVATE); |
else | else |
trigger_file_handler(node->path, TRUE); |
trigger_file_handler(node->path, GAMIN_ACTIVATE); |
| |
return(0); | return(0); |
} | } |
| |
|
/** |
|
* node_remove_subscription: |
|
* @node: the node tree pointer |
|
* @sub: the pointer to the subscription |
|
* |
|
* Removes a subscription for this node |
|
* |
|
* Returns 0 in case of success and -1 in case of failure |
|
*/ |
|
|
static int | static int |
node_remove_subscription(GamNode * node, GamSubscription * sub) | node_remove_subscription(GamNode * node, GamSubscription * sub) |
{ | { |
|
|
/* DNotify makes our life miserable here */ | /* DNotify makes our life miserable here */ |
if (gam_subscription_is_dir(sub)) { | if (gam_subscription_is_dir(sub)) { |
if (gam_node_is_dir(node)) | if (gam_node_is_dir(node)) |
trigger_dir_handler(path, FALSE); |
trigger_dir_handler(path, GAMIN_DESACTIVATE); |
else { | else { |
char *dir; | char *dir; |
| |
dir = g_path_get_dirname(path); | dir = g_path_get_dirname(path); |
trigger_file_handler(dir, FALSE); |
trigger_file_handler(dir, GAMIN_DESACTIVATE); |
g_free(dir); | g_free(dir); |
} | } |
} else { | } else { |
if (gam_node_is_dir(node)) | if (gam_node_is_dir(node)) |
trigger_dir_handler(path, FALSE); |
trigger_dir_handler(path, GAMIN_DESACTIVATE); |
else | else |
trigger_file_handler(path, FALSE); |
trigger_file_handler(path, GAMIN_DESACTIVATE); |
} | } |
| |
return(0); | return(0); |
} | } |
| |
|
/** |
|
* gam_poll_data_new: |
|
* @path: the path |
|
* |
|
* Creates a new data block for that path |
|
* |
|
* Returns the pointer to the block or NULL in case of failure |
|
*/ |
|
|
static GamPollData * | static GamPollData * |
gam_poll_data_new(const char *path) | gam_poll_data_new(const char *path) |
{ | { |
|
|
return data; | return data; |
} | } |
| |
|
/** |
|
* gam_poll_data_destroy: |
|
* @data: pointer to the data block |
|
* |
|
* Destroys a data block. |
|
*/ |
|
|
|
static void |
|
gam_poll_data_destroy(GamPollData * data) |
|
{ |
|
if (data != NULL) { |
|
if (data->path != NULL) |
|
g_free(data->path); |
|
g_free(data); |
|
} |
|
} |
|
|
static void | static void |
gam_poll_emit_event(GamNode * node, GaminEventType event, |
gam_poll_emit_event(GamNode * node, GaminEventType event) |
GList * exist_subs) |
|
{ | { |
GList *l; | GList *l; |
GamNode *parent; | GamNode *parent; |
GamPollData *data; |
|
GList *subs; | GList *subs; |
int is_dir_node = gam_node_is_dir(node); | int is_dir_node = gam_node_is_dir(node); |
| |
|
|
} | } |
} | } |
| |
if (exist_subs) { |
gam_server_emit_event(gam_node_get_path(node), is_dir_node, event, subs, 0); |
|
|
data = gam_node_get_data(node); |
|
|
|
for (l = subs; l; l = l->next) { |
|
GamSubscription *sub = l->data; |
|
GaminEventType new_event = event; |
|
GList *tmp; |
|
|
|
if (g_list_find(exist_subs, sub)) { |
|
if ((data) && (!(data->flags & MON_MISSING))) |
|
new_event = GAMIN_EVENT_EXISTS; |
|
else |
|
continue; |
|
} |
|
|
|
tmp = g_list_prepend(NULL, sub); |
|
gam_server_emit_event(gam_node_get_path(node), is_dir_node, |
|
new_event, tmp, 0); |
|
g_list_free(tmp); |
|
|
|
} |
|
} else { |
|
|
|
gam_server_emit_event(gam_node_get_path(node), is_dir_node, event, |
|
subs, 0); |
|
} |
|
| |
g_list_free(subs); | g_list_free(subs); |
} | } |
| |
static void |
|
gam_poll_data_destroy(GamPollData * data) |
|
{ |
|
g_free(data->path); |
|
g_free(data); |
|
} |
|
|
|
/** | /** |
* gam_poll_delist_node: | * gam_poll_delist_node: |
* @node: the node to delist | * @node: the node to delist |
|
|
static void | static void |
gam_poll_delist_node(GamNode * node) { | gam_poll_delist_node(GamNode * node) { |
GList *subs; | GList *subs; |
|
const char *path; |
| |
GAM_DEBUG(DEBUG_INFO, "gam_poll_delist_node %s\n", |
path = gam_node_get_path(node); |
gam_node_get_path(node)); |
GAM_DEBUG(DEBUG_INFO, "gam_poll_delist_node %s\n", path); |
|
|
|
if (gam_exclude_check(path)) |
|
return; |
subs = gam_node_get_subscriptions(node); | subs = gam_node_get_subscriptions(node); |
| |
while (subs != NULL) { | while (subs != NULL) { |
if (gam_node_is_dir(node)) | if (gam_node_is_dir(node)) |
trigger_dir_handler(gam_node_get_path(node), FALSE); |
trigger_dir_handler(path, GAMIN_DESACTIVATE); |
else | else |
trigger_file_handler(gam_node_get_path(node), FALSE); |
trigger_file_handler(path, GAMIN_DESACTIVATE); |
subs = subs->next; | subs = subs->next; |
} | } |
} | } |
|
|
static void | static void |
gam_poll_relist_node(GamNode * node) { | gam_poll_relist_node(GamNode * node) { |
GList *subs; | GList *subs; |
|
const char *path; |
| |
GAM_DEBUG(DEBUG_INFO, "gam_poll_relist_node %s\n", |
path = gam_node_get_path(node); |
gam_node_get_path(node)); |
GAM_DEBUG(DEBUG_INFO, "gam_poll_relist_node %s\n", path); |
|
|
|
if (gam_exclude_check(path)) |
|
return; |
subs = gam_node_get_subscriptions(node); | subs = gam_node_get_subscriptions(node); |
| |
while (subs != NULL) { | while (subs != NULL) { |
if (gam_node_is_dir(node)) | if (gam_node_is_dir(node)) |
trigger_dir_handler(gam_node_get_path(node), TRUE); |
trigger_dir_handler(path, GAMIN_ACTIVATE); |
else | else |
trigger_file_handler(gam_node_get_path(node), TRUE); |
trigger_file_handler(path, GAMIN_ACTIVATE); |
subs = subs->next; | subs = subs->next; |
} | } |
} | } |
| |
|
/** |
|
* gam_poll_flowon_node: |
|
* @node: the node to delist |
|
* |
|
* This function is called when kernel monitoring flow control for a |
|
* node should be started |
|
*/ |
|
static void |
|
gam_poll_flowon_node(GamNode * node) { |
|
const char *path; |
|
|
|
path = gam_node_get_path(node); |
|
GAM_DEBUG(DEBUG_INFO, "gam_poll_flowon_node %s\n",path); |
|
|
|
if (gam_exclude_check(path)) |
|
return; |
|
|
|
if (gam_node_is_dir(node)) |
|
trigger_dir_handler(path, GAMIN_FLOWCONTROLSTART); |
|
else |
|
trigger_file_handler(path, GAMIN_FLOWCONTROLSTART); |
|
} |
|
|
|
/** |
|
* gam_poll_flowoff_node: |
|
* @node: the node to delist |
|
* |
|
* This function is called when kernel monitoring flow control for a |
|
* node should be started |
|
*/ |
|
static void |
|
gam_poll_flowoff_node(GamNode * node) { |
|
const char *path; |
|
|
|
path = gam_node_get_path(node); |
|
GAM_DEBUG(DEBUG_INFO, "gam_poll_flowoff_node %s\n", path); |
|
if (gam_exclude_check(path)) |
|
return; |
|
|
|
if (gam_node_is_dir(node)) |
|
trigger_dir_handler(path, GAMIN_FLOWCONTROLSTOP); |
|
else |
|
trigger_file_handler(path, GAMIN_FLOWCONTROLSTOP); |
|
} |
| |
static GaminEventType | static GaminEventType |
poll_file(GamNode * node) | poll_file(GamNode * node) |
|
|
/* deleted */ | /* deleted */ |
data->flags = MON_MISSING; | data->flags = MON_MISSING; |
| |
|
gam_poll_remove_busy(node); |
if (gam_node_get_subscriptions(node) != NULL) { | if (gam_node_get_subscriptions(node) != NULL) { |
gam_poll_delist_node(node); | gam_poll_delist_node(node); |
gam_poll_add_missing(node); | gam_poll_add_missing(node); |
|
|
| |
/* | /* |
* load control, switch back to poll on very busy resources | * load control, switch back to poll on very busy resources |
* and back when no update has happened in 10 seconds |
* and back when no update has happened in 5 seconds |
*/ | */ |
if (current_time == data->lasttime) { | if (current_time == data->lasttime) { |
if (!(data->flags & MON_BUSY)) { | if (!(data->flags & MON_BUSY)) { |
|
|
} | } |
| |
if ((data->checks >= 4) && (!(data->flags & MON_BUSY))) { | if ((data->checks >= 4) && (!(data->flags & MON_BUSY))) { |
if (gam_node_get_subscriptions(node) != NULL) { |
if ((gam_node_get_subscriptions(node) != NULL) && |
|
(!gam_exclude_check(data->path))) { |
GAM_DEBUG(DEBUG_INFO, "switching %s back to polling\n", path); | GAM_DEBUG(DEBUG_INFO, "switching %s back to polling\n", path); |
data->flags |= MON_BUSY; | data->flags |= MON_BUSY; |
data->checks = 0; | data->checks = 0; |
gam_poll_add_missing(node); |
gam_poll_add_busy(node); |
gam_poll_delist_node(node); |
gam_poll_flowon_node(node); |
|
/* |
|
* DNotify can be nasty here, we will miss events for parent dir |
|
* if we are not careful about it |
|
*/ |
|
if (!gam_node_is_dir(node)) { |
|
GamNode *parent = gam_node_parent(node); |
|
|
|
if ((parent != NULL) && |
|
(gam_node_get_subscriptions(parent) != NULL)) { |
|
gam_poll_add_busy(parent); |
|
/* gam_poll_flowon_node(parent); */ |
|
} |
|
} |
} | } |
} | } |
| |
if ((event == 0) && (data->flags & MON_BUSY) && (data->checks > 10)) { |
if ((event == 0) && (data->flags & MON_BUSY) && (data->checks > 5)) { |
if ((gam_node_get_subscriptions(node) != NULL) && | if ((gam_node_get_subscriptions(node) != NULL) && |
(!gam_exclude_check(data->path))) { | (!gam_exclude_check(data->path))) { |
GAM_DEBUG(DEBUG_INFO, "switching %s back to kernel monitoring\n", | GAM_DEBUG(DEBUG_INFO, "switching %s back to kernel monitoring\n", |
path); | path); |
data->flags &= ~MON_BUSY; | data->flags &= ~MON_BUSY; |
data->checks = 0; | data->checks = 0; |
gam_poll_remove_missing(node); |
gam_poll_remove_busy(node); |
gam_poll_relist_node(node); |
gam_poll_flowoff_node(node); |
} | } |
} | } |
| |
|
|
event = poll_file(dir_node); | event = poll_file(dir_node); |
| |
if (event != 0) | if (event != 0) |
gam_poll_emit_event(dir_node, event, NULL); |
gam_poll_emit_event(dir_node, event); |
| |
dir = g_dir_open(dpath, 0, NULL); | dir = g_dir_open(dpath, 0, NULL); |
| |
|
|
} | } |
| |
if (fevent != 0) { | if (fevent != 0) { |
gam_poll_emit_event(node, fevent, NULL); |
gam_poll_emit_event(node, fevent); |
} else { | } else { |
GamPollData *data; | GamPollData *data; |
| |
|
|
if (missing_resources != NULL) { | if (missing_resources != NULL) { |
gam_poll_remove_missing (child); | gam_poll_remove_missing (child); |
} | } |
|
if (busy_resources != NULL) { |
|
gam_poll_remove_busy (child); |
|
} |
gam_tree_remove(tree, child); | gam_tree_remove(tree, child); |
} else { | } else { |
remove_dir = FALSE; | remove_dir = FALSE; |
|
|
static int in_poll_callback = 0; | static int in_poll_callback = 0; |
| |
#ifdef VERBOSE_POLL | #ifdef VERBOSE_POLL |
GAM_DEBUG(DEBUG_INFO, "gam_poll_scan_callback(): %d, %d items\n", |
GAM_DEBUG(DEBUG_INFO, "gam_poll_scan_callback(): %d, %d missing, %d busy\n", |
in_poll_callback, g_list_length(missing_resources)); |
in_poll_callback, g_list_length(missing_resources), |
|
g_list_length(busy_resources)); |
#endif | #endif |
if (in_poll_callback) | if (in_poll_callback) |
return(TRUE); | return(TRUE); |
|
|
GaminEventType event; | GaminEventType event; |
| |
event = poll_file(node); | event = poll_file(node); |
gam_poll_emit_event(node, event, NULL); |
gam_poll_emit_event(node, event); |
} | } |
| |
/* | /* |
|
|
} | } |
} | } |
| |
|
for (idx = 0;;idx++) { |
|
GamPollData *data; |
|
GamNode *node; |
|
|
|
/* |
|
* do not simply walk the list as it may be modified in the callback |
|
*/ |
|
node = (GamNode *) g_list_nth_data(busy_resources, idx); |
|
|
|
if (node == NULL) { |
|
#ifdef VERBOSE_POLL |
|
GAM_DEBUG(DEBUG_INFO, " node %d == NULL\n", idx); |
|
#endif |
|
break; |
|
} |
|
data = gam_node_get_data(node); |
|
if (data == NULL) { |
|
#ifdef VERBOSE_POLL |
|
GAM_DEBUG(DEBUG_INFO, " data %d == NULL\n", idx); |
|
#endif |
|
break; |
|
} |
|
|
|
#ifdef VERBOSE_POLL |
|
GAM_DEBUG(DEBUG_INFO, "Checking busy file %s", data->path); |
|
#endif |
|
if (node->is_dir) { |
|
gam_poll_scan_directory_internal(node); |
|
} else { |
|
GaminEventType event; |
|
|
|
event = poll_file(node); |
|
gam_poll_emit_event(node, event); |
|
} |
|
|
|
/* |
|
* if the resource exists again and is not in a special monitoring |
|
* mode then switch back to dnotify for monitoring. |
|
*/ |
|
if ((data->flags == 0) && (!gam_exclude_check(data->path))) { |
|
gam_poll_remove_busy(node); |
|
if (gam_node_get_subscriptions(node) != NULL) { |
|
gam_poll_flowoff_node(node); |
|
} |
|
} |
|
} |
in_poll_callback = 0; | in_poll_callback = 0; |
return(TRUE); | return(TRUE); |
} | } |
|
|
| |
parent = gam_node_parent(node); | parent = gam_node_parent(node); |
if (missing_resources != NULL) { | if (missing_resources != NULL) { |
gam_poll_remove_missing(node); |
gam_poll_remove_missing(node); |
|
} |
|
if (busy_resources != NULL) { |
|
gam_poll_remove_busy(node); |
} | } |
if (all_resources != NULL) { | if (all_resources != NULL) { |
all_resources = g_list_remove (all_resources, node); |
all_resources = g_list_remove (all_resources, node); |
} | } |
gam_tree_remove(tree, node); | gam_tree_remove(tree, node); |
prune_tree(parent); | prune_tree(parent); |
|
|
return(TRUE); | return(TRUE); |
} | } |
| |
|
|
/** |
|
* @defgroup Polling Polling Backend |
|
* @ingroup Backends |
|
* @brief Polling backend API |
|
* |
|
* This is the default backend used in Gamin. It basically just calls |
|
* stat() on files/directories every so often to see when things change. The |
|
* statting happens in a separate thread, controllable with arguments to |
|
* #gam_poll_init_full(). |
|
* |
|
* @{ |
|
*/ |
|
|
|
|
|
/** |
|
* gam_poll_add_missing: |
|
* @node: a missing node |
|
* |
|
* Add a missing node to the list for polling its creation. |
|
*/ |
|
void |
|
gam_poll_add_missing(GamNode *node) { |
|
#if 0 |
|
fprintf(stderr, "Adding %s to polling\n", gam_node_get_path(node)); |
|
#endif |
|
GAM_DEBUG(DEBUG_INFO, "Poll adding missing node %s\n", |
|
gam_node_get_path(node)); |
|
if (g_list_find(missing_resources, node) == NULL) { |
|
missing_resources = g_list_prepend(missing_resources, node); |
|
} else { |
|
GAM_DEBUG(DEBUG_INFO, " already registered\n"); |
|
} |
|
} |
|
|
|
/** |
|
* gam_poll_remove_missing: |
|
* @node: a missing node |
|
* |
|
* Remove a missing node from the list. |
|
*/ |
|
void |
|
gam_poll_remove_missing(GamNode *node) { |
|
#if 0 |
|
fprintf(stderr, "Removing %s from polling\n", gam_node_get_path(node)); |
|
#endif |
|
GAM_DEBUG(DEBUG_INFO, "Poll removing missing node %s\n", |
|
gam_node_get_path(node)); |
|
missing_resources = g_list_remove_all(missing_resources, node); |
|
} |
|
|
|
/** | /** |
* Initializes the polling system. This must be called before | * Initializes the polling system. This must be called before |
* any other functions in this module. | * any other functions in this module. |
|
|
if (missing_resources != NULL) { | if (missing_resources != NULL) { |
gam_poll_remove_missing(node); | gam_poll_remove_missing(node); |
} | } |
|
if (busy_resources != NULL) { |
|
gam_poll_remove_busy(node); |
|
} |
if (all_resources != NULL) { | if (all_resources != NULL) { |
all_resources = g_list_remove(all_resources, node); | all_resources = g_list_remove(all_resources, node); |
} | } |
|
|
if (missing_resources != NULL) { | if (missing_resources != NULL) { |
gam_poll_remove_missing(node); | gam_poll_remove_missing(node); |
} | } |
|
if (busy_resources != NULL) { |
|
gam_poll_remove_busy(node); |
|
} |
if (all_resources != NULL) { | if (all_resources != NULL) { |
all_resources = g_list_remove(all_resources, node); | all_resources = g_list_remove(all_resources, node); |
} | } |