diff options
Diffstat (limited to 'rcm-server-keymap.c')
-rw-r--r-- | rcm-server-keymap.c | 88 |
1 files changed, 88 insertions, 0 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) { |