From 6f51fdc108dd5251befb57e8df82ea723328ffed Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sat, 29 Aug 2015 12:30:11 +0200 Subject: Add basic sql db of available keymaps (to be used for automated searches) --- Makefile | 6 +- rcm-server-kdb.c | 702 ++++++++++++++++++++++++++++++++++++++++++++++++++++ rcm-server-kdb.h | 12 + rcm-server-keymap.c | 2 +- rcm-server-keymap.h | 5 + rcm-server-main.c | 26 +- rcm-server-main.h | 3 + utils.h | 10 + 8 files changed, 756 insertions(+), 10 deletions(-) create mode 100644 rcm-server-kdb.c create mode 100644 rcm-server-kdb.h diff --git a/Makefile b/Makefile index d4d8185..744ede8 100644 --- a/Makefile +++ b/Makefile @@ -14,13 +14,13 @@ EXTRA_LDFLAGS = COMMON_HEADERS = linux-input-keycodes.h linux-input.h shared.h -RCM_PACKAGES = libudev libsystemd libevdev +RCM_PACKAGES = libudev libsystemd libevdev sqlite3 RCM_CFLAGS = ${GENERIC_CFLAGS} ${EXTRA_CFLAGS} $(shell pkg-config --cflags ${RCM_PACKAGES}) RCM_LDFLAGS = ${GENERIC_LDFLAGS} ${EXTRA_LDFLAGS} $(shell pkg-config --libs ${RCM_PACKAGES}) RCM_COMPILE = $(CC) $(RCM_CFLAGS) RCM_LINK = $(CC) $(RCM_CFLAGS) $(RCM_LDFLAGS) -RCM_OBJECTS = rcm-server-main.o rcm-server-keymap.o rcm-server-evdev.o shared.o -RCM_HEADERS = rcm-server-main.h rcm-server-keymap.h rcm-server-evdev.h utils.h $(COMMON_HEADERS) +RCM_OBJECTS = rcm-server-main.o rcm-server-keymap.o rcm-server-evdev.o rcm-server-kdb.o shared.o +RCM_HEADERS = rcm-server-main.h rcm-server-keymap.h rcm-server-evdev.h rcm-server-kdb.h utils.h $(COMMON_HEADERS) RCC_PACKAGES = gtk+-3.0 RCC_CFLAGS = ${GENERIC_CFLAGS} ${EXTRA_CFLAGS} $(shell pkg-config --cflags ${RCC_PACKAGES}) diff --git a/rcm-server-kdb.c b/rcm-server-kdb.c new file mode 100644 index 0000000..14d4a56 --- /dev/null +++ b/rcm-server-kdb.c @@ -0,0 +1,702 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "shared.h" +#include "rcm-server-main.h" +#include "rcm-server-kdb.h" +#include "rcm-server-keymap.h" + +struct inotify_dir { + int iwd; + int dfd; + int64_t dirid; + struct list_head list; +}; + +static LIST_HEAD(inotify_dir_list); + +DEFINE_TRIVIAL_CLEANUP_FUNC(sqlite3_stmt *, sqlite3_finalize); +#define _cleanup_finalize_stmt_ _cleanup_(sqlite3_finalizep) + +DEFINE_TRIVIAL_CLEANUP_FUNC(sqlite3 *, sqlite3_close); +#define _cleanup_sqlite3_close_ _cleanup_(sqlite3_closep) + +static int +_sql_do_prep(sqlite3 *db, sqlite3_stmt **stmt_out, const char *sql, int nbyte, va_list ap) +{ + _cleanup_finalize_stmt_ sqlite3_stmt *stmt = NULL; + unsigned i; + int r; + + r = sqlite3_prepare_v2(db, sql, nbyte, &stmt, NULL); + if (r != SQLITE_OK) { + fprintf(stderr, "SQL error: %s - %s\n", sql, sqlite3_errmsg(db)); + return -EIO; + } + + for (i = 0; i < sqlite3_bind_parameter_count(stmt); i++) { + int type; + + type = va_arg(ap, int); + switch (type) { + case SQLITE_INTEGER: + r = sqlite3_bind_int64(stmt, i + 1, va_arg(ap, int64_t)); + break; + case SQLITE_TEXT: + r = sqlite3_bind_text(stmt, i + 1, va_arg(ap, char *), -1, SQLITE_STATIC); + break; + default: + fprintf(stderr, "SQL error: %s - invalid type (%i)\n", sql, type); + return -EINVAL; + } + + if (r != SQLITE_OK) { + fprintf(stderr, "SQL error: %s - %s\n", sql, sqlite3_errmsg(db)); + return -EIO; + } + } + + *stmt_out = stmt; + stmt = NULL; + return 0; +} + +static int +_sql_prep(sqlite3 *db, sqlite3_stmt **stmt_out, const char *sql, int nbyte, ...) +{ + va_list ap; + int r; + + va_start(ap, nbyte); + r = _sql_do_prep(db, stmt_out, sql, nbyte, ap); + va_end(ap); + return r; +} +#define sql_prep(db, stmt, sql, ...) _sql_prep((db), (stmt), (sql), strlen((sql)) + 1, __VA_ARGS__) + +static int +_sql_get_row(sqlite3_stmt *stmt, va_list ap) +{ + unsigned i; + int64_t *vint; + const char **vchar; + int r; + + + r = sqlite3_step(stmt); + if (r == SQLITE_DONE) + return 0; + else if (r != SQLITE_ROW) + return -r; + + for (i = 0; i < sqlite3_column_count(stmt); i++) { + int type = sqlite3_column_type(stmt, i); + + switch (type) { + case SQLITE_INTEGER: + vint = va_arg(ap, int64_t *); + *vint = sqlite3_column_int64(stmt, i); + break; + case SQLITE_TEXT: + vchar = va_arg(ap, const char **); + *vchar = (const char *)sqlite3_column_text(stmt, i); + break; + default: + fprintf(stderr, "SQL error: invalid type (%i)\n", type); + r = -SQLITE_ERROR; + goto out; + } + } + + return 1; + +out: + return r; +} + +static int +sql_get_row(sqlite3_stmt *stmt, ...) +{ + va_list ap; + int r; + + va_start(ap, stmt); + r = _sql_get_row(stmt, ap); + va_end(ap); + + return r; +} + +static int +sql_get_single_row(sqlite3_stmt *stmt, ...) +{ + va_list ap; + int r; + + va_start(ap, stmt); + r = _sql_get_row(stmt, ap); + va_end(ap); + + if (r < 1) + return r; + + r = sqlite3_step(stmt); + if (r != SQLITE_DONE) + return -EIO; + + return 1; +} + +static int +_sql_exec(sqlite3 *db, const char *sql, int nbyte, ...) +{ + _cleanup_finalize_stmt_ sqlite3_stmt *stmt = NULL; + va_list ap; + int r; + + va_start(ap, nbyte); + r = _sql_do_prep(db, &stmt, sql, nbyte, ap); + va_end(ap); + + if (r < 0 ) + return r; + + r = sqlite3_step(stmt); + + if (r != SQLITE_DONE) { + fprintf(stderr, "sql_exec: unexpected return code (%i) for query %s\n", r, sql); + return -EIO; + } + + return 0; +} +#define sql_exec(db, sql, ...) _sql_exec((db), (sql), strlen((sql)) + 1, __VA_ARGS__) + +static uint64_t +mtime_in_nsec(struct stat *stat) +{ + return (uint64_t)stat->st_atim.tv_sec * 1000000000 + + (uint64_t)stat->st_atim.tv_nsec; +} + +static int +get_directory(sqlite3 *db, const char *path, int64_t *dirid, uint64_t *mtime) +{ + _cleanup_finalize_stmt_ sqlite3_stmt *stmt = NULL; + int r; + + r = sql_prep(db, &stmt, + "SELECT dirid, mtime FROM directories WHERE abspath = :PATH;", + SQLITE_TEXT, path); + if (r < 0) + return r; + + r = sql_get_single_row(stmt, dirid, mtime); + return r; +} + +static void +kdb_check_file(struct manager *mgr, int dfd, int64_t dirid, int64_t fileid, const char *filename, uint64_t db_mtime) +{ + struct stat stat; + uint64_t file_mtime; + _cleanup_keymap_free_ struct keymap *keymap = NULL; + int r; + + printf("%s: check file %s (id %" PRIi64 " dir %" PRIi64 " dirfd %i db_mtime %" PRIu64 ")\n", + __FUNCTION__, filename, fileid, dirid, dfd, db_mtime); + + if (dfd < 0 || !filename) { + printf("Invalid"); + return; + } + + if (fstatat(dfd, filename, &stat, AT_SYMLINK_NOFOLLOW) < 0) { + fprintf(stderr, "\tfstatat(%s) - failed: %m\n", filename); + goto out; + } + + if ((stat.st_mode & S_IFMT) != S_IFREG) { + printf("\t%s - not a regular file\n", filename); + goto out; + } + + file_mtime = mtime_in_nsec(&stat); + if (fileid >= 0) { + if (file_mtime <= db_mtime) { + printf("\t%s - file not older than db entry, skipped\n", filename); + return; + } + + r = sql_exec(mgr->db, + "DELETE FROM keymap WHERE fileid = :FILEID;", + SQLITE_INTEGER, fileid); + if (r < 0) { + printf("\t%s - failed to clean out old entries\n", filename); + goto out; + } + } + + keymap = keymap_read(dfd, ".", filename); + if (!keymap) { + printf("\t%s - unable to parse keymap, skipped\n", filename); + goto out; + } + + if (fileid >= 0) { + r = sql_exec(mgr->db, "UPDATE files SET name = :NAME, desc = :DESC, scanned = 1, mtime = :MTIME" + " WHERE fileid = :FILEID", + SQLITE_TEXT, keymap->name, SQLITE_TEXT, keymap->description, + SQLITE_INTEGER, file_mtime, SQLITE_INTEGER, fileid); + if (r < 0) { + printf("\t%s - failed to update old file entry\n", filename); + goto out; + } + + } else { + r = sql_exec(mgr->db, "INSERT INTO files (dirid, filename, name, desc, scanned, mtime)" + " VALUES (:DIRID, :FILENAME, :NAME, :DESC, 1, :MTIME);", + SQLITE_INTEGER, dirid, SQLITE_TEXT, filename, + SQLITE_TEXT, keymap->name, SQLITE_TEXT, keymap->description, + SQLITE_INTEGER, file_mtime); + if (r < 0) { + printf("\t%s - failed to create new file entry\n", filename); + goto out; + } + + fileid = sqlite3_last_insert_rowid(mgr->db); + } + + for (unsigned i = 0; i < keymap->keycode_count; i++) { + printf("\t%s - inserting entry %u/%u 0x%08" PRIx64 ":0x%08" PRIx64 ":0x%08" PRIx64 "\n", + filename, i, keymap->keycode_count, + (int64_t)keymap->keycodes[i].protocol, + (int64_t)keymap->keycodes[i].scancode, + (int64_t)keymap->keycodes[i].lik->value); + + r = sql_exec(mgr->db, "INSERT INTO keymap (fileid, protocol, scancode, keycode)" + " VALUES (:FILEID, :PROTOCOL, :SCANCODE, :KEYCODE);", + SQLITE_INTEGER, fileid, + SQLITE_INTEGER, (int64_t)keymap->keycodes[i].protocol, + SQLITE_INTEGER, (int64_t)keymap->keycodes[i].scancode, + SQLITE_INTEGER, (int64_t)keymap->keycodes[i].lik->value); + if (r < 0) { + printf("\t%s - failed to insert keymap entry\n", filename); + goto out; + } + } + + printf("%s - inserted into db with %u entries\n", filename, keymap->keycode_count); + return; + +out: + if (fileid >= 0) { + printf("\t%s - Deleting row from DB\n", filename); + sql_exec(mgr->db, + "DELETE FROM files WHERE fileid = :FILEID;", + SQLITE_INTEGER, fileid); + } +} + +int +kdb_add_directory(struct manager *mgr, const char *path) +{ + sqlite3 *db = mgr->db; + _cleanup_free_ char *abspath = NULL; + _cleanup_free_ struct inotify_dir *in_dir = NULL; + _cleanup_closedir_ DIR *dir = NULL; + _cleanup_finalize_stmt_ sqlite3_stmt *stmt = NULL; + struct dirent *dent; + struct stat stat; + uint64_t mtime; + uint64_t old_mtime; + int64_t dirid; + int dfd; + int r; + + abspath = realpath(path, NULL); + if (!abspath) { + r = -errno; + fprintf(stderr, "realpath(%s) failed: %m\n", path); + goto out; + } + + printf("Scanning directory: %s (%s)\n", abspath, path); + + in_dir = malloc(sizeof(*in_dir)); + if (!in_dir) { + r = -ENOMEM; + fprintf(stderr, "malloc failed: %m\n"); + goto out; + } + in_dir->iwd = -1; + in_dir->dfd = -1; + in_dir->dirid = -1; + + dfd = open(abspath, O_RDONLY | O_CLOEXEC | O_DIRECTORY); + if (dfd < 0) { + r = -errno; + fprintf(stderr, "open(%s) failed: %m\n", abspath); + goto out; + } + in_dir->dfd = dfd; + + dfd = fcntl(in_dir->dfd, F_DUPFD_CLOEXEC); + if (dfd < 0) { + r = -errno; + fprintf(stderr, "fcntl(%i, F_DUPFD_CLOEXEC) failed: %m\n", in_dir->dfd); + goto out; + } + + dir = fdopendir(dfd); + if (!dir) { + close(dfd); + r = -errno; + fprintf(stderr, "fdopendir(%i) failed: %m\n", dfd); + goto out; + } + + if (fstat(dirfd(dir), &stat) < 0) { + r = -errno; + fprintf(stderr, "fstat(%s) failed: %s\n", abspath, strerror(errno)); + goto out; + } + + mtime = mtime_in_nsec(&stat); + + r = inotify_add_watch(mgr->db_ifd, abspath, IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_MOVE | IN_ONLYDIR); + if (r < 0) { + r = -errno; + fprintf(stderr, "inotify_add_watch(%s) failed: %m\n", abspath); + goto out; + } + in_dir->iwd = r; + + r = get_directory(db, abspath, &dirid, &old_mtime); + if (r < 0) { + fprintf(stderr, "get_directory(%s) failed: %i\n", abspath, r); + goto out; + + } else if (r == 0) { + printf("Adding new directory: %s\n", abspath); + + r = sql_exec(db, + "INSERT INTO directories (abspath, mtime) VALUES (:PATH, 0);", + SQLITE_TEXT, abspath); + if (r < 0) + goto out; + + dirid = (int64_t)sqlite3_last_insert_rowid(db); + old_mtime = 0; + + } else { + printf("Marking files in directory %s for a rescan\n", abspath); + + r = sql_exec(db, + "UPDATE files SET scanned = 0 WHERE dirid = :ID;", + SQLITE_INTEGER, dirid); + if (r < 0) + goto out; + } + + in_dir->dirid = dirid; + + if (mtime > old_mtime) { + /* Look for added files */ + printf("Directory %s modified, scanning for new files\n", abspath); + errno = 0; + + while ((dent = readdir(dir)) != NULL) { + if (dent->d_name[0] == '\0' || dent->d_name[0] == '.') { + printf("\t%s - skipped\n", dent->d_name); + errno = 0; + continue; + } + + switch (dent->d_type) { + case DT_UNKNOWN: + if (fstatat(dirfd(dir), dent->d_name, &stat, AT_SYMLINK_NOFOLLOW) < 0) { + fprintf(stderr, "\tfstatat(%s) - failed: %s\n", dent->d_name, strerror(errno)); + errno = 0; + continue; + } + + if ((stat.st_mode & S_IFMT) != S_IFREG) { + printf("\t%s - not a regular file\n", dent->d_name); + errno = 0; + continue; + } + + /* fall through */ + + case DT_REG: + printf("\t%s - add (possibly) to DB\n", dent->d_name); + r = sql_exec(db, + "INSERT OR IGNORE INTO" + " files (dirid, filename, name, scanned, mtime)" + " VALUES (:DIRID, :FILENAME, '', 0, 0);", + SQLITE_INTEGER, dirid, + SQLITE_TEXT, dent->d_name); + if (r < 0) + goto out; + + break; + + default: + printf("\t%s - not a regular file\n", dent->d_name); + break; + } + + errno = 0; + } + + if (errno != 0) + fprintf(stderr, "readdir error: %s\n", strerror(errno)); + } + + /* check all files in db */ + r = sql_prep(db, &stmt, + "SELECT fileid, filename, mtime FROM files WHERE dirid = :DIRID AND scanned = 0;", + SQLITE_INTEGER, dirid); + if (r < 0) + goto out; + else { + int64_t fileid; + char *filename; + uint64_t mtime; + + while ((r = sql_get_row(stmt, &fileid, &filename, &mtime)) > 0) + kdb_check_file(mgr, in_dir->dfd, dirid, fileid, filename, mtime); + + if (r < 0) + goto out; + } + + /* delete files not found during scan */ + r = sql_exec(db, + "DELETE FROM files WHERE dirid = :DIRID AND scanned = 0;", + SQLITE_INTEGER, dirid); + if (r < 0) + goto out; + + list_add(&in_dir->list, &inotify_dir_list); + in_dir = NULL; + return 0; + +out: + if (in_dir && in_dir->iwd >= 0) + inotify_rm_watch(mgr->db_ifd, in_dir->iwd); + if (in_dir && in_dir->dfd >= 0) + close(in_dir->dfd); + + return r; +} + +static int +create_tables(sqlite3 *db) +{ + int r; + + r = sql_exec(db, + "CREATE TABLE IF NOT EXISTS directories(" + " dirid INTEGER PRIMARY KEY NOT NULL," + " abspath TEXT UNIQUE NOT NULL," + " mtime INTEGER NOT NULL" + ");", NULL); + if (r != SQLITE_OK) + goto out; + + r = sql_exec(db, + "CREATE TABLE IF NOT EXISTS files(" + " dirid INTEGER NOT NULL REFERENCES directories(dirid) ON UPDATE CASCADE ON DELETE CASCADE," + " fileid INTEGER PRIMARY KEY NOT NULL," + " filename TEXT NOT NULL," + " name TEXT NOT NULL," + " desc TEXT," + " scanned BOOLEAN NOT NULL CHECK (scanned IN (0,1))," + " mtime INTEGER NOT NULL," + " UNIQUE(dirid, filename)" + ");", NULL); + if (r != SQLITE_OK) + goto out; + + r = sql_exec(db, + "CREATE TABLE IF NOT EXISTS keymap(" + " fileid INTEGER NOT NULL REFERENCES files(fileid) ON UPDATE CASCADE ON DELETE CASCADE," + " protocol INTEGER NOT NULL," + " scancode INTEGER NOT NULL," + " keycode INTEGER NOT NULL," + " UNIQUE(protocol, scancode)" + ");", NULL); + if (r != SQLITE_OK) + goto out; + + return SQLITE_OK; + +out: + return r; +} + +static int +enable_foreign_keys(sqlite3 *db) +{ + sqlite3_stmt *stmt; + bool supported = false; + int r; + + sql_exec(db, "PRAGMA foreign_keys = ON;", NULL); + + r = sql_prep(db, &stmt, "PRAGMA foreign_keys;", NULL); + if (r == SQLITE_OK) { + int64_t value; + + r = sql_get_single_row(stmt, &value); + if (r > 0 && value > 0) + supported = true; + } + + sqlite3_finalize(stmt); + return supported ? SQLITE_OK : SQLITE_ERROR; +} + +static int +inotify_read(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct manager *mgr = userdata; + char buf[sizeof(struct inotify_event) + NAME_MAX + 1] __attribute__ ((aligned(__alignof__(struct inotify_event)))); + struct inotify_event *ev = (struct inotify_event *)buf; + ssize_t r; + + if (revents & EPOLLHUP) { + fprintf(stderr, "kdb inotify connection closed!\n"); + return 0; + } + + if (!(revents & EPOLLIN)) { + fprintf(stderr, "unexpected udev event: %" PRIu32 "\n", revents); + return 0; + } + + while ((r = read(mgr->db_ifd, buf, sizeof(buf))) > 0) { + struct inotify_dir *idir; + sqlite3_stmt *stmt; + int64_t fileid; + uint64_t mtime; + + printf("Read inotify event:\n"); + printf("\tWD: %i\n", ev->wd); + printf("\tMask: %08" PRIx32 "\n", ev->mask); + printf("\tCookie: %08" PRIx32 "\n", ev->cookie); + printf("\tLen: %" PRIu32 "\n", ev->len); + printf("\tName: %s\n", ev->name); + + list_for_each_entry(idir, &inotify_dir_list, list) { + + if (idir->iwd != ev->wd) + continue; + + printf("\tSQL dirid: %" PRIi64 "\n", idir->dirid); + r = sql_prep(mgr->db, &stmt, + "SELECT fileid, mtime FROM files WHERE dirid = :DIRID AND filename = :FILENAME;", + SQLITE_INTEGER, idir->dirid, SQLITE_TEXT, ev->name); + if (r < 0) + break; + + fileid = -1; + mtime = 0; + r = sql_get_row(stmt, &fileid, &mtime); + sqlite3_finalize(stmt); + if (r < 0) + break; + + printf("\tSQL fileid: %" PRIi64 "\n", fileid); + printf("\tSQL mtime: %" PRIu64 "\n", mtime); + kdb_check_file(mgr, idir->dfd, idir->dirid, fileid, ev->name, mtime); + break; + } + } + + return 1; +}; + +int +kdb_init(struct manager *mgr, const char *path) +{ + _cleanup_close_ int ifd = -1; + _cleanup_event_source_unref_ sd_event_source *ev = NULL; + _cleanup_sqlite3_close_ sqlite3 *db = NULL; + int r; + + ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (ifd < 0) { + r = errno; + fprintf(stderr, "Failed to create inotify instance: %s\n", strerror(r)); + return -r; + } + + r = sd_event_add_io(mgr->event, &ev, ifd, EPOLLIN, inotify_read, mgr); + if (r < 0) { + fprintf(stderr, "Failed to add kdb inotify instance to event loop: %m\n"); + return r; + } + + r = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); + if (r != SQLITE_OK) { + fprintf(stderr, "Can't open database: (%i) %s\n", r, sqlite3_errmsg(db)); + return -1; + } + + r = enable_foreign_keys(db); + if (r != SQLITE_OK) { + fprintf(stderr, "Failed to enable foreign keys\n"); + return -1; + } + + r = create_tables(db); + if (r != SQLITE_OK) { + fprintf(stderr, "Failed to create tables\n"); + return -1; + } + + mgr->db_ifd_ev = ev; + mgr->db = db; + mgr->db_ifd = ifd; + ev = NULL; + db = NULL; + ifd = -1; + return 0; +} + +void +kdb_close(struct manager *mgr) +{ + struct inotify_dir *idir; + struct inotify_dir *tmp; + + list_for_each_entry_safe(idir, tmp, &inotify_dir_list, list) { + if (idir->dfd >= 0) + close(idir->dfd); + list_del(&idir->list); + free(idir); + } + + sqlite3_close(mgr->db); + close(mgr->db_ifd); + sd_event_source_unref(mgr->db_ifd_ev); +} + diff --git a/rcm-server-kdb.h b/rcm-server-kdb.h new file mode 100644 index 0000000..5362a80 --- /dev/null +++ b/rcm-server-kdb.h @@ -0,0 +1,12 @@ +#ifndef foorcmserverkdbhfoo +#define foorcmserverkdbhfoo + +#include + +int kdb_add_directory(struct manager *mgr, const char *path); + +int kdb_init(struct manager *mgr, const char *path); + +void kdb_close(struct manager *mgr); + +#endif diff --git a/rcm-server-keymap.c b/rcm-server-keymap.c index 56e17f6..84e8f7c 100644 --- a/rcm-server-keymap.c +++ b/rcm-server-keymap.c @@ -281,7 +281,7 @@ keymap_parse(FILE *fp, char **line, size_t *buf_size, struct keymap *keymap, return 0; } -static struct keymap * +struct keymap * keymap_read(int dfd, const char *path, const char *filename) { _cleanup_close_ int fd = -1; diff --git a/rcm-server-keymap.h b/rcm-server-keymap.h index a33632c..8da6bb6 100644 --- a/rcm-server-keymap.h +++ b/rcm-server-keymap.h @@ -21,11 +21,16 @@ struct keymap { struct keymap *find_keymap_by_id(struct device *dev, const char *id); +struct keymap *keymap_read(int dfd, const char *path, const char *filename); + int keymaps_load(struct device *device); int keymap_write(struct keymap *keymap); void keymap_free(struct keymap *keymap); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct keymap *, keymap_free); +#define _cleanup_keymap_free_ _cleanup_(keymap_freep) + #endif diff --git a/rcm-server-main.c b/rcm-server-main.c index 596d81c..5177c3c 100644 --- a/rcm-server-main.c +++ b/rcm-server-main.c @@ -17,6 +17,7 @@ #include "rcm-server-main.h" #include "rcm-server-keymap.h" #include "rcm-server-evdev.h" +#include "rcm-server-kdb.h" static struct device * find_device_by_path(struct manager *mgr, const char *path) @@ -1179,6 +1180,7 @@ free_manager(struct manager *mgr) { if (!mgr) return; + kdb_close(mgr); sd_event_source_unref(mgr->udev_ev); sd_bus_detach_event(mgr->bus); sd_event_unref(mgr->event); @@ -1245,6 +1247,24 @@ main(int argc, char **argv) } list_init(&mgr->devices); + r = sd_event_default(&mgr->event); + if (r < 0) { + fprintf(stderr, "Failed to create sd_event: %s\n", strerror(-r)); + goto finish; + } + + r = kdb_init(mgr, "./rcm.sqlite"); + if (r < 0) { + fprintf(stderr, "Failed to open/create sqlite database\n"); + goto finish; + } + + r = kdb_add_directory(mgr, "./keymaps/db"); + if (r < 0) { + fprintf(stderr, "Failed to add keymap directory: %s\n", strerror(-r)); + goto finish; + } + r = sd_bus_open_user(&bus); if (r < 0) { fprintf(stderr, "Failed to connect to system bus: %s\n", strerror(-r)); @@ -1278,12 +1298,6 @@ main(int argc, char **argv) goto finish; } - r = sd_event_default(&mgr->event); - if (r < 0) { - fprintf(stderr, "Failed to create sd_event: %s\n", strerror(-r)); - goto finish; - } - r = sd_bus_attach_event(mgr->bus, mgr->event, 0); if (r < 0) { fprintf(stderr, "Failed to attach bus to event loop: %s\n", strerror(-r)); diff --git a/rcm-server-main.h b/rcm-server-main.h index 4d2b57b..0d776ac 100644 --- a/rcm-server-main.h +++ b/rcm-server-main.h @@ -26,6 +26,9 @@ struct manager { sd_event_source *udev_ev; struct list_head devices; unsigned num_devices; + void *db; + int db_ifd; + sd_event_source *db_ifd_ev; }; #endif diff --git a/utils.h b/utils.h index 250dbdc..f02c47a 100644 --- a/utils.h +++ b/utils.h @@ -10,6 +10,10 @@ struct list_head { struct list_head *prev; }; +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) + static inline void list_init(struct list_head *list) { list->next = list; @@ -69,6 +73,12 @@ static inline bool list_empty(struct list_head *list) &pos->member != (head); \ pos = list_next_entry(pos, member)) +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + static inline void* zmalloc(size_t size) { return calloc(1, size); -- cgit v1.2.3