[systemd-commits] 2 commits - Makefile.am src/generate-kbd-model-map src/kbd-model-map src/localed.c src/org.freedesktop.locale1.policy.in src/util.c src/util.h src/vconsole-setup.c

Lennart Poettering lennart at kemper.freedesktop.org
Tue Sep 27 19:34:29 PDT 2011


 Makefile.am                           |   10 
 src/generate-kbd-model-map            |   49 ++
 src/kbd-model-map                     |   72 +++
 src/localed.c                         |  764 +++++++++++++++++++++++++++++++++-
 src/org.freedesktop.locale1.policy.in |   10 
 src/util.c                            |   18 
 src/util.h                            |    2 
 src/vconsole-setup.c                  |    4 
 8 files changed, 919 insertions(+), 10 deletions(-)

New commits:
commit e99fa3cba5c0a07bdcea22a308bf9b973e88b624
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Sep 28 04:25:20 2011 +0200

    vconsole: don't parse Fedora's KEYMAP= kernel parameters anymore
    
    KEYMAP are use to pass keymap configuration to initrd, but not to the
    system itself. Since the initrd might get out of date we need to make
    sure that changes made in userspace override the settings from the
    cmdline, hence drpo any use of it all for these variables.

diff --git a/src/vconsole-setup.c b/src/vconsole-setup.c
index 8a89358..c5f3628 100644
--- a/src/vconsole-setup.c
+++ b/src/vconsole-setup.c
@@ -203,10 +203,6 @@ int main(int argc, char **argv) {
 
         if (detect_container(NULL) <= 0)
                 if ((r = parse_env_file("/proc/cmdline", WHITESPACE,
-#if defined(TARGET_FEDORA) || defined(TARGET_MEEGO)
-                                        "SYSFONT", &vc_font,
-                                        "KEYTABLE", &vc_keymap,
-#endif
                                         "vconsole.keymap", &vc_keymap,
                                         "vconsole.keymap.toggle", &vc_keymap_toggle,
                                         "vconsole.font", &vc_font,

commit fb9de93dd3587e62e0fc0413673c98ea709c5a2f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Wed Sep 28 04:25:13 2011 +0200

    localed: add SetX11Keyboard() and SetVConsoleKeyboard() bus calls

diff --git a/Makefile.am b/Makefile.am
index 66a64fd..570b6be 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -76,6 +76,7 @@ AM_CPPFLAGS = \
 	-DSYSTEM_GENERATOR_PATH=\"$(systemgeneratordir)\" \
 	-DUSER_GENERATOR_PATH=\"$(usergeneratordir)\" \
 	-DSYSTEM_SHUTDOWN_PATH=\"$(systemshutdowndir)\" \
+        -DSYSTEMD_KBD_MODEL_MAP=\"$(pkgdatadir)/kbd-model-map\"
 	-I $(top_srcdir)/src
 
 if TARGET_GENTOO
@@ -1031,6 +1032,12 @@ systemd_localed_LDADD = \
 	libsystemd-daemon.la \
 	$(DBUS_LIBS)
 
+dist_pkgdata_DATA = \
+        src/kbd-model-map
+
+dist_noinst_SCRIPT = \
+        src/generate-kbd-model-map
+
 systemd_timedated_SOURCES = \
 	src/timedated.c \
         src/dbus-common.c \
@@ -2016,3 +2023,6 @@ upload: all distcheck
 
 git-tag:
 	git tag "v$(VERSION)" -m "systemd $(VERSION)"
+
+update-kbd-model-map:
+	src/generate-kbd-model-map > src/kbd-model-map
diff --git a/src/generate-kbd-model-map b/src/generate-kbd-model-map
new file mode 100755
index 0000000..4fcf785
--- /dev/null
+++ b/src/generate-kbd-model-map
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+
+import system_config_keyboard.keyboard_models, sys
+
+def strdash(s):
+        r = s.strip()
+
+        if r == "":
+                return "-"
+
+        return r
+
+def tab_extend(s, n = 1):
+
+        s = strdash(s)
+        k = len(s) / 8
+
+        if k >= n:
+                f = 1
+        else:
+                f = n - k
+
+        for x in range(0, f):
+                s = s + "\t"
+
+        return s
+
+
+
+models = system_config_keyboard.keyboard_models.KeyboardModels().get_models()
+
+print "# Generated from system-config-keyboard's model list"
+
+print "# consolelayout\t\txlayout\txmodel\t\txvariant\txoptions"
+
+k = models.keys()
+
+k.reverse()
+
+for key in k:
+        value = models[key]
+
+        options = value[4]
+        if len(options) > 0:
+                options = "terminate:ctrl_alt_bksp," + options
+        else:
+                options = "terminate:ctrl_alt_bksp"
+
+        print "%s%s%s%s%s" % (tab_extend(key, 3), tab_extend(value[1]), tab_extend(value[2], 2), tab_extend(value[3], 2), options)
diff --git a/src/kbd-model-map b/src/kbd-model-map
new file mode 100644
index 0000000..a895880
--- /dev/null
+++ b/src/kbd-model-map
@@ -0,0 +1,72 @@
+# 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 fbb5a41..16557b6 100644
--- a/src/localed.c
+++ b/src/localed.c
@@ -34,10 +34,30 @@
 #define INTERFACE                                                       \
         " <interface name=\"org.freedesktop.locale1\">\n"               \
         "  <property name=\"Locale\" type=\"as\" access=\"read\"/>\n"   \
+        "  <property name=\"VConsoleKeymap\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"VConsoleKeymapToggle\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"X11Layout\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"X11Model\" type=\"s\" access=\"read\"/>\n"  \
+        "  <property name=\"X11Variant\" type=\"s\" access=\"read\"/>\n" \
+        "  <property name=\"X11Options\" type=\"s\" access=\"read\"/>\n" \
         "  <method name=\"SetLocale\">\n"                               \
         "   <arg name=\"locale\" type=\"as\" direction=\"in\"/>\n"      \
         "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
         "  </method>\n"                                                 \
+        "  <method name=\"SetVConsoleKeyboard\">\n"                      \
+        "   <arg name=\"keymap\" type=\"s\" direction=\"in\"/>\n"       \
+        "   <arg name=\"keymap_toggle\" type=\"s\" direction=\"in\"/>\n" \
+        "   <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n"      \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
+        "  <method name=\"SetX11Keyboard\">\n"                          \
+        "   <arg name=\"layout\" type=\"s\" direction=\"in\"/>\n"       \
+        "   <arg name=\"model\" type=\"s\" direction=\"in\"/>\n"        \
+        "   <arg name=\"variant\" type=\"s\" direction=\"in\"/>\n"      \
+        "   <arg name=\"options\" type=\"s\" direction=\"in\"/>\n"      \
+        "   <arg name=\"convert\" type=\"b\" direction=\"in\"/>\n"      \
+        "   <arg name=\"user_interaction\" type=\"b\" direction=\"in\"/>\n" \
+        "  </method>\n"                                                 \
         " </interface>\n"
 
 #define INTROSPECTION                                                   \
@@ -109,9 +129,28 @@ static char *data[_PROP_MAX] = {
         NULL
 };
 
+static char *x11_layout = NULL, *x11_model = NULL, *x11_variant = NULL, *x11_options = NULL;
+static char *vc_keymap = NULL, *vc_keymap_toggle = NULL;
+
 static usec_t remain_until = 0;
 
-static void free_data(void) {
+static int free_and_set(char **s, const char *v) {
+        int r;
+        char *t;
+
+        assert(s);
+
+        r = strdup_or_null(isempty(v) ? NULL : v, &t);
+        if (r < 0)
+                return r;
+
+        free(*s);
+        *s = t;
+
+        return 0;
+}
+
+static void free_data_locale(void) {
         int p;
 
         for (p = 0; p < _PROP_MAX; p++) {
@@ -120,6 +159,22 @@ static void free_data(void) {
         }
 }
 
+static void free_data_x11(void) {
+        free(x11_layout);
+        free(x11_model);
+        free(x11_variant);
+        free(x11_options);
+
+        x11_layout = x11_model = x11_variant = x11_options = NULL;
+}
+
+static void free_data_vconsole(void) {
+        free(vc_keymap);
+        free(vc_keymap_toggle);
+
+        vc_keymap = vc_keymap_toggle = NULL;
+}
+
 static void simplify(void) {
         int p;
 
@@ -130,10 +185,10 @@ static void simplify(void) {
                 }
 }
 
-static int read_data(void) {
+static int read_data_locale(void) {
         int r;
 
-        free_data();
+        free_data_locale();
 
         r = parse_env_file("/etc/locale.conf", NEWLINE,
                            "LANG",              &data[PROP_LANG],
@@ -181,7 +236,129 @@ static int read_data(void) {
         return r;
 }
 
-static int write_data(void) {
+static void free_data(void) {
+        free_data_locale();
+        free_data_vconsole();
+        free_data_x11();
+}
+
+static int read_data_vconsole(void) {
+        int r;
+
+        free_data_vconsole();
+
+        r = parse_env_file("/etc/vconsole.conf", NEWLINE,
+                           "KEYMAP",        &vc_keymap,
+                           "KEYMAP_TOGGLE", &vc_keymap_toggle,
+                           NULL);
+
+        if (r < 0 && r != -ENOENT)
+                return r;
+
+        return 0;
+}
+
+static int read_data_x11(void) {
+        FILE *f;
+        char line[LINE_MAX];
+        bool in_section = false;
+
+        free_data_x11();
+
+        f = fopen("/etc/X11/xorg.conf.d/00-keyboard.conf", "re");
+        if (!f) {
+                if (errno == ENOENT) {
+
+#ifdef TARGET_FEDORA
+                        f = fopen("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf", "re");
+                        if (!f) {
+                                if (errno == ENOENT)
+                                        return 0;
+                                else
+                                        return -errno;
+                        }
+#else
+                        return 0;
+#endif
+
+                } else
+                          return -errno;
+        }
+
+        while (fgets(line, sizeof(line), f)) {
+                char *l;
+
+                char_array_0(line);
+                l = strstrip(line);
+
+                if (l[0] == 0 || l[0] == '#')
+                        continue;
+
+                if (in_section && first_word(l, "Option")) {
+                        char **a;
+
+                        a = strv_split_quoted(l);
+                        if (!a) {
+                                fclose(f);
+                                return -ENOMEM;
+                        }
+
+                        if (strv_length(a) == 3) {
+
+                                if (streq(a[1], "XkbLayout")) {
+                                        free(x11_layout);
+                                        x11_layout = a[2];
+                                        a[2] = NULL;
+                                } else if (streq(a[1], "XkbModel")) {
+                                        free(x11_model);
+                                        x11_model = a[2];
+                                        a[2] = NULL;
+                                } else if (streq(a[1], "XkbVariant")) {
+                                        free(x11_variant);
+                                        x11_variant = a[2];
+                                        a[2] = NULL;
+                                } else if (streq(a[1], "XkbOptions")) {
+                                        free(x11_options);
+                                        x11_options = a[2];
+                                        a[2] = NULL;
+                                }
+                        }
+
+                        strv_free(a);
+
+                } else if (!in_section && first_word(l, "Section")) {
+                        char **a;
+
+                        a = strv_split_quoted(l);
+                        if (!a) {
+                                fclose(f);
+                                return -ENOMEM;
+                        }
+
+                        if (strv_length(a) == 2 && streq(a[1], "InputClass"))
+                                in_section = true;
+
+                        strv_free(a);
+                } else if (in_section && first_word(l, "EndSection"))
+                        in_section = false;
+        }
+
+        fclose(f);
+
+        return 0;
+}
+
+static int read_data(void) {
+        int r, q, p;
+
+        r = read_data_locale();
+        q = read_data_vconsole();
+        p = read_data_x11();
+
+        return r < 0 ? r : q < 0 ? q : p;
+}
+
+static int write_data_locale(void) {
         int r, p;
         char **l = NULL;
 
@@ -320,6 +497,446 @@ finish:
         free(l_unset);
 }
 
+static int write_data_vconsole(void) {
+        int r;
+        char **l = NULL;
+
+        r = load_env_file("/etc/vconsole.conf", &l);
+        if (r < 0 && r != -ENOENT)
+                return r;
+
+        if (isempty(vc_keymap))
+                l = strv_env_unset(l, "KEYMAP");
+        else {
+                char *s, **u;
+
+                s = strappend("KEYMAP=", vc_keymap);
+                if (!s) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+                u = strv_env_set(l, s);
+                free(s);
+                strv_free(l);
+
+                if (!u)
+                        return -ENOMEM;
+
+                l = u;
+        }
+
+        if (isempty(vc_keymap_toggle))
+                l = strv_env_unset(l, "KEYMAP_TOGGLE");
+        else  {
+                char *s, **u;
+
+                s = strappend("KEYMAP_TOGGLE=", vc_keymap_toggle);
+                if (!s) {
+                        strv_free(l);
+                        return -ENOMEM;
+                }
+
+                u = strv_env_set(l, s);
+                free(s);
+                strv_free(l);
+
+                if (!u)
+                        return -ENOMEM;
+
+                l = u;
+        }
+
+        if (strv_isempty(l)) {
+                strv_free(l);
+
+                if (unlink("/etc/vconsole.conf") < 0)
+                        return errno == ENOENT ? 0 : -errno;
+
+                return 0;
+        }
+
+        r = write_env_file("/etc/vconsole.conf", l);
+        strv_free(l);
+
+        return r;
+}
+
+static int write_data_x11(void) {
+        FILE *f;
+        char *temp_path;
+        int r;
+
+        if (isempty(x11_layout) &&
+            isempty(x11_model) &&
+            isempty(x11_variant) &&
+            isempty(x11_options)) {
+
+#ifdef TARGET_FEDORA
+                unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
+#endif
+
+                if (unlink("/etc/X11/xorg.conf.d/00-keyboard.conf") < 0)
+                        return errno == ENOENT ? 0 : -errno;
+
+                return 0;
+        }
+
+        mkdir_parents("/etc/X11/xorg.conf.d", 0755);
+
+        r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path);
+        if (r < 0)
+                return r;
+
+        fchmod(fileno(f), 0644);
+
+        fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n"
+              "# manually too freely.\n"
+              "Section \"InputClass\"\n"
+              "        Identifier \"system-keyboard\"\n"
+              "        MatchIsKeyboard \"on\"\n", f);
+
+        if (!isempty(x11_layout))
+                fprintf(f, "        Option \"XkbLayout\" \"%s\"\n", x11_layout);
+
+        if (!isempty(x11_model))
+                fprintf(f, "        Option \"XkbModel\" \"%s\"\n", x11_model);
+
+        if (!isempty(x11_variant))
+                fprintf(f, "        Option \"XkbVariant\" \"%s\"\n", x11_variant);
+
+        if (!isempty(x11_options))
+                fprintf(f, "        Option \"XkbOptions\" \"%s\"\n", x11_options);
+
+        fputs("EndSection\n", f);
+        fflush(f);
+
+        if (ferror(f) || rename(temp_path, "/etc/X11/xorg.conf.d/00-keyboard.conf") < 0) {
+                r = -errno;
+                unlink("/etc/X11/xorg.conf.d/00-keyboard.conf");
+                unlink(temp_path);
+        } else {
+
+#ifdef TARGET_FEDORA
+                unlink("/etc/X11/xorg.conf.d/00-system-setup-keyboard.conf");
+#endif
+
+                r = 0;
+        }
+
+        fclose(f);
+        free(temp_path);
+
+        return r;
+}
+
+static int load_vconsole_keymap(DBusConnection *bus, DBusError *error) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *name = "systemd-vconsole-setup.service", *mode = "replace";
+        int r;
+        DBusError _error;
+
+        assert(bus);
+
+        if (!error) {
+                dbus_error_init(&_error);
+                error = &_error;
+        }
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "RestartUnit");
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_STRING, &mode,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        if (error == &_error)
+                dbus_error_free(error);
+
+        return r;
+}
+
+static char *strnulldash(const char *s) {
+        return s == NULL || *s == 0 || (s[0] == '-' && s[1] == 0) ? NULL : (char*) s;
+}
+
+static int read_next_mapping(FILE *f, unsigned *n, char ***a) {
+        assert(f);
+        assert(n);
+        assert(a);
+
+        for (;;) {
+                char line[LINE_MAX];
+                char *l, **b;
+
+                errno = 0;
+                if (!fgets(line, sizeof(line), f)) {
+
+                        if (ferror(f))
+                                return errno ? -errno : -EIO;
+
+                        return 0;
+                }
+
+                (*n) ++;
+
+                l = strstrip(line);
+                if (l[0] == 0 || l[0] == '#')
+                        continue;
+
+                b = strv_split_quoted(l);
+                if (!b)
+                        return -ENOMEM;
+
+                if (strv_length(b) < 5) {
+                        log_error("Invalid line "SYSTEMD_KBD_MODEL_MAP":%u, ignoring.", *n);
+                        strv_free(b);
+                        continue;
+
+                }
+
+                *a = b;
+                return 1;
+        }
+}
+
+static int convert_vconsole_to_x11(DBusConnection *connection) {
+        bool modified = false;
+
+        assert(connection);
+
+        if (isempty(vc_keymap)) {
+
+                modified =
+                        !isempty(x11_layout) ||
+                        !isempty(x11_model) ||
+                        !isempty(x11_variant) ||
+                        !isempty(x11_options);
+
+                free_data_x11();
+        } else {
+                FILE *f;
+                unsigned n = 0;
+
+                f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
+                if (!f)
+                        return -errno;
+
+                for (;;) {
+                        char **a;
+                        int r;
+
+                        r = read_next_mapping(f, &n, &a);
+                        if (r < 0) {
+                                fclose(f);
+                                return r;
+                        }
+
+                        if (r == 0)
+                                break;
+
+                        if (!streq(vc_keymap, a[0])) {
+                                strv_free(a);
+                                continue;
+                        }
+
+                        if (!streq_ptr(x11_layout, strnulldash(a[1])) ||
+                            !streq_ptr(x11_model, strnulldash(a[2])) ||
+                            !streq_ptr(x11_variant, strnulldash(a[3])) ||
+                            !streq_ptr(x11_options, strnulldash(a[4]))) {
+
+                                if (free_and_set(&x11_layout, strnulldash(a[1])) < 0 ||
+                                    free_and_set(&x11_model, strnulldash(a[2])) < 0 ||
+                                    free_and_set(&x11_variant, strnulldash(a[3])) < 0 ||
+                                    free_and_set(&x11_options, strnulldash(a[4])) < 0) {
+                                        strv_free(a);
+                                        fclose(f);
+                                        return -ENOMEM;
+                                }
+
+                                modified = true;
+                        }
+
+                        strv_free(a);
+                        break;
+                }
+
+                fclose(f);
+        }
+
+        if (modified) {
+                dbus_bool_t b;
+                DBusMessage *changed;
+                int r;
+
+                r = write_data_x11();
+                if (r < 0)
+                        log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
+
+                changed = bus_properties_changed_new(
+                                "/org/freedesktop/locale1",
+                                "org.freedesktop.locale1",
+                                "X11Layout\0"
+                                "X11Model\0"
+                                "X11Variant\0"
+                                "X11Options\0");
+
+                if (!changed)
+                        return -ENOMEM;
+
+                b = dbus_connection_send(connection, changed, NULL);
+                dbus_message_unref(changed);
+
+                if (!b)
+                        return -ENOMEM;
+        }
+
+        return 0;
+}
+
+static int convert_x11_to_vconsole(DBusConnection *connection) {
+        bool modified = false;
+
+        assert(connection);
+
+        if (isempty(x11_layout)) {
+
+                modified =
+                        !isempty(vc_keymap) ||
+                        !isempty(vc_keymap_toggle);
+
+                free_data_x11();
+        } else {
+                FILE *f;
+                unsigned n = 0;
+                unsigned best_matching = 0;
+                char *new_keymap = NULL;
+
+                f = fopen(SYSTEMD_KBD_MODEL_MAP, "re");
+                if (!f)
+                        return -errno;
+
+                for (;;) {
+                        char **a;
+                        unsigned matching = 0;
+                        int r;
+
+                        r = read_next_mapping(f, &n, &a);
+                        if (r < 0) {
+                                fclose(f);
+                                return r;
+                        }
+
+                        if (r == 0)
+                                break;
+
+                        /* Determine how well matching this entry is */
+                        if (streq_ptr(x11_layout, a[1])) {
+                                matching ++;
+
+                                if (streq_ptr(x11_model, a[2])) {
+                                        matching++;
+
+                                        if (streq_ptr(x11_variant, a[3])) {
+                                                matching++;
+
+                                                if (streq_ptr(x11_options, a[4]))
+                                                        matching++;
+                                        }
+                                }
+                        }
+
+                        /* The best matching entry so far, then let's
+                         * save that */
+                        if (matching > best_matching) {
+                                best_matching = matching;
+
+                                free(new_keymap);
+                                new_keymap = strdup(a[0]);
+
+                                if (!new_keymap) {
+                                        strv_free(a);
+                                        fclose(f);
+                                        return -ENOMEM;
+                                }
+                        }
+
+                        strv_free(a);
+                }
+
+                fclose(f);
+
+                if (!streq_ptr(vc_keymap, new_keymap)) {
+                        free(vc_keymap);
+                        vc_keymap = new_keymap;
+
+                        free(vc_keymap_toggle);
+                        vc_keymap_toggle = NULL;
+
+                        modified = true;
+                } else
+                        free(new_keymap);
+        }
+
+        if (modified) {
+                dbus_bool_t b;
+                DBusMessage *changed;
+                int r;
+
+                r = write_data_vconsole();
+                if (r < 0)
+                        log_error("Failed to set virtual console keymap: %s", strerror(-r));
+
+                changed = bus_properties_changed_new(
+                                "/org/freedesktop/locale1",
+                                "org.freedesktop.locale1",
+                                "VConsoleKeymap\0"
+                                "VConsoleKeymapToggle\0");
+
+                if (!changed)
+                        return -ENOMEM;
+
+                b = dbus_connection_send(connection, changed, NULL);
+                dbus_message_unref(changed);
+
+                if (!b)
+                        return -ENOMEM;
+
+                return load_vconsole_keymap(connection, NULL);
+        }
+
+        return 0;
+}
+
 static int append_locale(DBusMessageIter *i, const char *property, void *userdata) {
         int r, c = 0, p;
         char **l;
@@ -354,7 +971,13 @@ static DBusHandlerResult locale_message_handler(
                 void *userdata) {
 
         const BusProperty properties[] = {
-                { "org.freedesktop.locale1", "Locale", append_locale, "as", NULL},
+                { "org.freedesktop.locale1", "Locale",               append_locale,              "as", NULL                   },
+                { "org.freedesktop.locale1", "X11Layout",            bus_property_append_string, "s",  x11_layout             },
+                { "org.freedesktop.locale1", "X11Model",             bus_property_append_string, "s",  x11_model              },
+                { "org.freedesktop.locale1", "X11Variant",           bus_property_append_string, "s",  x11_variant            },
+                { "org.freedesktop.locale1", "X11Options",           bus_property_append_string, "s",  x11_options            },
+                { "org.freedesktop.locale1", "VConsoleKeymap",       bus_property_append_string, "s",  vc_keymap              },
+                { "org.freedesktop.locale1", "VConsoleKeymapToggle", bus_property_append_string, "s",  vc_keymap_toggle       },
                 { NULL, NULL, NULL, NULL, NULL }
         };
 
@@ -472,7 +1095,7 @@ static DBusHandlerResult locale_message_handler(
 
                         simplify();
 
-                        r = write_data();
+                        r = write_data_locale();
                         if (r < 0) {
                                 log_error("Failed to set locale: %s", strerror(-r));
                                 return bus_send_error_reply(connection, message, NULL, r);
@@ -489,10 +1112,139 @@ static DBusHandlerResult locale_message_handler(
                         if (!changed)
                                 goto oom;
                 }
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetVConsoleKeyboard")) {
+
+                const char *keymap, *keymap_toggle;
+                dbus_bool_t convert, interactive;
 
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &keymap,
+                                    DBUS_TYPE_STRING, &keymap_toggle,
+                                    DBUS_TYPE_BOOLEAN, &convert,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (isempty(keymap))
+                        keymap = NULL;
+
+                if (isempty(keymap_toggle))
+                        keymap_toggle = NULL;
+
+                if (!streq_ptr(keymap, vc_keymap) ||
+                    !streq_ptr(keymap_toggle, vc_keymap_toggle)) {
+
+                        r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        if (free_and_set(&vc_keymap, keymap) < 0 ||
+                            free_and_set(&vc_keymap_toggle, keymap_toggle) < 0)
+                                goto oom;
+
+                        r = write_data_vconsole();
+                        if (r < 0) {
+                                log_error("Failed to set virtual console keymap: %s", strerror(-r));
+                                return bus_send_error_reply(connection, message, NULL, r);
+                        }
+
+                        log_info("Changed virtual console keymap to '%s'", strempty(vc_keymap));
+
+                        r = load_vconsole_keymap(connection, NULL);
+                        if (r < 0)
+                                log_error("Failed to request keymap reload: %s", strerror(-r));
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/locale1",
+                                        "org.freedesktop.locale1",
+                                        "VConsoleKeymap\0"
+                                        "VConsoleKeymapToggle\0");
+                        if (!changed)
+                                goto oom;
+
+                        if (convert) {
+                                r = convert_vconsole_to_x11(connection);
+
+                                if (r < 0)
+                                        log_error("Failed to convert keymap data: %s", strerror(-r));
+                        }
+                }
+
+        } else if (dbus_message_is_method_call(message, "org.freedesktop.locale1", "SetX11Keyboard")) {
+
+                const char *layout, *model, *variant, *options;
+                dbus_bool_t convert, interactive;
+
+                if (!dbus_message_get_args(
+                                    message,
+                                    &error,
+                                    DBUS_TYPE_STRING, &layout,
+                                    DBUS_TYPE_STRING, &model,
+                                    DBUS_TYPE_STRING, &variant,
+                                    DBUS_TYPE_STRING, &options,
+                                    DBUS_TYPE_BOOLEAN, &convert,
+                                    DBUS_TYPE_BOOLEAN, &interactive,
+                                    DBUS_TYPE_INVALID))
+                        return bus_send_error_reply(connection, message, &error, -EINVAL);
+
+                if (isempty(layout))
+                        layout = NULL;
+
+                if (isempty(model))
+                        model = NULL;
+
+                if (isempty(variant))
+                        variant = NULL;
+
+                if (isempty(options))
+                        options = NULL;
+
+                if (!streq_ptr(layout, x11_layout) ||
+                    !streq_ptr(model, x11_model) ||
+                    !streq_ptr(variant, x11_variant) ||
+                    !streq_ptr(options, x11_options)) {
+
+                        r = verify_polkit(connection, message, "org.freedesktop.locale1.set-keyboard", interactive, &error);
+                        if (r < 0)
+                                return bus_send_error_reply(connection, message, &error, r);
+
+                        if (free_and_set(&x11_layout, layout) < 0 ||
+                            free_and_set(&x11_model, model) < 0 ||
+                            free_and_set(&x11_variant, variant) < 0 ||
+                            free_and_set(&x11_options, options) < 0)
+                                goto oom;
+
+                        r = write_data_x11();
+                        if (r < 0) {
+                                log_error("Failed to set X11 keyboard layout: %s", strerror(-r));
+                                return bus_send_error_reply(connection, message, NULL, r);
+                        }
+
+                        log_info("Changed X11 keyboard layout to '%s'", strempty(x11_layout));
+
+                        changed = bus_properties_changed_new(
+                                        "/org/freedesktop/locale1",
+                                        "org.freedesktop.locale1",
+                                        "X11Layout\0"
+                                        "X11Model\0"
+                                        "X11Variant\0"
+                                        "X11Options\0");
+                        if (!changed)
+                                goto oom;
+
+                        if (convert) {
+                                r = convert_x11_to_vconsole(connection);
+
+                                if (r < 0)
+                                        log_error("Failed to convert keymap data: %s", strerror(-r));
+                        }
+                }
         } else
                 return bus_default_message_handler(connection, message, INTROSPECTION, INTERFACES_LIST, properties);
 
+
         if (!(reply = dbus_message_new_method_return(message)))
                 goto oom;
 
diff --git a/src/org.freedesktop.locale1.policy.in b/src/org.freedesktop.locale1.policy.in
index 186d7d3..1ac50bf 100644
--- a/src/org.freedesktop.locale1.policy.in
+++ b/src/org.freedesktop.locale1.policy.in
@@ -26,4 +26,14 @@
                 </defaults>
         </action>
 
+        <action id="org.freedesktop.locale1.set-keyboard">
+                <_description>Set system keyboard settings</_description>
+                <_message>Authentication is required to set the system keyboard settings.</_message>
+                <defaults>
+                        <allow_any>auth_admin_keep</allow_any>
+                        <allow_inactive>auth_admin_keep</allow_inactive>
+                        <allow_active>auth_admin_keep</allow_active>
+                </defaults>
+        </action>
+
 </policyconfig>
diff --git a/src/util.c b/src/util.c
index 425a732..7977ee4 100644
--- a/src/util.c
+++ b/src/util.c
@@ -5682,3 +5682,21 @@ bool kexec_loaded(void) {
        }
        return loaded;
 }
+
+int strdup_or_null(const char *a, char **b) {
+        char *c;
+
+        assert(b);
+
+        if (!a) {
+                *b = NULL;
+                return 0;
+        }
+
+        c = strdup(a);
+        if (!c)
+                return -ENOMEM;
+
+        *b = c;
+        return 0;
+}
diff --git a/src/util.h b/src/util.h
index ba0800d..ccbe8a3 100644
--- a/src/util.h
+++ b/src/util.h
@@ -467,6 +467,8 @@ int block_get_whole_disk(dev_t d, dev_t *ret);
 
 int file_is_sticky(const char *p);
 
+int strdup_or_null(const char *a, char **b);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 



More information about the systemd-commits mailing list