From 5879972108bc28629f41c56d34592642df0c4588 Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Thu, 9 Feb 2012 03:26:48 -0500 Subject: Add beginnings of localed (read-only and no xorg.conf support) --- .gitignore | 1 + Makefile.am | 21 ++++- README | 22 ++++++ TODO | 2 +- data/locale1.xml | 30 ++++++++ src/localed.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/localed.h | 31 ++++++++ src/main.c | 3 + src/shell-utils.c | 83 ++++++++++++++++---- src/shell-utils.h | 5 ++ 10 files changed, 406 insertions(+), 19 deletions(-) create mode 100644 data/locale1.xml create mode 100644 src/localed.c create mode 100644 src/localed.h diff --git a/.gitignore b/.gitignore index b0d689f..8fae933 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ /openrc-settingsd /stamp-h1 /src/hostname1-generated.[ch] +/src/locale1-generated.[ch] # Relative rules *.o diff --git a/Makefile.am b/Makefile.am index f2a770a..7ae61ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,6 +2,7 @@ ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} EXTRA_DIST = \ data/hostname1.xml \ + data/locale1.xml \ $(NULL) AM_CPPFLAGS = \ @@ -22,10 +23,18 @@ hostnamed_built_sources = \ src/hostname1-generated.h \ $(NULL) +localed_built_sources = \ + src/locale1-generated.c \ + src/locale1-generated.h \ + $(NULL) + openrc_settingsd_SOURCES = \ $(hostnamed_built_sources) \ + $(localed_built_sources) \ src/hostnamed.c \ src/hostnamed.h \ + src/localed.c \ + src/localed.h \ src/bus-utils.c \ src/bus-utils.h \ src/shell-utils.c \ @@ -41,5 +50,13 @@ $(hostnamed_built_sources) : data/hostname1.xml $(srcdir)/data/hostname1.xml; \ mv hostname1-generated.{c,h} $(top_srcdir)/src/ ) -BUILT_SOURCES = $(hostnamed_built_sources) -CLEANFILES = $(hostnamed_built_sources) +$(localed_built_sources) : data/locale1.xml + ( $(GDBUS_CODEGEN) \ + --interface-prefix org.freedesktop. \ + --c-namespace OpenrcSettingsdLocaled \ + --generate-c-code locale1-generated \ + $(srcdir)/data/locale1.xml; \ + mv locale1-generated.{c,h} $(top_srcdir)/src/ ) + +BUILT_SOURCES = $(hostnamed_built_sources) $(localed_built_sources) +CLEANFILES = $(hostnamed_built_sources) $(localed_built_sources) diff --git a/README b/README index 62bd904..bd6adac 100644 --- a/README +++ b/README @@ -16,6 +16,28 @@ Hostnamed: PRETTY_HOSTNAME="Foo !" ICON_NAME="computer-desktop" +Localed: + + See http://www.freedesktop.org/wiki/Software/systemd/localed for the DBus + protocol description. + + The system locale variables are set in /etc/env.d/02locale. + + Virtual console keymap is set in /etc/conf.d/keymaps as + keymap="foo" + The virtual console keymap toggle is not supported. + +/* + Not implemented yet. + + X11 keyboard options are set in /etc/X11/xorg.conf.d/30-keyboard.conf + (falling back to 00-keyboard.conf if it exists and 30-keyboard.conf does + not) in an InputClass section. + Localed will attempt to detect if multiple InputClass sections with + keyboard options exist in /etc/X11/xorg.conf and /etc/X11/xorg.conf.d/, + and if that is the case, will refuse to modify X11 keyboard settings. +*/ + Note that openrc-settingsd expects any shell-syntax settings files that it modifies to be in UTF-8 encoding, and to consist only of comments and simple scalar assignments, i.e. something like diff --git a/TODO b/TODO index e8694da..a7feb33 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -Add nss-myhostname detection (and add nss-myhostnane to portage) +Add nss-myhostname detection Source /etc/rc.conf after /etc/conf.d/$service; do something intelligent if the relevant variable is set in /etc/rc.conf (go to read-only mode?) diff --git a/data/locale1.xml b/data/locale1.xml new file mode 100644 index 0000000..dfd98d3 --- /dev/null +++ b/data/locale1.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/localed.c b/src/localed.c new file mode 100644 index 0000000..12d9ad7 --- /dev/null +++ b/src/localed.c @@ -0,0 +1,227 @@ +/* + Copyright 2012 Alexandre Rostovtsev + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 +*/ + +#include + +#include +#include +#include + +#include "localed.h" +#include "locale1-generated.h" +#include "bus-utils.h" +#include "shell-utils.h" + +#include "config.h" + +#define SERVICE_NAME "openrc-settingsd localed" + +static guint bus_id = 0; +static gboolean read_only = FALSE; + +static OpenrcSettingsdLocaledLocale1 *locale1 = NULL; + +static gchar *locale_variables[] = { + "LANG", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", "LC_TELEPHONE", "LC_MEASUREMENT", "LC_IDENTIFICATION", NULL +}; + +static gchar **locale = NULL; /* Expected format is { "LANG=foo", "LC_TIME=bar", NULL } */ +static GFile *locale_file = NULL; +G_LOCK_DEFINE_STATIC (locale); + +static gchar *vconsole_keymap = NULL; +static gchar *vconsole_keymap_toggle = NULL; +static GFile *keymaps_file = NULL; +G_LOCK_DEFINE_STATIC (keymaps); + +static gchar *x11_layout = NULL; +static gchar *x11_model = NULL; +static gchar *x11_variant = NULL; +static gchar *x11_options = NULL; +G_LOCK_DEFINE_STATIC (xorg_conf); + +static gboolean +on_handle_set_locale (OpenrcSettingsdLocaledLocale1 *locale1, + GDBusMethodInvocation *invocation, + const gchar * const *_locale, + const gboolean user_interaction, + gpointer user_data) +{ + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + + return TRUE; +} + +static gboolean +on_handle_set_vconsole_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, + GDBusMethodInvocation *invocation, + const gchar *keymap, + const gchar *keymap_toggle, + const gboolean convert, + const gboolean user_interaction, + gpointer user_data) +{ + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + + return TRUE; +} + +static gboolean +on_handle_set_x11_keyboard (OpenrcSettingsdLocaledLocale1 *locale1, + GDBusMethodInvocation *invocation, + const gchar *layout, + const gchar *model, + const gchar *variant, + const gchar *options, + const gboolean convert, + const gboolean user_interaction, + gpointer user_data) +{ + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + SERVICE_NAME " is in read-only mode"); + + return TRUE; +} + +static void +on_bus_acquired (GDBusConnection *connection, + const gchar *bus_name, + gpointer user_data) +{ + gchar *name; + GError *err = NULL; + + g_debug ("Acquired a message bus connection"); + + locale1 = openrc_settingsd_localed_locale1_skeleton_new (); + + openrc_settingsd_localed_locale1_set_locale (locale1, (const gchar * const *) locale); + openrc_settingsd_localed_locale1_set_vconsole_keymap (locale1, vconsole_keymap); + openrc_settingsd_localed_locale1_set_vconsole_keymap_toggle (locale1, vconsole_keymap_toggle); + openrc_settingsd_localed_locale1_set_x11_layout (locale1, x11_layout); + openrc_settingsd_localed_locale1_set_x11_model (locale1, x11_model); + openrc_settingsd_localed_locale1_set_x11_variant (locale1, x11_variant); + openrc_settingsd_localed_locale1_set_x11_options (locale1, x11_options); + + g_signal_connect (locale1, "handle-set-locale", G_CALLBACK (on_handle_set_locale), NULL); + g_signal_connect (locale1, "handle-set-vconsole-keyboard", G_CALLBACK (on_handle_set_vconsole_keyboard), NULL); + g_signal_connect (locale1, "handle-set-x11-keyboard", G_CALLBACK (on_handle_set_x11_keyboard), NULL); + + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (locale1), + connection, + "/org/freedesktop/locale1", + &err)) { + if (err != NULL) { + g_printerr ("Failed to export interface on /org/freedesktop/locale1: %s\n", err->message); + g_error_free (err); + } + } +} + +static void +on_name_acquired (GDBusConnection *connection, + const gchar *bus_name, + gpointer user_data) +{ + g_debug ("Acquired the name %s", bus_name); +} + +static void +on_name_lost (GDBusConnection *connection, + const gchar *bus_name, + gpointer user_data) +{ + if (connection == NULL) + g_printerr ("Failed to acquire a dbus connection\n"); + else + g_printerr ("Failed to acquire dbus name %s\n", bus_name); + exit(-1); +} + +void +localed_init (gboolean _read_only) +{ + GError *err = NULL; + gchar **locale_values = NULL; + + read_only = _read_only; + locale_file = g_file_new_for_path (SYSCONFDIR "/env.d/02locale"); + keymaps_file = g_file_new_for_path (SYSCONFDIR "/conf.d/keymaps"); + + locale = g_new0 (gchar *, g_strv_length (locale_variables) + 1); + locale_values = shell_utils_trivial_source_var_list (locale_file, (const gchar * const *)locale_variables, &err); + if (locale_values != NULL) { + gchar **variable, **value, **loc; + loc = locale; + for (variable = locale_variables, value = locale_values; *variable != NULL; variable++, value++) { + if (*value != NULL) { + *loc = g_strdup_printf ("%s=%s", *variable, *value); + loc++; + } + } + + g_strfreev (locale_values); + } + if (err != NULL) { + g_debug ("%s", err->message); + g_clear_error (&err); + } + + vconsole_keymap = shell_utils_source_var (keymaps_file, "${keymap}", &err); + if (vconsole_keymap == NULL) + vconsole_keymap = g_strdup (""); + if (err != NULL) { + g_debug ("%s", err->message); + g_clear_error (&err); + } + + /* We don't have a good equivalent for this in openrc at the moment */ + vconsole_keymap_toggle = g_strdup (""); + + bus_id = g_bus_own_name (G_BUS_TYPE_SYSTEM, + "org.freedesktop.locale1", + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + on_name_lost, + NULL, + NULL); +} + +void +localed_destroy (void) +{ + g_bus_unown_name (bus_id); + bus_id = 0; + read_only = FALSE; + g_strfreev (locale); + g_free (vconsole_keymap); + g_free (vconsole_keymap_toggle); + g_free (x11_layout); + g_free (x11_model); + g_free (x11_variant); + g_free (x11_options); + + g_object_unref (locale_file); + g_object_unref (keymaps_file); +} diff --git a/src/localed.h b/src/localed.h new file mode 100644 index 0000000..dff55c6 --- /dev/null +++ b/src/localed.h @@ -0,0 +1,31 @@ +/* + Copyright 2012 Alexandre Rostovtsev + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 OPENRC_LOCALED_H +#define OPENRC_LOCALED_H + +#include +#include + +void +localed_init (gboolean read_only); + +void +localed_destroy (void); + +#endif diff --git a/src/main.c b/src/main.c index c7165f0..fabb96c 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include #include "hostnamed.h" +#include "localed.h" #include "shell-utils.h" #include "config.h" @@ -68,10 +69,12 @@ main (gint argc, gchar *argv[]) shell_utils_init (); hostnamed_init (read_only); + localed_init (read_only); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); g_main_loop_unref (loop); + localed_destroy (); hostnamed_destroy (); shell_utils_destroy (); return 0; diff --git a/src/shell-utils.c b/src/shell-utils.c index 30026a9..db67303 100644 --- a/src/shell-utils.c +++ b/src/shell-utils.c @@ -45,6 +45,7 @@ struct ShellEntry { enum ShellEntryType type; gchar *string; gchar *variable; /* only relevant for assignments */ + gchar *unquoted_value; /* only relevant for assignments */ }; gchar * @@ -104,10 +105,9 @@ shell_entry_free (struct ShellEntry *entry) if (entry == NULL) return; - if (entry->string != NULL) - g_free (entry->string); - if (entry->variable != NULL) - g_free (entry->variable); + g_free (entry->string); + g_free (entry->variable); + g_free (entry->unquoted_value); g_free (entry); } @@ -132,7 +132,7 @@ shell_utils_trivial_new (GFile *file, { gchar *filebuf = NULL; ShellUtilsTrivial *ret = NULL; - GError *local_err; + GError *local_err = NULL; gchar *s; if (file == NULL) @@ -214,6 +214,7 @@ shell_utils_trivial_new (GFile *file, matched = g_regex_match (var_equals_regex, s, 0, &match_info); if (matched) { + gchar *raw_value = NULL, *temp1 = NULL, *temp2 = NULL; /* If we expect a separator and get an assignment instead, fail */ if (want_separator) goto no_match; @@ -231,7 +232,6 @@ shell_utils_trivial_new (GFile *file, while (*s != 0) { g_debug ("Scanning string for values: ``%s''", s); gboolean matched2 = FALSE; - gchar *temp1 = NULL, *temp2 = NULL; matched2 = g_regex_match (single_quoted_regex, s, 0, &match_info); if (matched2) @@ -256,18 +256,36 @@ shell_utils_trivial_new (GFile *file, break; append_value: + + if (raw_value == NULL) { + raw_value = g_match_info_fetch (match_info, 0); + s += strlen (raw_value); + g_debug ("Scanned value: ``%s''", raw_value); + } else { + temp1 = raw_value; + temp2 = g_match_info_fetch (match_info, 0); + raw_value = g_strconcat (temp1, temp2, NULL); + s += strlen (temp2); + g_debug ("Scanned value: ``%s''", temp2); + g_free (temp1); + g_free (temp2); + } + g_match_info_free (match_info); + match_info = NULL; + } + + if (raw_value != NULL) { + entry->unquoted_value = g_shell_unquote (raw_value, &local_err); + g_debug ("Unquoted value: ``%s''", entry->unquoted_value); temp1 = entry->string; - temp2 = g_match_info_fetch (match_info, 0); + temp2 = raw_value; entry->string = g_strconcat (temp1, temp2, NULL); - s += strlen (temp2); - g_debug ("Scanned value: ``%s''", temp2); g_free (temp1); g_free (temp2); - g_match_info_free (match_info); - match_info = NULL; + ret->entry_list = g_list_prepend (ret->entry_list, entry); + if (local_err != NULL) + goto no_match; } - - ret->entry_list = g_list_prepend (ret->entry_list, entry); continue; } @@ -275,9 +293,12 @@ no_match: /* Nothing matches, parsing has failed! */ g_match_info_free (match_info); match_info = NULL; - g_propagate_error (error, - g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, - "Unable to parse '%s'", ret->filename)); + if (local_err != NULL) + g_propagate_prefixed_error (error, local_err, "Unable to parse '%s':", ret->filename); + else + g_propagate_error (error, + g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED, + "Unable to parse '%s'", ret->filename)); shell_utils_trivial_free (ret); return NULL; } @@ -420,6 +441,36 @@ shell_utils_trivial_set_and_save (GFile *file, return ret; } +gchar ** +shell_utils_trivial_source_var_list (GFile *file, + const gchar * const *var_names, + GError **error) +{ + ShellUtilsTrivial *trivial; + gchar **ret = NULL, **value; + const gchar* const* var_name; + + if (var_names == NULL) + return NULL; + + if ((trivial = shell_utils_trivial_new (file, error)) == NULL) + return NULL; + + ret = g_new0 (gchar *, g_strv_length ((gchar **)var_names) + 1); + for (var_name = var_names, value = ret; *var_name != NULL; var_name++, value++) { + GList *curr; + for (curr = trivial->entry_list; curr != NULL; curr = curr->next) { + struct ShellEntry *entry; + + entry = (struct ShellEntry *)(curr->data); + if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (*var_name, entry->variable) == 0) + *value = g_strdup (entry->unquoted_value); + } + } + shell_utils_trivial_free (trivial); + return ret; +} + void shell_utils_destroy (void) { diff --git a/src/shell-utils.h b/src/shell-utils.h index 5e1863d..9ca40e9 100644 --- a/src/shell-utils.h +++ b/src/shell-utils.h @@ -66,4 +66,9 @@ shell_utils_trivial_set_and_save (GFile *file, const gchar *first_alt_var_name, const gchar *first_value, ...); + +gchar ** +shell_utils_trivial_source_var_list (GFile *file, + const gchar * const *var_names, + GError **error); #endif -- cgit v1.2.3-65-gdbad