From db2a311c9f4c77fec8f7baca6cd075ebf4806832 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 2 Nov 2012 17:26:12 +0100 Subject: [PATCH] inotify: Move inotify code into its own file The inotify code will be used by the core (config.c) and the session policy plugin. We introduce a new API for file modifcation notifcation. We move the factored out code part from the last patch into a new file and also change the inotify code so that it allows to monitor not only STORAGEDIR. When registering a new observer, the callee has to tell which directory should be watched. inotify.c will group the observers together. --- Makefile.am | 6 +- include/inotify.h | 37 +++++++++ src/config.c | 122 +--------------------------- src/connman.h | 5 ++ src/inotify.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 2 + 6 files changed, 286 insertions(+), 122 deletions(-) create mode 100644 include/inotify.h create mode 100644 src/inotify.c diff --git a/Makefile.am b/Makefile.am index 21d820e..bd130c3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,8 @@ include_HEADERS = include/types.h include/log.h include/plugin.h \ include/resolver.h include/ipconfig.h \ include/device.h include/network.h include/inet.h \ include/storage.h include/provision.h \ - include/session.h include/ipaddress.h include/agent.h + include/session.h include/ipaddress.h include/agent.h \ + include/inotify.h nodist_include_HEADERS = include/version.h @@ -95,7 +96,8 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources) $(gweb_sources) \ src/technology.c src/counter.c src/ntp.c \ src/session.c src/tethering.c src/wpad.c src/wispr.c \ src/stats.c src/iptables.c src/dnsproxy.c src/6to4.c \ - src/ippool.c src/bridge.c src/nat.c src/ipaddress.c + src/ippool.c src/bridge.c src/nat.c src/ipaddress.c \ + src/inotify.c src_connmand_LDADD = $(builtin_libadd) @GLIB_LIBS@ @DBUS_LIBS@ \ @XTABLES_LIBS@ @GNUTLS_LIBS@ -lresolv -ldl -lrt diff --git a/include/inotify.h b/include/inotify.h new file mode 100644 index 0000000..1f642ab --- /dev/null +++ b/include/inotify.h @@ -0,0 +1,37 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __CONNMAN_INOTIFY_H +#define __CONNMAN_INOTIFY_H + +struct inotify_event; + +typedef void (* inotify_event_cb) (struct inotify_event *event, + const char *ident); + +int connman_inotify_register(const char *path, inotify_event_cb callback); +void connman_inotify_unregister(const char *path, inotify_event_cb callback); + +#ifdef __cplusplus +} +#endif + +#endif /* __CONNMAN_INOTIFY_H */ diff --git a/src/config.c b/src/config.c index 16fed8d..6a5b12c 100644 --- a/src/config.c +++ b/src/config.c @@ -66,10 +66,6 @@ struct connman_config { static GHashTable *config_table = NULL; static GSList *protected_services = NULL; -static int inotify_wd = -1; - -static GIOChannel *inotify_channel = NULL; -static uint inotify_watch = 0; static connman_bool_t cleanup = FALSE; #define INTERNAL_CONFIG_PREFIX "__internal" @@ -613,120 +609,6 @@ static void config_notify_handler(struct inotify_event *event, g_hash_table_remove(config_table, ident); } -static gboolean inotify_data(GIOChannel *channel, GIOCondition cond, - gpointer user_data) -{ - char buffer[256]; - char *next_event; - gsize bytes_read; - GIOStatus status; - - if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { - inotify_watch = 0; - return FALSE; - } - - status = g_io_channel_read_chars(channel, buffer, - sizeof(buffer) -1, &bytes_read, NULL); - - switch (status) { - case G_IO_STATUS_NORMAL: - break; - case G_IO_STATUS_AGAIN: - return TRUE; - default: - connman_error("Reading from inotify channel failed"); - inotify_watch = 0; - return FALSE; - } - - next_event = buffer; - - while (bytes_read > 0) { - struct inotify_event *event; - gchar *ident; - gsize len; - - event = (struct inotify_event *) next_event; - if (event->len) - ident = next_event + sizeof(struct inotify_event); - else - ident = NULL; - - len = sizeof(struct inotify_event) + event->len; - - /* check if inotify_event block fit */ - if (len > bytes_read) - break; - - next_event += len; - bytes_read -= len; - - config_notify_handler(event, ident); - } - - return TRUE; -} - -static int create_watch(void) -{ - int fd; - - fd = inotify_init(); - if (fd < 0) - return -EIO; - - inotify_wd = inotify_add_watch(fd, STORAGEDIR, - IN_MODIFY | IN_CREATE | IN_DELETE); - if (inotify_wd < 0) { - connman_error("Creation of STORAGEDIR watch failed"); - close(fd); - return -EIO; - } - - inotify_channel = g_io_channel_unix_new(fd); - if (inotify_channel == NULL) { - connman_error("Creation of inotify channel failed"); - inotify_rm_watch(fd, inotify_wd); - inotify_wd = 0; - - close(fd); - return -EIO; - } - - g_io_channel_set_close_on_unref(inotify_channel, TRUE); - g_io_channel_set_encoding(inotify_channel, NULL, NULL); - g_io_channel_set_buffered(inotify_channel, FALSE); - - inotify_watch = g_io_add_watch(inotify_channel, - G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, - inotify_data, NULL); - - return 0; -} - -static void remove_watch(void) -{ - int fd; - - if (inotify_channel == NULL) - return; - - if (inotify_watch > 0) { - g_source_remove(inotify_watch); - inotify_watch = 0; - } - - fd = g_io_channel_unix_get_fd(inotify_channel); - - if (inotify_wd >= 0) { - inotify_rm_watch(fd, inotify_wd); - inotify_wd = 0; - } - - g_io_channel_unref(inotify_channel); -} - int __connman_config_init(void) { DBG(""); @@ -734,7 +616,7 @@ int __connman_config_init(void) config_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, unregister_config); - create_watch(); + connman_inotify_register(STORAGEDIR, config_notify_handler); return read_configs(); } @@ -745,7 +627,7 @@ void __connman_config_cleanup(void) cleanup = TRUE; - remove_watch(); + connman_inotify_unregister(STORAGEDIR, config_notify_handler); g_hash_table_destroy(config_table); config_table = NULL; diff --git a/src/connman.h b/src/connman.h index 67c1636..70178d8 100644 --- a/src/connman.h +++ b/src/connman.h @@ -219,6 +219,11 @@ gboolean __connman_storage_remove_service(const char *service_id); int __connman_detect_init(void); void __connman_detect_cleanup(void); +#include + +int __connman_inotify_init(void); +void __connman_inotify_cleanup(void); + #include int __connman_proxy_init(void); diff --git a/src/inotify.c b/src/inotify.c new file mode 100644 index 0000000..6646f5e --- /dev/null +++ b/src/inotify.c @@ -0,0 +1,236 @@ +/* + * + * Connection Manager + * + * Copyright (C) 2007-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2012 BMW Car IT GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "connman.h" + +struct connman_inotify { + GIOChannel *channel; + uint watch; + int wd; + + GSList *list; +}; + +static GHashTable *inotify_hash; + +static gboolean inotify_data(GIOChannel *channel, GIOCondition cond, + gpointer user_data) +{ + struct connman_inotify *inotify = user_data; + char buffer[256]; + char *next_event; + gsize bytes_read; + GIOStatus status; + GSList *list; + + if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { + inotify->watch = 0; + return FALSE; + } + + status = g_io_channel_read_chars(channel, buffer, + sizeof(buffer) -1, &bytes_read, NULL); + + switch (status) { + case G_IO_STATUS_NORMAL: + break; + case G_IO_STATUS_AGAIN: + return TRUE; + default: + connman_error("Reading from inotify channel failed"); + inotify->watch = 0; + return FALSE; + } + + next_event = buffer; + + while (bytes_read > 0) { + struct inotify_event *event; + gchar *ident; + gsize len; + + event = (struct inotify_event *) next_event; + if (event->len) + ident = next_event + sizeof(struct inotify_event); + else + ident = NULL; + + len = sizeof(struct inotify_event) + event->len; + + /* check if inotify_event block fit */ + if (len > bytes_read) + break; + + next_event += len; + bytes_read -= len; + + for (list = inotify->list; list != NULL; list = list->next) { + inotify_event_cb callback = list->data; + + (*callback)(event, ident); + } + } + + return TRUE; +} + +static int create_watch(const char *path, struct connman_inotify *inotify) +{ + int fd; + + DBG("Add directory watch for %s", path); + + fd = inotify_init(); + if (fd < 0) + return -EIO; + + inotify->wd = inotify_add_watch(fd, path, + IN_MODIFY | IN_CREATE | IN_DELETE); + if (inotify->wd < 0) { + connman_error("Creation of %s watch failed", path); + close(fd); + return -EIO; + } + + inotify->channel = g_io_channel_unix_new(fd); + if (inotify->channel == NULL) { + connman_error("Creation of inotify channel failed"); + inotify_rm_watch(fd, inotify->wd); + inotify->wd = 0; + + close(fd); + return -EIO; + } + + g_io_channel_set_close_on_unref(inotify->channel, TRUE); + g_io_channel_set_encoding(inotify->channel, NULL, NULL); + g_io_channel_set_buffered(inotify->channel, FALSE); + + inotify->watch = g_io_add_watch(inotify->channel, + G_IO_IN | G_IO_HUP | G_IO_NVAL | G_IO_ERR, + inotify_data, inotify); + + return 0; +} + +static void remove_watch(struct connman_inotify *inotify) +{ + int fd; + + if (inotify->channel == NULL) + return; + + if (inotify->watch > 0) + g_source_remove(inotify->watch); + + fd = g_io_channel_unix_get_fd(inotify->channel); + + if (inotify->wd >= 0) + inotify_rm_watch(fd, inotify->wd); + + g_io_channel_unref(inotify->channel); +} + +int connman_inotify_register(const char *path, inotify_event_cb callback) +{ + struct connman_inotify *inotify; + int err; + + if (callback == NULL) + return -EINVAL; + + inotify = g_hash_table_lookup(inotify_hash, path); + if (inotify != NULL) + goto update; + + inotify = g_try_new0(struct connman_inotify, 1); + if (inotify == NULL) + return -ENOMEM; + + inotify->wd = -1; + + err = create_watch(path, inotify); + if (err < 0) { + g_free(inotify); + return err; + } + + g_hash_table_replace(inotify_hash, g_strdup(path), inotify); + +update: + inotify->list = g_slist_prepend(inotify->list, callback); + + return 0; +} + +static void cleanup_inotify(gpointer user_data) +{ + struct connman_inotify *inotify = user_data; + + g_slist_free(inotify->list); + + remove_watch(inotify); + g_free(inotify); +} + +void connman_inotify_unregister(const char *path, inotify_event_cb callback) +{ + struct connman_inotify *inotify; + + inotify = g_hash_table_lookup(inotify_hash, path); + if (inotify == NULL) + return; + + inotify->list = g_slist_remove(inotify->list, callback); + if (inotify->list != NULL) + return; + + g_hash_table_remove(inotify_hash, path); +} + +int __connman_inotify_init(void) +{ + DBG(""); + + inotify_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, cleanup_inotify); + return 0; +} + +void __connman_inotify_cleanup(void) +{ + DBG(""); + + g_hash_table_destroy(inotify_hash); +} diff --git a/src/main.c b/src/main.c index 77a071a..a7afa44 100644 --- a/src/main.c +++ b/src/main.c @@ -557,6 +557,7 @@ int main(int argc, char *argv[]) config_init(option_config); __connman_storage_migrate(); + __connman_inotify_init(); __connman_technology_init(); __connman_notifier_init(); __connman_agent_init(); @@ -636,6 +637,7 @@ int main(int argc, char *argv[]) __connman_ipconfig_cleanup(); __connman_notifier_cleanup(); __connman_technology_cleanup(); + __connman_inotify_cleanup(); __connman_dbus_cleanup(); -- 2.7.4