diff --git a/src/lib/common/include/sol-platform.h b/src/lib/common/include/sol-platform.h index ce2fea8b9..bf424ef51 100644 --- a/src/lib/common/include/sol-platform.h +++ b/src/lib/common/include/sol-platform.h @@ -484,6 +484,9 @@ const char *sol_platform_get_locale(enum sol_platform_locale_category category); * @param data The data to @c cb * * @return 0 on success, negative errno otherwise. + * + * @note If an error happens while the locale is being monitored the @c cb + * will be called and @c category will be set to #SOL_PLATFORM_SERVICE_STATE_UNKNOWN and @c locale to @c NULL. */ int sol_platform_add_locale_monitor(void (*cb)(void *data, enum sol_platform_locale_category category, const char *locale), const void *data); diff --git a/src/lib/common/sol-platform-impl-linux-micro.c b/src/lib/common/sol-platform-impl-linux-micro.c index 3198488d4..43b2ff326 100644 --- a/src/lib/common/sol-platform-impl-linux-micro.c +++ b/src/lib/common/sol-platform-impl-linux-micro.c @@ -63,6 +63,7 @@ #define SOL_DEBUG_ARG "sol-debug=1" #define SOL_DEBUG_COMM_ARG "sol-debug-comm=" +#define LOCALE_CONF_MAX_CREATE_ATTEMPTS (5) static enum sol_platform_state platform_state = SOL_PLATFORM_STATE_INITIALIZING; static int reboot_cmd = RB_AUTOBOOT; @@ -91,9 +92,15 @@ struct sol_fd_watcher_ctx { int fd; }; +struct sol_locale_monitor { + struct sol_fd_watcher_ctx fd_watcher; + struct sol_timeout *create_timeout; + uint8_t create_attempts; +}; + static struct sol_fd_watcher_ctx hostname_monitor = { NULL, -1 }; static struct sol_fd_watcher_ctx timezone_monitor = { NULL, -1 }; -static struct sol_fd_watcher_ctx locale_monitor = { NULL, -1 }; +static struct sol_locale_monitor locale_monitor = { { NULL, -1 }, NULL, 0 }; #ifdef ENABLE_DYNAMIC_MODULES struct service_module { @@ -1155,22 +1162,21 @@ timezone_changed(void *data, int fd, uint32_t active_flags) } static int -add_watch(struct sol_fd_watcher_ctx *monitor, const char *path, - bool (*cb)(void *data, int fd, uint32_t active_flags)) +add_watch(struct sol_fd_watcher_ctx *monitor, uint32_t inotify_flags, + const char *path, bool (*cb)(void *data, int fd, uint32_t active_flags)) { int r; if (monitor->watcher) return 0; - monitor->fd = inotify_init1(IN_CLOEXEC); + monitor->fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); if (monitor->fd < 0) { return -errno; } - if (inotify_add_watch(monitor->fd, path, - IN_MODIFY | IN_DONT_FOLLOW) < 0) { + if (inotify_add_watch(monitor->fd, path, inotify_flags) < 0) { r = -errno; goto err_exit; } @@ -1193,7 +1199,8 @@ add_watch(struct sol_fd_watcher_ctx *monitor, const char *path, int sol_platform_register_timezone_monitor(void) { - return add_watch(&timezone_monitor, "/etc/localtime", timezone_changed); + return add_watch(&timezone_monitor, IN_MODIFY | IN_DONT_FOLLOW, + "/etc/localtime", timezone_changed); } int @@ -1235,26 +1242,99 @@ sol_platform_impl_set_locale(char **locales) return 0; } +static bool +timeout_locale(void *data) +{ + int r; + + r = sol_platform_register_locale_monitor(); + if (!r) { + SOL_DBG("Watching /etc/locale.conf again"); + goto unregister; + } + + if (++locale_monitor.create_attempts == LOCALE_CONF_MAX_CREATE_ATTEMPTS) { + sol_platform_inform_locale_monitor_error(); + SOL_WRN("/etc/locale.conf was not created. Giving up."); + goto unregister; + } + + SOL_DBG("/etc/locale.conf was not created yet, trying again in some time"); + return true; +unregister: + locale_monitor.create_timeout = NULL; + sol_platform_inform_locale_changed(); + return false; +} + static bool locale_changed(void *data, int fd, uint32_t active_flags) { char buf[4096]; + struct inotify_event *event; + char *ptr; + ssize_t len; + bool dispatch_callback, deleted; - sol_platform_inform_locale_changed(); - (void)read(fd, buf, sizeof(buf)); - return true; + deleted = dispatch_callback = false; + + while (1) { + len = read(fd, buf, sizeof(buf)); + + if (len == -1 && errno != EAGAIN && errno != EINTR) { + SOL_WRN("Could read the locale.conf inotify. Reason: %d", errno); + sol_platform_inform_locale_monitor_error(); + close_fd_monitor(&locale_monitor.fd_watcher); + return false; + } + + if (len <= 0) + break; + + for (ptr = buf; ptr < buf + len; + ptr += sizeof(struct inotify_event) + event->len) { + + event = (struct inotify_event *)ptr; + + if (event->mask & IN_MODIFY) { + SOL_DBG("locale.conf changed"); + dispatch_callback = true; + } + if (event->mask & IN_DELETE_SELF) { + SOL_DBG("locale.conf was moved"); + deleted = true; + } + } + } + + if (deleted) { + close_fd_monitor(&locale_monitor.fd_watcher); + //One second from now, check if a new locale has been created. + locale_monitor.create_timeout = + sol_timeout_add(1000, timeout_locale, NULL); + locale_monitor.create_attempts = 0; + if (!locale_monitor.create_timeout) { + SOL_WRN("Could not create a timer to check if" + " a new /etc/locale.conf has been created."); + sol_platform_inform_locale_monitor_error(); + } + } else if (dispatch_callback) + sol_platform_inform_locale_changed(); + return !deleted; } int sol_platform_register_locale_monitor(void) { - return add_watch(&locale_monitor, "/etc/locale.conf", - locale_changed); + return add_watch(&locale_monitor.fd_watcher, IN_MODIFY | IN_DELETE_SELF, + "/etc/locale.conf", locale_changed); } int sol_platform_unregister_locale_monitor(void) { - close_fd_monitor(&locale_monitor); + close_fd_monitor(&locale_monitor.fd_watcher); + if (locale_monitor.create_timeout) + sol_timeout_del(locale_monitor.create_timeout); return 0; } diff --git a/src/lib/common/sol-platform-impl.h b/src/lib/common/sol-platform-impl.h index b434046a9..10682a81f 100644 --- a/src/lib/common/sol-platform-impl.h +++ b/src/lib/common/sol-platform-impl.h @@ -104,3 +104,5 @@ const char *sol_platform_locale_to_c_str_category(enum sol_platform_locale_categ int sol_platform_impl_locale_to_c_category(enum sol_platform_locale_category category); const char *sol_platform_impl_locale_to_c_str_category(enum sol_platform_locale_category category); + +void sol_platform_inform_locale_monitor_error(void); diff --git a/src/lib/common/sol-platform.c b/src/lib/common/sol-platform.c index ce03dd776..ab7401ed2 100644 --- a/src/lib/common/sol-platform.c +++ b/src/lib/common/sol-platform.c @@ -699,6 +699,18 @@ sol_platform_inform_locale_changed(void) } } +void +sol_platform_inform_locale_monitor_error(void) +{ + struct sol_monitors_entry *entry; + uint16_t i; + + SOL_MONITORS_WALK (&_ctx.locale_monitors, entry, i) { + ((void (*)(void *, enum sol_platform_locale_category, const char *)) + entry->cb)((void *)entry->data, SOL_PLATFORM_LOCALE_UNKNOWN, NULL); + } +} + static bool locale_timeout_cb(void *data) { diff --git a/src/modules/flow/platform/platform.c b/src/modules/flow/platform/platform.c index aea861680..c6c871261 100644 --- a/src/modules/flow/platform/platform.c +++ b/src/modules/flow/platform/platform.c @@ -57,6 +57,8 @@ struct monitor_node_type { int (*monitor_unregister)(struct sol_flow_node *); }; +static int locale_monitor_unregister(struct sol_flow_node *node); + static int state_dispatch_ready(struct platform_data *mdata) { @@ -419,6 +421,13 @@ timezone_process(struct sol_flow_node *node, void *data, uint16_t port, static int locale_send(struct sol_flow_node *node, enum sol_platform_locale_category category, const char *locale) { + if (category == SOL_PLATFORM_LOCALE_UNKNOWN && !locale) { + locale_monitor_unregister(node); + return sol_flow_send_error_packet(node, ECANCELED, + "Something wrong happened with the locale monitor," + "stoping to monitor locale changes"); + } + if (!locale) { locale = sol_platform_get_locale(category); SOL_NULL_CHECK(locale, -EINVAL);