diff options
-rw-r--r-- | rcm-server-keymap.c | 88 | ||||
-rw-r--r-- | rcm-server-keymap.h | 2 | ||||
-rw-r--r-- | rcm-server-main.c | 11 |
3 files changed, 96 insertions, 5 deletions
diff --git a/rcm-server-keymap.c b/rcm-server-keymap.c index 6e45570..04dd1a4 100644 --- a/rcm-server-keymap.c +++ b/rcm-server-keymap.c @@ -438,6 +438,94 @@ keymaps_load(struct device *device) return 0; } +int +keymap_write(struct keymap *keymap) +{ + _cleanup_close_ int dfd = -1; + _cleanup_close_ int fd = -1; + _cleanup_fclose_ FILE *file = NULL; + char *separator; + unsigned i; + + separator = strrchr(keymap->id, '/'); + if (!separator) + return -EINVAL; + + /* FIXME: This is just a temporary location */ + dfd = open("./keymaps", O_PATH | O_DIRECTORY); + if (dfd < 0) + return -errno; + + *separator = '\0'; + fd = openat(dfd, keymap->id, O_TMPFILE | O_WRONLY, 0644); + *separator = '/'; + if (fd < 0) + return -errno; + + /* https://sourceware.org/bugzilla/show_bug.cgi?id=17523 */ + if (fchmod(fd, 0644) < 0) + return -errno; + + int fdpath_len; + fdpath_len = snprintf(NULL, 0, "/proc/self/fd/%i", fd); + if (fdpath_len < 0) + return -errno; + + char fdpath[fdpath_len + 1]; + sprintf(fdpath, "/proc/self/fd/%d", fd); + + file = fdopen(fd, "w"); + if (!file) + return -errno; + else + fd = -1; + + fprintf(file, "[Description]\n"); + fprintf(file, "Name=%s\n", keymap->name); + fprintf(file, "Description=%s\n", keymap->description); + fprintf(file, "Rows=%u\n", keymap->rows); + fprintf(file, "Cols=%u\n", keymap->cols); + fprintf(file, "\n"); + + fprintf(file, "[Keymap]\n"); + for (i = 0; i < keymap->keycode_count; i++) { + fprintf(file, "Map=%s:0x%08" PRIx64 ":%s\n", + keymap->keycodes[i].protocol, + keymap->keycodes[i].scancode, + keymap->keycodes[i].keycode); + } + fprintf(file, "\n"); + + fprintf(file, "[Layout]\n"); + for (i = 0; i < (keymap->rows * keymap->cols); i++) { + if (keymap->layout[i]) + fprintf(file, "Button=%s\n", keymap->layout[i]->keycode); + else + fprintf(file, "Button=Blank\n"); + } + + fflush(file); + + if (ferror(file)) + return errno ? -errno : -EIO; + + char tmppath[strlen(keymap->id) + strlen(".tmp") + 1]; + sprintf(tmppath, "%s.tmp", keymap->id); + + if (linkat(AT_FDCWD, fdpath, dfd, tmppath, AT_SYMLINK_FOLLOW) < 0) { + printf("linkat failed: %i - %s\n", errno, strerror(errno)); + return -errno; + } + + if (renameat(dfd, tmppath, dfd, keymap->id) < 0) { + int r = -errno; + unlinkat(dfd, tmppath, 0); + return r; + } + + return 0; +} + void keymap_free(struct keymap *keymap) { diff --git a/rcm-server-keymap.h b/rcm-server-keymap.h index 03badb1..a68a1fd 100644 --- a/rcm-server-keymap.h +++ b/rcm-server-keymap.h @@ -23,6 +23,8 @@ struct keymap *find_keymap_by_id(struct device *dev, const char *id); int keymaps_load(struct device *device); +int keymap_write(struct keymap *keymap); + void keymap_free(struct keymap *keymap); #endif diff --git a/rcm-server-main.c b/rcm-server-main.c index 0623a01..cde13a0 100644 --- a/rcm-server-main.c +++ b/rcm-server-main.c @@ -587,29 +587,30 @@ method_setkeymap(sd_bus_message *m, void *userdata, sd_bus_error *error) new_keymap->layout = zmalloc(sizeof(struct keycode *) * cols * rows); if (!new_keymap->layout) { - keymap_free(new_keymap); r = -ENOMEM; goto out; } r = method_setkeymap_parse_dbus_msg(m, new_keymap, NULL, NULL, NULL, NULL); - if (r < 0) { - keymap_free(new_keymap); + if (r < 0) goto out; - } printf("\tUpdated keymap: name (%s), desc (%s), keycodes(%u), rows(%u), cols(%u)\n", new_keymap->name, new_keymap->description, new_keymap->keycode_count, new_keymap->rows, new_keymap->cols); - /* FIXME: Write to disk */ + r = keymap_write(new_keymap); + if (r < 0) + goto out; + list_replace(&old_keymap->list, &new_keymap->list); keymap_free(old_keymap); return sd_bus_reply_method_return(m, NULL); out: + keymap_free(new_keymap); return sd_bus_error_set_errno(error, r); } |