Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

Commit

Permalink
Sol-platform: Register a new inotify if /etc/locale.conf was deleted.
Browse files Browse the repository at this point in the history
When one changes the locale using systemd's localectl, the systemd
will not simple open the /etc/locale.conf file and update its contents.
It will create a new file file and then replace the /etc/locale.conf.
(basically mv /etc/ANeWLocaleFile.conf /etc/locale.conf)
Since the old locale.conf was deleted, a new inode watcher must
be created.

Signed-off-by: Guilherme Iscaro <[email protected]>
  • Loading branch information
cabelitos committed Jan 26, 2016
1 parent 54b9593 commit d662d58
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 13 deletions.
3 changes: 3 additions & 0 deletions src/lib/common/include/sol-platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
106 changes: 93 additions & 13 deletions src/lib/common/sol-platform-impl-linux-micro.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
2 changes: 2 additions & 0 deletions src/lib/common/sol-platform-impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
12 changes: 12 additions & 0 deletions src/lib/common/sol-platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
9 changes: 9 additions & 0 deletions src/modules/flow/platform/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit d662d58

Please sign in to comment.