public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, data/
@ 2012-02-09  8:42 Alexandre Restovtsev
  0 siblings, 0 replies; 3+ messages in thread
From: Alexandre Restovtsev @ 2012-02-09  8:42 UTC (permalink / raw
  To: gentoo-commits

commit:     5879972108bc28629f41c56d34592642df0c4588
Author:     Alexandre Rostovtsev <tetromino <AT> gentoo <DOT> org>
AuthorDate: Thu Feb  9 08:26:48 2012 +0000
Commit:     Alexandre Restovtsev <tetromino <AT> gmail <DOT> com>
CommitDate: Thu Feb  9 08:40:24 2012 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git;a=commit;h=58799721

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(-)

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 @@
+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+<node name="/org/freedesktop/locale1">
+    <interface name="org.freedesktop.locale1">
+        <method name="SetLocale">
+            <arg direction="in" type="as" name="locale"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <method name="SetVConsoleKeyboard">
+            <arg direction="in" type="s" name="keymap"/>
+            <arg direction="in" type="s" name="keymap_toggle"/>
+            <arg direction="in" type="b" name="convert"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <method name="SetX11Keyboard">
+            <arg direction="in" type="s" name="layout"/>
+            <arg direction="in" type="s" name="model"/>
+            <arg direction="in" type="s" name="variant"/>
+            <arg direction="in" type="s" name="options"/>
+            <arg direction="in" type="b" name="convert"/>
+            <arg direction="in" type="b" name="user_interaction"/>
+        </method>
+        <property name="Locale" type="as" access="read"/>
+        <property name="VConsoleKeymap" type="s" access="read"/>
+        <property name="VConsoleKeymapToggle" type="s" access="read"/>
+        <property name="X11Layout" type="s" access="read"/>
+        <property name="X11Model" type="s" access="read"/>
+        <property name="X11Variant" type="s" access="read"/>
+        <property name="X11Options" type="s" access="read"/>
+    </interface>
+</node>

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 <stdlib.h>
+
+#include <dbus/dbus-protocol.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#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 <glib.h>
+#include <gio/gio.h>
+
+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 <gio/gio.h>
 
 #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



^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, data/
@ 2012-09-05  5:33 Alexandre Restovtsev
  0 siblings, 0 replies; 3+ messages in thread
From: Alexandre Restovtsev @ 2012-09-05  5:33 UTC (permalink / raw
  To: gentoo-commits

commit:     a0e3c463acbda2d81081e223ef999fdab59a0304
Author:     Alexandre Rostovtsev <tetromino <AT> gentoo <DOT> org>
AuthorDate: Wed Sep  5 05:29:46 2012 +0000
Commit:     Alexandre Restovtsev <tetromino <AT> gmail <DOT> com>
CommitDate: Wed Sep  5 05:33:18 2012 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git;a=commit;h=a0e3c463

Finish SetVConsoleKeyboard() and SetX11Keyboard() implementation

Also, fix xorg.conf.d parser so it works as intended.

---
 Makefile.am        |    5 +
 TODO               |    2 +-
 data/kbd-model-map |   73 ++++++
 src/localed.c      |  730 +++++++++++++++++++++++++++++++++++++++++++++++-----
 src/shell-utils.c  |   80 +++++-
 src/shell-utils.h  |   14 +-
 6 files changed, 828 insertions(+), 76 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 7ae61ed..b0694bf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -5,10 +5,15 @@ EXTRA_DIST = \
 	data/locale1.xml \
 	$(NULL)
 
+pkgdata_DATA = \
+	data/kbd-model-map \
+	$(NULL)
+
 AM_CPPFLAGS = \
 	-include $(top_builddir)/config.h \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
 	-DLIBEXECDIR=\""$(libexecdir)"\" \
+	-DPKGDATADIR=\""$(pkgdatadir)"\" \
 	$(OPENRC_SETTINGSD_CFLAGS) \
 	-I$(top_srcdir)/src \
 	-I$(top_builddir)/src \

diff --git a/TODO b/TODO
index a7feb33..d75749a 100644
--- a/TODO
+++ b/TODO
@@ -9,7 +9,7 @@ document that this case is not supported?
 Write an init.d file to read RC_SYS so that we can piggyback on openrc's
 built-in virtualization detection.
 
-Implement localed and timedated.
+Implement timedated.
 
 Do something about runtime dependency on systemd's org.freedesktop.hostname1.policy,
 org.freedesktop.locale1.policy, and org.freedesktop.timedate1.policy files.

diff --git a/data/kbd-model-map b/data/kbd-model-map
new file mode 100644
index 0000000..5876801
--- /dev/null
+++ b/data/kbd-model-map
@@ -0,0 +1,73 @@
+# Shamelessly stolen from systemd-44 source (src/locale/kbd-model-map)
+# Generated from system-config-keyboard's model list
+# consolelayout		xlayout	xmodel		xvariant	xoptions
+sg			ch	pc105		de_nodeadkeys	terminate:ctrl_alt_bksp
+nl			nl	pc105		-		terminate:ctrl_alt_bksp
+mk-utf			mkd,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+trq			tr	pc105		-		terminate:ctrl_alt_bksp
+guj			in,us	pc105		guj		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+uk			gb	pc105		-		terminate:ctrl_alt_bksp
+is-latin1		is	pc105		-		terminate:ctrl_alt_bksp
+de			de	pc105		-		terminate:ctrl_alt_bksp
+gur			gur,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+la-latin1		latam	pc105		-		terminate:ctrl_alt_bksp
+us			us	pc105+inet	-		terminate:ctrl_alt_bksp
+ko			kr	pc105		-		terminate:ctrl_alt_bksp
+ro-std			ro	pc105		std		terminate:ctrl_alt_bksp
+de-latin1		de	pc105		-		terminate:ctrl_alt_bksp
+tml-inscript		in,us	pc105		tam		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+slovene			si	pc105		-		terminate:ctrl_alt_bksp
+hu101			hu	pc105		qwerty		terminate:ctrl_alt_bksp
+jp106			jp	jp106		-		terminate:ctrl_alt_bksp
+croat			hr	pc105		-		terminate:ctrl_alt_bksp
+ben-probhat		in,us	pc105		ben_probhat	terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+fi-latin1		fi	pc105		-		terminate:ctrl_alt_bksp
+it2			it	pc105		-		terminate:ctrl_alt_bksp
+hu			hu	pc105		-		terminate:ctrl_alt_bksp
+sr-latin		rs	pc105		latin		terminate:ctrl_alt_bksp
+fi			fi	pc105		-		terminate:ctrl_alt_bksp
+fr_CH			ch	pc105		fr		terminate:ctrl_alt_bksp
+dk-latin1		dk	pc105		-		terminate:ctrl_alt_bksp
+fr			fr	pc105		-		terminate:ctrl_alt_bksp
+it			it	pc105		-		terminate:ctrl_alt_bksp
+tml-uni			in,us	pc105		tam_TAB		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+ua-utf			ua,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+fr-latin1		fr	pc105		-		terminate:ctrl_alt_bksp
+sg-latin1		ch	pc105		de_nodeadkeys	terminate:ctrl_alt_bksp
+be-latin1		be	pc105		-		terminate:ctrl_alt_bksp
+dk			dk	pc105		-		terminate:ctrl_alt_bksp
+fr-pc			fr	pc105		-		terminate:ctrl_alt_bksp
+bg_pho-utf8		bg,us	pc105		,phonetic	terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+it-ibm			it	pc105		-		terminate:ctrl_alt_bksp
+cz-us-qwertz		cz,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+ar-digits		ara,us	pc105		digits		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+br-abnt2		br	abnt2		-		terminate:ctrl_alt_bksp
+ro			ro	pc105		-		terminate:ctrl_alt_bksp
+us-acentos		us	pc105		intl		terminate:ctrl_alt_bksp
+pt-latin1		pt	pc105		-		terminate:ctrl_alt_bksp
+ro-std-cedilla		ro	pc105		std_cedilla	terminate:ctrl_alt_bksp
+tj			tj	pc105		-		terminate:ctrl_alt_bksp
+ar-qwerty		ara,us	pc105		qwerty		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+ar-azerty-digits	ara,us	pc105		azerty_digits	terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+ben			in,us	pc105		ben		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+de-latin1-nodeadkeys	de	pc105		nodeadkeys	terminate:ctrl_alt_bksp
+no			no	pc105		-		terminate:ctrl_alt_bksp
+bg_bds-utf8		bg,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+dvorak			us	pc105		dvorak		terminate:ctrl_alt_bksp
+ru			ru,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+cz-lat2			cz	pc105		qwerty		terminate:ctrl_alt_bksp
+pl2			pl	pc105		-		terminate:ctrl_alt_bksp
+es			es	pc105		-		terminate:ctrl_alt_bksp
+ro-cedilla		ro	pc105		cedilla		terminate:ctrl_alt_bksp
+ie			ie	pc105		-		terminate:ctrl_alt_bksp
+ar-azerty		ara,us	pc105		azerty		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+ar-qwerty-digits	ara,us	pc105		qwerty_digits	terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+et			ee	pc105		-		terminate:ctrl_alt_bksp
+sk-qwerty		sk	pc105		-		terminate:ctrl_alt_bksp,qwerty
+dev			dev,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll
+fr-latin9		fr	pc105		latin9		terminate:ctrl_alt_bksp
+fr_CH-latin1		ch	pc105		fr		terminate:ctrl_alt_bksp
+cf			ca(fr)	pc105		-		terminate:ctrl_alt_bksp
+sv-latin1		se	pc105		-		terminate:ctrl_alt_bksp
+sr-cy			rs	pc105		-		terminate:ctrl_alt_bksp
+gr			gr,us	pc105		-		terminate:ctrl_alt_bksp,grp:shifts_toggle,grp_led:scroll

diff --git a/src/localed.c b/src/localed.c
index 706a8ed..2e8e44e 100644
--- a/src/localed.c
+++ b/src/localed.c
@@ -16,6 +16,7 @@
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
+#include <limits.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -58,6 +59,211 @@ static GFile *x11_gentoo_file = NULL;
 static GFile *x11_systemd_file = NULL;
 G_LOCK_DEFINE_STATIC (xorg_conf);
 
+/* keyboard model map file parser */
+
+static GFile *kbd_model_map_file = NULL;
+
+GRegex *kbd_model_map_line_comment_re = NULL;
+GRegex *kbd_model_map_line_re = NULL;
+
+struct kbd_model_map_entry {
+    gchar *vconsole_keymap;
+    gchar *x11_layout;
+    gchar *x11_model;
+    gchar *x11_variant;
+    gchar *x11_options;
+};
+
+static void
+kbd_model_map_regex_destroy ()
+{
+    if (kbd_model_map_line_comment_re != NULL) {
+        g_regex_unref (kbd_model_map_line_comment_re);
+        kbd_model_map_line_comment_re = NULL;
+    }
+    if (kbd_model_map_line_re != NULL) {
+        g_regex_unref (kbd_model_map_line_re);
+        kbd_model_map_line_re = NULL;
+    }
+}
+
+static void
+kbd_model_map_regex_init ()
+{
+    if (kbd_model_map_line_comment_re == NULL) {
+        kbd_model_map_line_comment_re = g_regex_new ("^\\s*(?:#.*)?$", G_REGEX_ANCHORED, 0, NULL);
+        g_assert (kbd_model_map_line_comment_re != NULL);
+    }
+    if (kbd_model_map_line_re == NULL) {
+        kbd_model_map_line_re = g_regex_new ("^\\s*(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)", G_REGEX_ANCHORED, 0, NULL);
+        g_assert (kbd_model_map_line_re != NULL);
+    }
+}
+
+static gboolean
+kbd_model_map_entry_matches_vconsole (const struct kbd_model_map_entry *entry,
+                                      const gchar *vconsole_keymap)
+{
+    return !g_strcmp0 (vconsole_keymap, entry->vconsole_keymap);
+}
+
+static gboolean
+matches_delimeted (const gchar *left,
+                   const gchar *right,
+                   const gchar *delimeter,
+                   unsigned int *failure_score)
+{
+    gboolean ret = FALSE;
+    gchar **leftv = NULL, **rightv = NULL;
+    gchar **leftcur = NULL, **rightcur = NULL;
+
+    if (left == NULL || left[0] == 0)
+        leftv = g_new0 (gchar *, 1);
+    else
+        leftv = g_strsplit (left, delimeter, 0);
+
+    if (right == NULL || right[0] == 0)
+        rightv = g_new0 (gchar *, 1);
+    else
+        rightv = g_strsplit (right, delimeter, 0);
+
+    if (failure_score != NULL)
+        *failure_score = 0;
+
+    for (leftcur = leftv; *leftcur != NULL; leftcur++) {
+        gboolean found = FALSE;
+        for (rightcur = rightv; *rightcur != NULL; rightcur++)
+            if (!g_strcmp0 (*leftcur, *rightcur)) {
+                found = TRUE;
+                break;
+            }
+        if (found)
+            ret = TRUE;
+        else if (failure_score != NULL)
+            (*failure_score)++;
+    }
+
+    for (rightcur = rightv; *rightcur != NULL; rightcur++) {
+        gboolean found = FALSE;
+        for (leftcur = leftv; *leftcur != NULL; leftcur++)
+            if (!g_strcmp0 (*rightcur, *leftcur)) {
+                found = TRUE;
+                break;
+            }
+        if (found)
+            ret = TRUE;
+        else if (failure_score != NULL)
+            (*failure_score)++;
+    }
+
+    g_strfreev (leftv);
+    g_strfreev (rightv);
+    return ret;
+}
+
+static gboolean
+kbd_model_map_entry_matches_x11 (const struct kbd_model_map_entry *entry,
+                                 const gchar *_x11_layout,
+                                 const gchar *_x11_model,
+                                 const gchar *_x11_variant,
+                                 const gchar *_x11_options,
+                                 unsigned int *failure_score)
+{
+    unsigned int x11_layout_failures;
+    gboolean ret = FALSE;
+
+    ret = matches_delimeted (_x11_layout, entry->x11_layout, ",", &x11_layout_failures);
+    if (failure_score != NULL)
+        *failure_score = 10000 * !ret +
+                         100 * x11_layout_failures +
+                         (g_strcmp0 (_x11_model, entry->x11_model) ? 1 : 0) +
+                         10 * (g_strcmp0 (_x11_variant, entry->x11_variant) ? 1 : 0) +
+                         !matches_delimeted (_x11_options, entry->x11_options, ",", NULL);
+    return ret;
+}
+
+static void
+kbd_model_map_entry_free (struct kbd_model_map_entry *entry)
+{
+    if (entry == NULL)
+        return;
+
+    g_free (entry->vconsole_keymap);
+    g_free (entry->x11_layout);
+    g_free (entry->x11_model);
+    g_free (entry->x11_variant);
+    g_free (entry->x11_options);
+
+    g_free (entry);
+}
+
+static GList*
+kbd_model_map_load (GError **error)
+{
+    GList *ret = NULL;
+    gchar *filename = NULL, *filebuf = NULL, *line = NULL, *newline = NULL;
+    struct kbd_model_map_entry *entry = NULL;
+
+    filename = g_file_get_path (kbd_model_map_file);
+    g_debug ("Parsing keyboard model map file file: '%s'", filename);
+
+    if (!g_file_load_contents (kbd_model_map_file, NULL, &filebuf, NULL, NULL, error)) {
+        g_prefix_error (error, "Unable to read '%s':", filename);
+        goto out;
+    }
+
+    for (line = filebuf; *line != 0; line = newline + 1) {
+        struct kbd_model_map_entry *entry = NULL;
+        GMatchInfo *match_info = NULL;
+
+        if ((newline = strstr (line, "\n")) != NULL)
+            *newline = 0;
+        else
+            newline = line + strlen (line) - 1;
+
+        if (g_regex_match (kbd_model_map_line_comment_re, line, 0, &match_info)) {
+            g_match_info_free (match_info);
+            continue;
+        }
+
+        if (!g_regex_match (kbd_model_map_line_re, line,  0, &match_info)) {
+            g_propagate_error (error,
+                               g_error_new (G_FILE_ERROR, G_FILE_ERROR_FAILED,
+                                            "Failed to parse line '%s' in '%s'", line, filename));
+            g_match_info_free (match_info);
+            if (ret != NULL) {
+                g_list_free_full (ret, (GDestroyNotify)kbd_model_map_entry_free);
+                ret = NULL;
+            }
+            goto out;
+        }
+        entry = g_new0 (struct kbd_model_map_entry, 1);
+        entry->vconsole_keymap = g_match_info_fetch (match_info, 1);
+        entry->x11_layout = g_match_info_fetch (match_info, 2);
+        entry->x11_model = g_match_info_fetch (match_info, 3);
+        entry->x11_variant = g_match_info_fetch (match_info, 4);
+        entry->x11_options = g_match_info_fetch (match_info, 5);
+
+        // "-" in the map file stands for an empty string
+        if (!g_strcmp0 (entry->x11_model, "-"))
+            entry->x11_model[0] = 0;
+        if (!g_strcmp0 (entry->x11_variant, "-"))
+            entry->x11_variant[0] = 0;
+        if (!g_strcmp0 (entry->x11_options, "-"))
+            entry->x11_options[0] = 0;
+
+        ret = g_list_prepend (ret, entry);
+        g_match_info_free (match_info);
+    }
+  out:
+    if (ret != NULL)
+        ret = g_list_reverse (ret);
+
+    g_free (filename);
+    g_free (filebuf);
+    return ret;
+}
+
 /* Trivial /etc/X11/xorg.conf.d/30-keyboard.conf parser */
 
 enum XORG_CONFD_LINE_TYPE {
@@ -141,7 +347,7 @@ static void
 xorg_confd_regex_init ()
 {
     if (xorg_confd_line_comment_re == NULL) {
-        xorg_confd_line_comment_re = g_regex_new ("^\\s*#", G_REGEX_ANCHORED|G_REGEX_CASELESS, 0, NULL);
+        xorg_confd_line_comment_re = g_regex_new ("^\\s*#", G_REGEX_ANCHORED, 0, NULL);
         g_assert (xorg_confd_line_comment_re != NULL);
     }
     if (xorg_confd_line_section_input_class_re == NULL) {
@@ -190,7 +396,6 @@ xorg_confd_line_entry_free (struct xorg_confd_line_entry *entry)
     g_free (entry);
 }
 
-/* Note that string and value are not duplicated */
 static struct xorg_confd_line_entry *
 xorg_confd_line_entry_new (const gchar *string,
                            const gchar *value,
@@ -202,6 +407,7 @@ xorg_confd_line_entry_new (const gchar *string,
     entry->string = g_strdup (string);
     entry->value = g_strdup (value);
     entry->type = type;
+    return entry;
 }
 
 static void
@@ -226,9 +432,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file,
                        GError **error)
 {
     struct xorg_confd_parser *parser = NULL;
-    gchar *filebuf = NULL;
-    gchar **linebuf = NULL;
-    gchar **lines = NULL;
+    gchar *filebuf = NULL, *line = NULL, *newline = NULL;
     GList *input_class_section_start = NULL;
     gboolean in_section = FALSE, in_xkb_section = FALSE;
 
@@ -244,63 +448,64 @@ xorg_confd_parser_new (GFile *xorg_confd_file,
         goto fail;
     }
 
-    lines = g_strsplit (filebuf, "\n", 0);
-    if (lines == NULL)
-        goto out;
-
-    for (linebuf = lines; *linebuf != NULL; linebuf++) {
+    for (line = filebuf; *line != 0; line = newline + 1) {
         struct xorg_confd_line_entry *entry = NULL;
         GMatchInfo *match_info = NULL;
         gboolean matched = FALSE;
 
-        entry = xorg_confd_line_entry_new (*linebuf, NULL, XORG_CONFD_LINE_TYPE_UNKNOWN);
+        if ((newline = strstr (line, "\n")) != NULL)
+            *newline = 0;
+        else
+            newline = line + strlen (line) - 1;
+
+        entry = xorg_confd_line_entry_new (line, NULL, XORG_CONFD_LINE_TYPE_UNKNOWN);
 
-        if (g_regex_match (xorg_confd_line_comment_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as comment", *linebuf);
+        if (g_regex_match (xorg_confd_line_comment_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as comment", line);
             entry->type = XORG_CONFD_LINE_TYPE_COMMENT;
-        } else if (g_regex_match (xorg_confd_line_section_input_class_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as InputClass section", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_section_input_class_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as InputClass section", line);
             if (in_section)
                 goto no_match;
             in_section = TRUE;
             entry->type = XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS;
-        } else if (g_regex_match (xorg_confd_line_section_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as non-InputClass section", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_section_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as non-InputClass section", line);
             if (in_section)
                 goto no_match;
             in_section = TRUE;
             entry->type = XORG_CONFD_LINE_TYPE_SECTION_OTHER;
-        } else if (g_regex_match (xorg_confd_line_end_section_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as end of section", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_end_section_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as end of section", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_END_SECTION;
-        } else if (g_regex_match (xorg_confd_line_match_is_keyboard_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as MatchIsKeyboard declaration", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_match_is_keyboard_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as MatchIsKeyboard declaration", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD;
             in_xkb_section = TRUE;
-        } else if (g_regex_match (xorg_confd_line_xkb_layout_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as XkbLayout option", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_xkb_layout_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as XkbLayout option", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_XKB_LAYOUT;
             entry->value = g_match_info_fetch (match_info, 2);
-        } else if (g_regex_match (xorg_confd_line_xkb_model_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as XkbModel option", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_xkb_model_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as XkbModel option", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_XKB_MODEL;
             entry->value = g_match_info_fetch (match_info, 2);
-        } else if (g_regex_match (xorg_confd_line_xkb_variant_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as XkbVariant option", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_xkb_variant_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as XkbVariant option", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_XKB_VARIANT;
             entry->value = g_match_info_fetch (match_info, 2);
-        } else if (g_regex_match (xorg_confd_line_xkb_options_re, *linebuf, 0, &match_info)) {
-            g_debug ("Parsed line '%s' as XkbOptions option", *linebuf);
+        } else if (g_regex_match (xorg_confd_line_xkb_options_re, line, 0, &match_info)) {
+            g_debug ("Parsed line '%s' as XkbOptions option", line);
             if (!in_section)
                 goto no_match;
             entry->type = XORG_CONFD_LINE_TYPE_XKB_OPTIONS;
@@ -308,7 +513,7 @@ xorg_confd_parser_new (GFile *xorg_confd_file,
         }
 
         if (entry->type == XORG_CONFD_LINE_TYPE_UNKNOWN)
-            g_debug ("Parsing line '%s' as unknown", *linebuf);
+            g_debug ("Parsing line '%s' as unknown", line);
 
         g_match_info_free (match_info);
         parser->line_list = g_list_prepend (parser->line_list, entry);
@@ -342,7 +547,6 @@ xorg_confd_parser_new (GFile *xorg_confd_file,
 
   out:
     g_free (filebuf);
-    g_strfreev (lines);
     return parser;
 
   parse_fail:
@@ -351,7 +555,6 @@ xorg_confd_parser_new (GFile *xorg_confd_file,
                                    "Unable to parse '%s'", parser->filename));
   fail:
     g_free (filebuf);
-    g_strfreev (lines);
     xorg_confd_parser_free (parser);
     return NULL;
 }
@@ -402,17 +605,24 @@ xorg_confd_parser_line_set_or_delete (GList *line,
 
     if (value == NULL || !g_strcmp0 (value, "")) {
         /* If value is null, we delete the line and return previous one */
-        GList *prev = line->prev;
-        prev->next = line->next;
-        prev->next->prev = prev;
+        g_debug ("Deleting entry '%s'", entry->string);
+        GList *prev, *next;
+
+        prev = line->prev;
+        next = line->next;
         line->prev = NULL;
         line->next = NULL;
         g_list_free_full (line, (GDestroyNotify)xorg_confd_line_entry_free);
+        if (prev != NULL)
+            prev->next = next;
+        if (next != NULL)
+            next->prev = prev;
         return prev;
     }
     entry->value = g_strdup (value);
-    replacement = g_strdup_printf ("\1\"%s\"", value);
-    replaced = g_regex_replace (re, entry->string, 0, 0, replacement, 0, NULL);
+    replacement = g_strdup_printf ("\\1\"%s\"", value);
+    replaced = g_regex_replace (re, entry->string, -1, 0, replacement, 0, NULL);
+    g_debug ("Setting entry '%s' to new value '%s' i.e. '%s'", entry->string, value, replaced);
     g_free (replacement);
     g_free (entry->string);
     entry->string = replaced;
@@ -429,24 +639,25 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser,
 {
     GList *curr = NULL, *end = NULL;
     gboolean layout_found = FALSE, model_found = FALSE, variant_found = FALSE, options_found = FALSE;
+    struct xorg_confd_line_entry *entry = NULL;
+    gchar *string = NULL;
 
     if (parser == NULL)
         return;
 
     if (parser->section == NULL) {
-        struct xorg_confd_line_entry *entry = NULL;
         GList *section = NULL;
 
-        entry = xorg_confd_line_entry_new ("Section \"InputClass\"\n", NULL, XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS);
+        entry = xorg_confd_line_entry_new ("Section \"InputClass\"", NULL, XORG_CONFD_LINE_TYPE_SECTION_INPUT_CLASS);
         section = g_list_prepend (section, entry);
 
-        entry = xorg_confd_line_entry_new ("        Identifier \"keyboard-all\"\n", NULL, XORG_CONFD_LINE_TYPE_UNKNOWN);
+        entry = xorg_confd_line_entry_new ("        Identifier \"keyboard-all\"", NULL, XORG_CONFD_LINE_TYPE_UNKNOWN);
         section = g_list_prepend (section, entry);
 
-        entry = entry = xorg_confd_line_entry_new ("        MatchIsKeyboard \"on\"\n", NULL, XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD);
+        entry = entry = xorg_confd_line_entry_new ("        MatchIsKeyboard \"on\"", NULL, XORG_CONFD_LINE_TYPE_MATCH_IS_KEYBOARD);
         section = g_list_prepend (section, entry);
 
-        entry = entry = xorg_confd_line_entry_new ("EndSection\n", NULL, XORG_CONFD_LINE_TYPE_END_SECTION);
+        entry = entry = xorg_confd_line_entry_new ("EndSection", NULL, XORG_CONFD_LINE_TYPE_END_SECTION);
         section = g_list_prepend (section, entry);
 
         section = g_list_reverse (section);
@@ -455,7 +666,7 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser,
     }
 
     for (curr = parser->section; curr != NULL; curr = curr->next) {
-        struct xorg_confd_line_entry *entry = (struct xorg_confd_line_entry *) curr->data;
+        entry = (struct xorg_confd_line_entry *) curr->data;
 
         if (entry->type == XORG_CONFD_LINE_TYPE_END_SECTION) {
             end = curr;
@@ -474,38 +685,31 @@ xorg_confd_parser_set_xkb (struct xorg_confd_parser *parser,
             curr = xorg_confd_parser_line_set_or_delete (curr, options, xorg_confd_line_xkb_options_re);
         }
     }
-    if (!layout_found && layout != NULL && g_strcmp0 (layout, "")) {
-        struct xorg_confd_line_entry *entry;
-        gchar *string;
 
+    if (!layout_found && layout != NULL && g_strcmp0 (layout, "")) {
         string = g_strdup_printf ("        Option \"XkbLayout\" \"%s\"", layout);
+        g_debug ("Inserting new entry: '%s'", string);
         entry = xorg_confd_line_entry_new (string, layout, XORG_CONFD_LINE_TYPE_XKB_LAYOUT);
         parser->line_list = g_list_insert_before (parser->line_list, end, entry);
         g_free (string);
     }
     if (!model_found && model != NULL && g_strcmp0 (model, "")) {
-        struct xorg_confd_line_entry *entry;
-        gchar *string;
-
         string = g_strdup_printf ("        Option \"XkbModel\" \"%s\"", model);
+        g_debug ("Inserting new entry: '%s'", string);
         entry = xorg_confd_line_entry_new (string, model, XORG_CONFD_LINE_TYPE_XKB_MODEL);
         parser->line_list = g_list_insert_before (parser->line_list, end, entry);
         g_free (string);
     }
     if (!variant_found && variant != NULL && g_strcmp0 (variant, "")) {
-        struct xorg_confd_line_entry *entry;
-        gchar *string;
-
         string = g_strdup_printf ("        Option \"XkbVariant\" \"%s\"", variant);
+        g_debug ("Inserting new entry: '%s'", string);
         entry = xorg_confd_line_entry_new (string, variant, XORG_CONFD_LINE_TYPE_XKB_VARIANT);
         parser->line_list = g_list_insert_before (parser->line_list, end, entry);
         g_free (string);
     }
     if (!options_found && options != NULL && g_strcmp0 (options, "")) {
-        struct xorg_confd_line_entry *entry;
-        gchar *string;
-
         string = g_strdup_printf ("        Option \"XkbOptions\" \"%s\"", options);
+        g_debug ("Inserting new entry: '%s'", string);
         entry = xorg_confd_line_entry_new (string, options, XORG_CONFD_LINE_TYPE_XKB_OPTIONS);
         parser->line_list = g_list_insert_before (parser->line_list, end, entry);
         g_free (string);
@@ -556,19 +760,268 @@ xorg_confd_parser_save (const struct xorg_confd_parser *parser,
 /* End of trivial /etc/X11/xorg.conf.d/30-keyboard.conf parser */
 
 static gboolean
+locale_name_is_valid (gchar *name)
+{
+    return g_regex_match_simple ("^[a-zA-Z0-9_.@-]*$", name, G_REGEX_MULTILINE, 0);
+}
+
+struct invoked_locale {
+    GDBusMethodInvocation *invocation;
+    gchar **locale; /* newly allocated */
+};
+
+static void
+invoked_locale_free (struct invoked_locale *data)
+{
+    if (data == NULL)
+        return;
+    g_strfreev (data->locale);
+    g_free (data);
+}
+
+static void
+on_handle_set_locale_authorized_cb (GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_locale *data;
+    gchar **loc, **var, **val, **locale_values = NULL;
+    ShellUtilsTrivial *locale_file_parsed = NULL;
+
+    data = (struct invoked_locale *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (locale);
+    locale_values = g_new0 (gchar *, g_strv_length (locale_variables) + 1);
+    /* Don't allow unknown locale variables or invalid values */
+    if (data->locale != NULL) {
+        for (loc = data->locale; *loc != NULL; loc++) {
+            gboolean found = FALSE;
+            for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) {
+                size_t varlen;
+                gchar *unquoted = NULL;
+
+                varlen = strlen (*var);
+                if (g_str_has_prefix (*loc, *var) && (*loc)[varlen] == '=' &&
+                    (unquoted = g_shell_unquote (*loc + varlen + 1, NULL)) != NULL &&
+                    locale_name_is_valid (unquoted)) {
+                    found = TRUE;
+                    if (*val != NULL)
+                        g_free (*val);
+                    *val = unquoted;
+                } else
+                    g_free (unquoted);
+            }
+            if (!found) {
+                g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_INVALID_ARGS,
+                                                            "Invalid locale variable name or value");
+                G_UNLOCK (locale);
+                goto out;
+            }
+        }
+    }
+
+    if ((locale_file_parsed = shell_utils_trivial_new (locale_file, &err)) == NULL) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        G_UNLOCK (locale);
+        goto out;
+    }
+
+    if (shell_utils_trivial_is_empty (locale_file_parsed)) {
+        /* Simply write the new env file */
+        shell_utils_trivial_free (locale_file_parsed);
+        if ((locale_file_parsed = shell_utils_trivial_new_from_string (locale_file, "# Configuration file for eselect\n# This file has been automatically generated\n", &err)) == NULL) {
+            g_dbus_method_invocation_return_gerror (data->invocation, err);
+            G_UNLOCK (locale);
+            goto out;
+        }
+    }
+
+    for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) {
+        if (*val == NULL)
+            shell_utils_trivial_clear_variable (locale_file_parsed, *var);
+        else
+            shell_utils_trivial_set_variable (locale_file_parsed, *var, *val, TRUE);
+    }
+
+    if (!shell_utils_trivial_save (locale_file_parsed, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        G_UNLOCK (locale);
+        goto out;
+    }
+
+    g_strfreev (locale);
+    locale = g_new0 (gchar *, g_strv_length (locale_variables) + 1);
+    loc = locale;
+    for (val = locale_values, var = locale_variables; *var != NULL; val++, var++) {
+        if (*val != NULL) {
+            *loc = g_strdup_printf ("%s=%s", *var, *val);
+            loc++;
+        }
+    }
+
+    openrc_settingsd_localed_locale1_complete_set_locale (locale1, data->invocation);
+    openrc_settingsd_localed_locale1_set_locale (locale1, (const gchar * const *) locale);
+    G_UNLOCK (locale);
+
+  out:
+    shell_utils_trivial_free (locale_file_parsed);
+    g_strfreev (locale_values);
+    invoked_locale_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
+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");
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_locale *data;
+        data = g_new0 (struct invoked_locale, 1);
+        data->invocation = invocation;
+        data->locale = g_strdupv ((gchar**)_locale);
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-locale", user_interaction, on_handle_set_locale_authorized_cb, data);
+    }
 
     return TRUE;
 }
 
+struct invoked_vconsole_keyboard {
+    GDBusMethodInvocation *invocation;
+    gchar *vconsole_keymap; /* newly allocated */
+    gchar *vconsole_keymap_toggle; /* newly allocated */
+    gboolean convert;
+};
+
+static void
+invoked_vconsole_keyboard_free (struct invoked_vconsole_keyboard *data)
+{
+    if (data == NULL)
+        return;
+    g_free (data->vconsole_keymap);
+    g_free (data->vconsole_keymap_toggle);
+    g_free (data);
+}
+
+static void
+on_handle_set_vconsole_keyboard_authorized_cb (GObject *source_object,
+                                               GAsyncResult *res,
+                                               gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_vconsole_keyboard *data;
+    GList *kbd_model_map = NULL;
+    struct kbd_model_map_entry *best_entry = NULL;
+    struct xorg_confd_parser *x11_parser = NULL;
+
+    data = (struct invoked_vconsole_keyboard *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (keymaps);
+    if (data->convert) {
+        GList *cur;
+
+        G_LOCK (xorg_conf);
+        kbd_model_map = kbd_model_map_load (&err);
+        if (err != NULL) {
+            g_dbus_method_invocation_return_gerror (data->invocation, err);
+            goto unlock;
+        }
+
+        for (cur = kbd_model_map; cur->next != NULL; cur = cur->next) {
+            struct kbd_model_map_entry *cur_entry = NULL;
+            cur_entry = (struct kbd_model_map_entry *) cur->data;
+            if (kbd_model_map_entry_matches_vconsole (cur_entry, data->vconsole_keymap)) {
+                best_entry = cur_entry;
+                break;
+            }
+        }
+    }
+
+    /* We do not set vconsole_keymap_toggle because there is no good equivalent for it in OpenRC */
+    if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, data->vconsole_keymap, NULL)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto unlock;
+    }
+
+    g_free (vconsole_keymap);
+    vconsole_keymap = g_strdup (data->vconsole_keymap);
+    openrc_settingsd_localed_locale1_set_vconsole_keymap (locale1, vconsole_keymap);
+
+    if (data->convert) {
+        if (best_entry == NULL) {
+            gchar *filename;
+            filename = g_file_get_path (kbd_model_map_file);
+            g_printerr ("Failed to find conversion entry for console keymap '%s' in '%s'\n", data->vconsole_keymap, filename);
+            g_free (filename);
+            G_UNLOCK (xorg_conf);
+        } else {
+            unsigned int failure_score = 0;
+
+            kbd_model_map_entry_matches_x11 (best_entry, x11_layout, x11_model, x11_variant, x11_options, &failure_score);
+            if (failure_score > 0) {
+                /* The xkb data has changed, so we want to update it */
+                if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL))
+                    x11_parser = xorg_confd_parser_new (x11_systemd_file, &err);
+                else
+                    x11_parser = xorg_confd_parser_new (x11_gentoo_file, &err);
+
+                if (x11_parser == NULL) {
+                    g_dbus_method_invocation_return_gerror (data->invocation, err);
+                    goto unlock;
+                }
+                xorg_confd_parser_set_xkb (x11_parser, best_entry->x11_layout, best_entry->x11_model, best_entry->x11_variant, best_entry->x11_options);
+                if (!xorg_confd_parser_save (x11_parser, &err)) {
+                    g_dbus_method_invocation_return_gerror (data->invocation, err);
+                    goto unlock;
+                }
+                g_free (x11_layout);
+                g_free (x11_model);
+                g_free (x11_variant);
+                g_free (x11_options);
+                x11_layout = g_strdup (best_entry->x11_layout);
+                x11_model = g_strdup (best_entry->x11_model);
+                x11_variant = g_strdup (best_entry->x11_variant);
+                x11_options = g_strdup (best_entry->x11_options);
+                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);
+            }
+        }
+    }
+    /* We do not modify vconsole_keymap_toggle because there is no good equivalent for it in OpenRC */
+    openrc_settingsd_localed_locale1_complete_set_vconsole_keyboard (locale1, data->invocation);
+
+  unlock:
+    if (data->convert)
+        G_UNLOCK (xorg_conf);
+    G_UNLOCK (keymaps);
+
+  out:
+    if (kbd_model_map != NULL)
+        g_list_free_full (kbd_model_map, (GDestroyNotify)kbd_model_map_entry_free);
+    xorg_confd_parser_free (x11_parser);
+    invoked_vconsole_keyboard_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
 static gboolean
 on_handle_set_vconsole_keyboard (OpenrcSettingsdLocaledLocale1 *locale1,
                                  GDBusMethodInvocation *invocation,
@@ -578,13 +1031,146 @@ on_handle_set_vconsole_keyboard (OpenrcSettingsdLocaledLocale1 *locale1,
                                  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");
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_vconsole_keyboard *data;
+        data = g_new0 (struct invoked_vconsole_keyboard, 1);
+        data->invocation = invocation;
+        data->vconsole_keymap = g_strdup (keymap);
+        data->vconsole_keymap_toggle = g_strdup (keymap_toggle);
+        data->convert = convert;
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-keyboard", user_interaction, on_handle_set_vconsole_keyboard_authorized_cb, data);
+    }
 
     return TRUE;
 }
 
+struct invoked_x11_keyboard {
+    GDBusMethodInvocation *invocation;
+    gchar *x11_layout; /* newly allocated */
+    gchar *x11_model; /* newly allocated */
+    gchar *x11_variant; /* newly allocated */
+    gchar *x11_options; /* newly allocated */
+    gboolean convert;
+};
+
+static void
+invoked_x11_keyboard_free (struct invoked_x11_keyboard *data)
+{
+    if (data == NULL)
+        return;
+    g_free (data->x11_layout);
+    g_free (data->x11_model);
+    g_free (data->x11_variant);
+    g_free (data->x11_options);
+    g_free (data);
+}
+
+static void
+on_handle_set_x11_keyboard_authorized_cb (GObject *source_object,
+                                          GAsyncResult *res,
+                                          gpointer user_data)
+{
+    GError *err = NULL;
+    struct invoked_x11_keyboard *data;
+    GList *kbd_model_map = NULL;
+    struct kbd_model_map_entry *best_entry = NULL;
+    unsigned int best_failure_score = UINT_MAX;
+    struct xorg_confd_parser *x11_parser = NULL;
+
+    data = (struct invoked_x11_keyboard *) user_data;
+    if (!check_polkit_finish (res, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto out;
+    }
+
+    G_LOCK (xorg_conf);
+    if (data->convert) {
+        GList *cur;
+
+        G_LOCK (keymaps);
+        kbd_model_map = kbd_model_map_load (&err);
+        if (err != NULL) {
+            g_dbus_method_invocation_return_gerror (data->invocation, err);
+            goto unlock;
+        }
+
+        for (cur = kbd_model_map; cur->next != NULL; cur = cur->next) {
+            struct kbd_model_map_entry *cur_entry = NULL;
+            unsigned int cur_failure_score = 0;
+
+            cur_entry = (struct kbd_model_map_entry *) cur->data;
+            if (kbd_model_map_entry_matches_x11 (cur_entry, data->x11_layout, data->x11_model, data->x11_variant, data->x11_options, &cur_failure_score))
+                if (cur_failure_score < best_failure_score) {
+                    best_entry = cur_entry;
+                    best_failure_score = cur_failure_score;
+                }
+        }
+    }
+
+    if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL))
+        x11_parser = xorg_confd_parser_new (x11_systemd_file, &err);
+    else
+        x11_parser = xorg_confd_parser_new (x11_gentoo_file, &err);
+
+    if (x11_parser == NULL) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto unlock;
+    }
+    xorg_confd_parser_set_xkb (x11_parser, data->x11_layout, data->x11_model, data->x11_variant, data->x11_options);
+    if (!xorg_confd_parser_save (x11_parser, &err)) {
+        g_dbus_method_invocation_return_gerror (data->invocation, err);
+        goto unlock;
+    }
+    g_free (x11_layout);
+    g_free (x11_model);
+    g_free (x11_variant);
+    g_free (x11_options);
+    x11_layout = g_strdup (data->x11_layout);
+    x11_model = g_strdup (data->x11_model);
+    x11_variant = g_strdup (data->x11_variant);
+    x11_options = g_strdup (data->x11_options);
+    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);
+
+    if (data->convert) {
+        if (best_entry == NULL) {
+            gchar *filename;
+            filename = g_file_get_path (kbd_model_map_file);
+            g_printerr ("Failed to find conversion entry for x11 layout '%s' in '%s'\n", data->x11_layout, filename);
+            g_free (filename);
+        } else {
+            if (!shell_utils_trivial_set_and_save (keymaps_file, &err, "keymap", NULL, best_entry->vconsole_keymap, NULL)) {
+                g_dbus_method_invocation_return_gerror (data->invocation, err);
+                goto unlock;
+            }
+            g_free (vconsole_keymap);
+            vconsole_keymap = g_strdup (best_entry->vconsole_keymap);
+            openrc_settingsd_localed_locale1_set_vconsole_keymap (locale1, vconsole_keymap);
+        }
+    }
+
+    openrc_settingsd_localed_locale1_complete_set_x11_keyboard (locale1, data->invocation);
+
+  unlock:
+    if (data->convert)
+        G_UNLOCK (keymaps);
+    G_UNLOCK (xorg_conf);
+
+  out:
+    if (kbd_model_map != NULL)
+        g_list_free_full (kbd_model_map, (GDestroyNotify)kbd_model_map_entry_free);
+    xorg_confd_parser_free (x11_parser);
+    invoked_x11_keyboard_free (data);
+    if (err != NULL)
+        g_error_free (err);
+}
+
 static gboolean
 on_handle_set_x11_keyboard (OpenrcSettingsdLocaledLocale1 *locale1,
                             GDBusMethodInvocation *invocation,
@@ -596,9 +1182,21 @@ on_handle_set_x11_keyboard (OpenrcSettingsdLocaledLocale1 *locale1,
                             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");
+    if (read_only)
+        g_dbus_method_invocation_return_dbus_error (invocation,
+                                                    DBUS_ERROR_NOT_SUPPORTED,
+                                                    SERVICE_NAME " is in read-only mode");
+    else {
+        struct invoked_x11_keyboard *data;
+        data = g_new0 (struct invoked_x11_keyboard, 1);
+        data->invocation = invocation;
+        data->x11_layout = g_strdup (layout);
+        data->x11_model = g_strdup (model);
+        data->x11_variant = g_strdup (variant);
+        data->x11_options = g_strdup (options);
+        data->convert = convert;
+        check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.locale1.set-keyboard", user_interaction, on_handle_set_x11_keyboard_authorized_cb, data);
+    }
 
     return TRUE;
 }
@@ -666,6 +1264,7 @@ localed_init (gboolean _read_only)
     struct xorg_confd_parser *x11_parser = NULL;
 
     read_only = _read_only;
+    kbd_model_map_file = g_file_new_for_path (PKGDATADIR "/kbd-model-map");
     locale_file = g_file_new_for_path (SYSCONFDIR "/env.d/02locale");
     keymaps_file = g_file_new_for_path (SYSCONFDIR "/conf.d/keymaps");
 
@@ -703,6 +1302,7 @@ localed_init (gboolean _read_only)
     /* We don't have a good equivalent for this in openrc at the moment */
     vconsole_keymap_toggle = g_strdup ("");
 
+    kbd_model_map_regex_init ();
     xorg_confd_regex_init ();
 
     if (!g_file_query_exists (x11_gentoo_file, NULL) && g_file_query_exists (x11_systemd_file, NULL))
@@ -735,6 +1335,7 @@ localed_destroy (void)
     bus_id = 0;
     read_only = FALSE;
     g_strfreev (locale);
+    kbd_model_map_regex_destroy ();
     xorg_confd_regex_destroy ();
     g_free (vconsole_keymap);
     g_free (vconsole_keymap_toggle);
@@ -747,4 +1348,5 @@ localed_destroy (void)
     g_object_unref (keymaps_file);
     g_object_unref (x11_gentoo_file);
     g_object_unref (x11_systemd_file);
+    g_object_unref (kbd_model_map_file);
 }

diff --git a/src/shell-utils.c b/src/shell-utils.c
index db67303..545c5dd 100644
--- a/src/shell-utils.c
+++ b/src/shell-utils.c
@@ -131,31 +131,51 @@ shell_utils_trivial_new (GFile *file,
                          GError **error)
 {
     gchar *filebuf = NULL;
-    ShellUtilsTrivial *ret = NULL;
     GError *local_err = NULL;
-    gchar *s;
 
     if (file == NULL)
         return NULL;
 
-    ret = g_new0 (ShellUtilsTrivial, 1);
-    g_object_ref (file);
-    ret->file = file;
-    ret->filename = g_file_get_path (file);
-
     if (!g_file_load_contents (file, NULL, &filebuf, NULL, NULL, &local_err)) {
         if (local_err != NULL) {
             /* Inability to parse or open is a failure; file not existing at all is *not* a failure */
             if (local_err->code == G_IO_ERROR_NOT_FOUND) {
+                ShellUtilsTrivial *ret = NULL;
+
                 g_error_free (local_err);
+                ret = g_new0 (ShellUtilsTrivial, 1);
+                g_object_ref (file);
+                ret->file = file;
+                ret->filename = g_file_get_path (file);
                 return ret;
+            } else {
+                gchar *filename;
+                filename = g_file_get_path (file);
+                g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", filename);
+                g_free (filename);
             }
-
-            g_propagate_prefixed_error (error, local_err, "Unable to read '%s':", ret->filename);
         }
-        shell_utils_trivial_free (ret);
         return NULL;
     }
+    return shell_utils_trivial_new_from_string (file, filebuf, error);
+}
+
+ShellUtilsTrivial *
+shell_utils_trivial_new_from_string (GFile *file,
+                                     gchar *filebuf,
+                                     GError **error)
+{
+    ShellUtilsTrivial *ret = NULL;
+    GError *local_err = NULL;
+    gchar *s;
+
+    if (file == NULL || filebuf == NULL)
+        return NULL;
+
+    ret = g_new0 (ShellUtilsTrivial, 1);
+    g_object_ref (file);
+    ret->file = file;
+    ret->filename = g_file_get_path (file);
 
     gboolean want_separator = FALSE; /* Do we expect the next entry to be a separator or comment? */
     s = filebuf;
@@ -308,6 +328,14 @@ no_match:
 }
 
 gboolean
+shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial)
+{
+    if (trivial == NULL || trivial->entry_list == NULL)
+        return TRUE;
+    return FALSE;
+}
+
+gboolean
 shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial,
                                   const gchar *variable,
                                   const gchar *value,
@@ -350,6 +378,38 @@ shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial,
     return ret;
 }
 
+void
+shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial,
+                                    const gchar *variable)
+{
+    GList *curr = NULL;
+    gboolean ret = FALSE;
+
+    g_assert (trivial != NULL);
+    g_assert (variable != NULL);
+
+    for (curr = trivial->entry_list; curr != NULL; ) {
+        struct ShellEntry *entry;
+
+        entry = (struct ShellEntry *)(curr->data);
+        if (entry->type == SHELL_ENTRY_TYPE_ASSIGNMENT && g_strcmp0 (variable, entry->variable) == 0) {
+            GList *prev, *next;
+
+            prev = curr->prev;
+            next = curr->next;
+            curr->prev = NULL;
+            curr->next = NULL;
+            g_list_free_full (curr, (GDestroyNotify)shell_entry_free);
+            if (prev != NULL)
+                prev->next = next;
+            if (next != NULL)
+                next->prev = prev;
+            curr = next;
+        } else
+            curr = curr->next;
+    }
+}
+
 gboolean
 shell_utils_trivial_save (ShellUtilsTrivial *trivial,
                           GError **error)

diff --git a/src/shell-utils.h b/src/shell-utils.h
index 9ca40e9..629d017 100644
--- a/src/shell-utils.h
+++ b/src/shell-utils.h
@@ -22,7 +22,7 @@
 #include <glib.h>
 #include <gio/gio.h>
 
-typedef struct _ShellUtilsTrivial        ShellUtilsTrivial;
+typedef struct _ShellUtilsTrivial ShellUtilsTrivial;
 
 struct _ShellUtilsTrivial
 {
@@ -46,15 +46,27 @@ ShellUtilsTrivial *
 shell_utils_trivial_new (GFile *file,
                          GError **error);
 
+ShellUtilsTrivial *
+shell_utils_trivial_new_from_string (GFile *file,
+                                     gchar *filebuf,
+                                     GError **error);
+
 void
 shell_utils_trivial_free (ShellUtilsTrivial *trivial);
 
 gboolean
+shell_utils_trivial_is_empty (ShellUtilsTrivial *trivial);
+
+gboolean
 shell_utils_trivial_set_variable (ShellUtilsTrivial *trivial,
                                   const gchar *variable,
                                   const gchar *value,
                                   gboolean add_if_unset);
 
+void
+shell_utils_trivial_clear_variable (ShellUtilsTrivial *trivial,
+                                    const gchar *variable);
+
 gboolean
 shell_utils_trivial_save (ShellUtilsTrivial *trivial,
                           GError **error);


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, data/
@ 2012-09-07 16:04 Alexandre Restovtsev
  0 siblings, 0 replies; 3+ messages in thread
From: Alexandre Restovtsev @ 2012-09-07 16:04 UTC (permalink / raw
  To: gentoo-commits

commit:     a14f465d4576bf5895f67c3a7d68d862acda149c
Author:     Alexandre Rostovtsev <tetromino <AT> gentoo <DOT> org>
AuthorDate: Fri Sep  7 15:49:54 2012 +0000
Commit:     Alexandre Restovtsev <tetromino <AT> gmail <DOT> com>
CommitDate: Fri Sep  7 15:49:54 2012 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git;a=commit;h=a14f465d

Add man page and homepage information, and --version support

---
 Makefile.am                           |    2 +
 README                                |   10 +++--
 configure.ac                          |    2 +-
 data/openrc-settingsd.8               |   67 +++++++++++++++++++++++++++++++++
 data/org.freedesktop.hostname1.policy |    2 +-
 data/org.freedesktop.locale1.policy   |    2 +-
 data/org.freedesktop.timedate1.policy |    2 +-
 src/main.c                            |    7 +++
 8 files changed, 86 insertions(+), 8 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 0802620..bdfb3dd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -60,6 +60,8 @@ data/init.d/openrc-settingsd : data/init.d/openrc-settingsd.in
 %.service : %.service.in
 	$(do_subst) < $(srcdir)/$< > $(srcdir)/$@
 
+dist_man_MANS = data/openrc-settingsd.8
+
 AM_CPPFLAGS = \
 	-include $(top_builddir)/config.h \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \

diff --git a/README b/README
index e279f16..344fea5 100644
--- a/README
+++ b/README
@@ -1,10 +1,12 @@
-openrc-settingsd provides an implementation of the the hostnamed, localed,
+OpenRC-settingsd provides an implementation of the the hostnamed, localed,
 and timedated D-Bus services for OpenRC-based systems - in particular, for
 typical installations of Gentoo Linux.
 
 It is maintained by Gentoo's GNOME desktop team, which can be contacted
 via gnome@gentoo.org, or in #gentoo-desktop on Freenode IRC.
 
+Homepage: http://gnome.gentoo.org/openrc-settingsd.xml
+
 Bugs should be reported to Gentoo Bugzilla (https://bugs.gentoo.org/)
 using "GNOME" as the component.
 
@@ -50,10 +52,10 @@ Timedated:
 
   The timezone is set in /etc/timezone and /etc/localtime.
 
-  openrc-settingsd attempts to auto-detect an appropriate ntp implementation.
+  OpenRC-settingsd attempts to auto-detect an appropriate ntp implementation.
   To avoid auto-detection, use the --ntp-service command line option.
 
-Note that openrc-settingsd expects any shell-syntax settings files that it
+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
 
@@ -61,5 +63,5 @@ scalar assignments, i.e. something like
 foo="bar"
 baz='Let'\''s go!'
 
-If openrc-settingsd fails to parse a settings file, it will refuse to modify
+If OpenRC-settingsd fails to parse a settings file, it will refuse to modify
 it.

diff --git a/configure.ac b/configure.ac
index a7ccb04..3e0480a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
 AC_PREREQ([2.60])
-AC_INIT([openrc-settingsd], [0])
+AC_INIT([openrc-settingsd], [0], [https://bugs.gentoo.org/], [openrc-settingsd], [http://gnome.gentoo.org/openrc-settingsd.xml])
 AC_CONFIG_SRCDIR([src/main.c])
 AM_INIT_AUTOMAKE([check-news foreign 1.11 -Wall -Wno-portability silent-rules tar-pax no-dist-gzip dist-xz])
 AC_PROG_CC_STDC

diff --git a/data/openrc-settingsd.8 b/data/openrc-settingsd.8
new file mode 100644
index 0000000..6e4666c
--- /dev/null
+++ b/data/openrc-settingsd.8
@@ -0,0 +1,67 @@
+.TH "OPENRC-SETTINGSD" "8" "September 2012" "OpenRC-settingsd" "openrc-settingsd"
+.SH "NAME"
+openrc\-settingsd \- system settings D\-Bus service for OpenRC
+.SH "SYNOPSIS"
+\fBopenrc\-settingsd\fR [\fB\-\-help\fR] [\fB\-\-debug\fR] [\fB\-\-read\-only\fR]
+[\fB\-\-foreground\fR] [\fB\-\-update\-rc\-status\fR] [\fB\-\-ntp\-service\fR=\fISERVICE\fR]
+.SH "DESCRIPTION"
+.PP
+The \fBopenrc\-settingsd\fR daemon implements the standard hostnamed (i.e.
+\fIorg.freedesktop.hostname1\fR), localed (i.e. \fIorg.freedesktop.locale1\fR), and
+timedated (\fIorg.freedesktop.timedate1\fR) D\-Bus interfaces for OpenRC systems.
+Users and administrators should not need to launch the \fBopenrc\-settingsd\fR
+executable manually. Depending on the installation, it will either be launched
+automatically via D\-Bus activation when needed, or started by the administrator
+as an OpenRC service using \fI/etc/init.d/openrc\-settingsd\fR.    
+.SH "OPTIONS"
+.PP
+\fB\-\-help\fR
+.RS 4
+Show help options.
+.RE
+.PP
+\fB\-\-debug\fR
+.RS 4
+Enable debugging messages. Not recommended unless in foreground mode.
+.RE
+.PP
+\fB\-\-read\-only\fR
+.RS 4
+Run daemon in read-only mode. It will read settings files, but will not
+modify them.
+.RE
+.PP
+\fB\-\-foreground\fR
+.RS 4
+Run in foreground mode (i.e. not daemonized). Messages will be logged to stderr
+instead of the system log.
+.RE
+.PP
+\fB\-\-update\-rc\-status\fR
+.RS 4
+Automatically set the status of the \fIopenrc\-settingsd\fR service to \fIstarted\fR
+when the daemon successfully starts, and to \fIstopped\fR when the daemon stops. If
+\fBopenrc\-settingsd\fR is manually launched with this argument, the administrator
+will be able to stop it via \fI/etc/init.d/openrc\-settingsd\fR\ \fIstop\fR.
+.RE
+.PP
+\fB\-\-ntp\-service\fR=\fISERVICE\fR
+.RS 4
+Specify which OpenRC service to use as the NTP service. If this option is not used,
+\fBopenrc\-settingsd\fR will attempt to autodetect an appropriate ntp implementation.
+.RE
+.SH "AUTHORS"
+.PP
+Written by
+.MT tetromino@gentoo.org
+Alexandre Rostovtsev
+.ME .
+.SH "BUGS"
+.PP
+Bug reports should be submitted to the
+.UR https://bugs.gentoo.org/
+Gentoo Bugzilla
+.UE ; use \fIGNOME\fR as the component.
+.SH "SEE ALSO"
+.PP
+\fBdbus\-daemon\fR(1), \fBpolkit\fR(8), \fBrc\fR(8)
\ No newline at end of file

diff --git a/data/org.freedesktop.hostname1.policy b/data/org.freedesktop.hostname1.policy
index 74851e2..4717ce1 100644
--- a/data/org.freedesktop.hostname1.policy
+++ b/data/org.freedesktop.hostname1.policy
@@ -3,7 +3,7 @@
 
 <policyconfig>
     <vendor>Gentoo Linux</vendor>
-    <vendor_url>http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git</vendor_url>
+    <vendor_url>http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml</vendor_url>
 
     <action id="org.freedesktop.hostname1.set-hostname">
         <description>Set local host name</description>

diff --git a/data/org.freedesktop.locale1.policy b/data/org.freedesktop.locale1.policy
index e56cc74..640ea39 100644
--- a/data/org.freedesktop.locale1.policy
+++ b/data/org.freedesktop.locale1.policy
@@ -3,7 +3,7 @@
 
 <policyconfig>
     <vendor>Gentoo Linux</vendor>
-    <vendor_url>http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git</vendor_url>
+    <vendor_url>http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml</vendor_url>
 
     <action id="org.freedesktop.locale1.set-locale">
         <description>Set system locale environment variables</description>

diff --git a/data/org.freedesktop.timedate1.policy b/data/org.freedesktop.timedate1.policy
index 9bc276b..fafafed 100644
--- a/data/org.freedesktop.timedate1.policy
+++ b/data/org.freedesktop.timedate1.policy
@@ -3,7 +3,7 @@
 
 <policyconfig>
     <vendor>Gentoo Linux</vendor>
-    <vendor_url>http://git.overlays.gentoo.org/gitweb/?p=proj/openrc-settingsd.git</vendor_url>
+    <vendor_url>http://www.gentoo.org/proj/en/desktop/gnome/openrc-settingsd.xml</vendor_url>
 
     <action id="org.freedesktop.timedate1.set-time">
         <description>Set system time</description>

diff --git a/src/main.c b/src/main.c
index 8374218..78b6013 100644
--- a/src/main.c
+++ b/src/main.c
@@ -42,6 +42,7 @@ static gboolean foreground = FALSE;
 static gboolean use_syslog = FALSE;
 static gboolean read_only = FALSE;
 static gboolean update_rc_status = FALSE;
+static gboolean print_version = FALSE;
 static gchar *ntp_preferred_service = NULL;
 
 static guint components_started = 0;
@@ -56,6 +57,7 @@ static GOptionEntry option_entries[] =
     { "read-only", 0, 0, G_OPTION_ARG_NONE, &read_only, "Run in read-only mode", NULL },
     { "ntp-service", 0, 0, G_OPTION_ARG_STRING, &ntp_preferred_service, "Preferred rc NTP service for timedated", NULL },
     { "update-rc-status", 0, 0, G_OPTION_ARG_NONE, &update_rc_status, "Force openrc-settingsd rc service to be marked as started", NULL },
+    { "version", 0, 0, G_OPTION_ARG_NONE, &print_version, "Show version information", NULL },
     { NULL }
 };
 
@@ -209,6 +211,11 @@ main (gint argc, gchar *argv[])
         return 1;
     }
 
+    if (print_version) {
+        g_print ("%s\n", PACKAGE_STRING);
+        return 0;
+    }
+
     if (!foreground) {
         if (daemon_retval_init () < 0) {
             g_critical ("Failed to create pipe");


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2012-09-07 16:05 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-07 16:04 [gentoo-commits] proj/openrc-settingsd:master commit in: src/, /, data/ Alexandre Restovtsev
  -- strict thread matches above, loose matches on Subject: below --
2012-09-05  5:33 Alexandre Restovtsev
2012-02-09  8:42 Alexandre Restovtsev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox