diff --git a/inc/common/macro.h b/inc/common/macro.h index 1ba00aa134172feef27e329bad2c29a9d6d0e674..da56e358042a2cbb3ce8579e1ac7fffdf0ef58d9 100644 --- a/inc/common/macro.h +++ b/inc/common/macro.h @@ -75,6 +75,9 @@ #define SELF_EXECUTABLE_FREEBSD "/proc/curproc/file" #define SELF_EXECUTABLE_SOLARIS "/proc/self/path/a.out" +#define LKT_ENV_RESTART "__LKT_RESTART" +#define LKT_ENV_CURRENT "__LKT_CURRENT" + #define LKT_MAX_SQLITE_STATEMENT 1024 #define PROTECTED_DATABASE "disk" diff --git a/inc/common/queue.h b/inc/common/queue.h new file mode 100644 index 0000000000000000000000000000000000000000..fc3809ae205af4aa4dc43fa1eaf6491f13bbde95 --- /dev/null +++ b/inc/common/queue.h @@ -0,0 +1,35 @@ +#pragma once + +#include <pthread.h> +#include <common/common.h> + +enum lkt_event_type { + lkt_event_null = 0, /* NULL */ + lkt_event_play_pos = (1 << 1), /* size_t */ + lkt_event_play_file = (1 << 2), /* const char* */ +} type; + +#define lkt_event_play (lkt_event_play_pos | lkt_event_play_file) + +typedef struct { + unsigned int type; + void *attr; +} lkt_event; + +struct queue { + pthread_mutex_t lock; + + volatile lkt_event *contents; + volatile size_t size; + volatile size_t last; + + volatile int available; +}; + +int lkt_queue_new(struct queue *); +void lkt_queue_free(struct queue *); + +void lkt_queue_send(struct queue *, enum lkt_event_type, void *attr); +lkt_event lkt_queue_handle(struct queue *); + +void lkt_queue_make_available(struct queue *, enum lkt_event_type); diff --git a/inc/lektor/config.inc b/inc/lektor/config.def similarity index 100% rename from inc/lektor/config.inc rename to inc/lektor/config.def diff --git a/inc/lektor/config.h b/inc/lektor/config.h index 22c200b1ef6d88e7d6794ce6c179b2c5eb722746..5746e7b73103e19c4bede54429414962e1974d49 100644 --- a/inc/lektor/config.h +++ b/inc/lektor/config.h @@ -12,7 +12,7 @@ struct lkt_state; #define value(key, val) key " = " val "\n" #define value_opt(key, val) value(key, val) static const char *const lkt_default_config_file = -#include <lektor/config.inc> +#include <lektor/config.def> ; #undef value #undef value_opt @@ -35,3 +35,7 @@ int config_new(volatile sqlite3 *db, const char *conf); /* Prints the default config file. */ void config_default(FILE *output); + +/* Manipulate Environment Variables */ +#define env_set(var, val) setenv(var, val, 1) +#define env_get getenv diff --git a/inc/lektor/module/module_sdl2.h b/inc/lektor/module/module_sdl2.h index cdd608ebecf25ed502611897671e74eedef08bd8..30dea5744268769565c646d7a85343082ca9c18e 100644 --- a/inc/lektor/module/module_sdl2.h +++ b/inc/lektor/module/module_sdl2.h @@ -17,7 +17,6 @@ int load_sdl2(void *mod, struct lkt_state *srv, void *handle); bool module_sdl2_new(struct lkt_win *const win); void module_sdl2_close(struct lkt_win *const win); void module_sdl2_free(struct lkt_win *const win); -void module_sdl2_attach(struct lkt_win *const win, void *server); bool module_sdl2_toggle_pause(struct lkt_win *const win); bool module_sdl2_load_file(struct lkt_win *const win, const char *filepath); diff --git a/inc/lektor/net.h b/inc/lektor/net.h index e7d244bb356414064b60588d5479ff8a87fd0998..e9985dfc49d267bc0ff314e1f099954e08da16d8 100644 --- a/inc/lektor/net.h +++ b/inc/lektor/net.h @@ -1,6 +1,7 @@ #pragma once #include <common/common.h> +#include <common/queue.h> #include <lektor/mkv.h> #include <lektor/config.h> #include <lektor/window.h> @@ -73,6 +74,7 @@ struct lkt_state { size_t fds_max; char host[HOST_NAME_MAX]; char port[INI_MAX_LINE_LEN]; + struct queue queue; volatile sqlite3 *db; const char *kara_prefix; @@ -84,6 +86,8 @@ struct lkt_state { struct lkt_win win; }; +void lkt_set_event_available(struct lkt_state *srv, enum lkt_event_type type); + /* Send a message to the connected client. */ void lkt_state_send(struct lkt_state *srv, size_t c, struct lkt_message *msg); diff --git a/inc/lektor/reg.h b/inc/lektor/reg.h index d156b68b67261a28e7fe0361bada4720f1668f26..50049e703f5da7e27fd324c65586af08b08a0ff3 100644 --- a/inc/lektor/reg.h +++ b/inc/lektor/reg.h @@ -6,14 +6,15 @@ struct lkt_state; with a name that can be generated or red from a config file. */ struct module_reg { const char *name; - int (*func)(void *, struct lkt_state *, void *); + void (*func)(void); }; extern struct module_reg *reg; -#define REG_BEGIN(reg) struct module_reg reg[] = { -#define REG_ADD(__func) { .name = #__func, .func = __func }, -#define REG_END() { .name = NULL, .func = NULL } }; +#define REG_BEGIN(reg) struct module_reg reg[] = { +#define REG_ADD(__f) { .name = #__f, .func = (void(*)(void)) __f }, +#define REG_ADD_NAMED(__n, __f) { .name = __n, .func = (void(*)(void)) __f }, +#define REG_END() { .name = NULL, .func = NULL } }; /* If handle is NULL, file is unused and the reg is red. Otherwise, we use dlfcn to search for the symbol which is returned. If *handle diff --git a/meson.build b/meson.build index d3a1af2027754a1fab4de39f63da1c8dec346157..9d2322971f33735e289aff57c7ddd71b860f017b 100644 --- a/meson.build +++ b/meson.build @@ -13,6 +13,7 @@ project( 'lektor' ) libdl = meson.get_compiler('c').find_library('dl') +librt = meson.get_compiler('c').find_library('rt') dep_x11 = dependency('x11', required: false) dep_mpv = dependency('mpv', required: false) dep_sdl = dependency('sdl2', required: false) @@ -54,6 +55,7 @@ mthread_sources = [ 'src/mthread/mthread.c' ## Common files common_sources = [ 'src/common.c' , 'src/stack.c' + , 'src/queue.c' , 'src/bufferfd.c' ] @@ -95,6 +97,8 @@ core_deps = [ dependency('sqlite3', version: '>= 3.31.0') , dependency('libcurl', required: true) , dependency('json-c', required: true) , dependency('threads', required: true) + , librt + , libdl ] manpath = custom_target( 'manpath' @@ -116,7 +120,7 @@ lib = both_libraries( 'lektor' , xxd.process('src/database/memory.sql') , manpath , include_directories: includes - , dependencies: [ core_deps, libdl, common_deps ] ) + , dependencies: [ core_deps, common_deps ] ) if get_option('static_liblektor') bin_deps = [ declare_dependency( link_with: lib.get_static_lib(), include_directories: includes) ] diff --git a/src/commands.c b/src/commands.c index 9ad34084ef1317c2dadf1b3ef0b0e2488e2a49b6..d2db41c8adccebea328565ec7007798edc2e0c18 100644 --- a/src/commands.c +++ b/src/commands.c @@ -23,12 +23,28 @@ inline bool command_restart(struct lkt_state *srv, size_t c) { const char *const argv[] = { executable_name, NULL }; + struct lkt_queue_state sta = {0}; RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false); if (!executable_name) { LOG_ERROR_SCT("GENERAL", "%s", "Can't restart if the executable path was not found at start-up"); return false; } close(srv->fds[0].fd); + database_queue_state(srv->db, &sta); + lkt_queue_free(&srv->queue); + env_set(LKT_ENV_RESTART, "1"); + int len = long_length(LONG_MAX); + if (len > 0) { + char *pos = calloc(len, sizeof(char)); + if (pos) { + safe_snprintf(pos, len, "%d", sta.current); + env_set(LKT_ENV_CURRENT, pos); + free(pos); + } else + LOG_WARN_SCT("GENERAL", "Failed to malloc, don't set %s to %d", + LKT_ENV_CURRENT, sta.current); + } else + env_set(LKT_ENV_CURRENT, "NULL"); database_close_all(); execv(executable_name, (char *const *) argv); LOG_ERROR_SCT("GENERAL", "%s", "Failed to exec lektor or OS not supported"); @@ -74,6 +90,7 @@ command_kill(struct lkt_state *srv, size_t c) RETURN_UNLESS(lkt_client_auth(srv, c, false), "Failed to authentificate user", false); LOG_INFO_SCT("GENERAL", "%s", "Stopping lektord"); close(srv->fds[0].fd); + lkt_queue_free(&srv->queue); database_close_all(); exit(EXIT_SUCCESS); } diff --git a/src/database/config.c b/src/database/config.c index 3c53210f002ebe7e7df6482d3965777adc944654..098377b2abd0cf7b80b51fb37b1211d9417cddf9 100644 --- a/src/database/config.c +++ b/src/database/config.c @@ -171,7 +171,7 @@ database_validate_conf(volatile sqlite3 *db) section); \ return false; \ } -#include <lektor/config.inc> +#include <lektor/config.def> #undef section #undef value #undef value_opt diff --git a/src/database/open.c b/src/database/open.c index 6cc01a60cfb485142881b78f45942a949334db73..9e06bc940daee6fb6a13f0142f02b2edefe5d02d 100644 --- a/src/database/open.c +++ b/src/database/open.c @@ -55,7 +55,7 @@ database_close_all(void) } static inline void -__inc(volatile sqlite3 *db, const char *name) +__inc(volatile sqlite3 *db, const char *name, bool check) { SQLITE_EXEC(db, "UPDATE misc SET opened = (SELECT opened + 1 FROM misc);", error); @@ -63,23 +63,22 @@ __inc(volatile sqlite3 *db, const char *name) if (!atexited) { atexited = 1; if (stack_new(&db_stack)) - goto out_of_memory; + exit(EXIT_FAILURE); } struct named_db *item = malloc(sizeof(struct named_db)); if (!item) - goto out_of_memory; + exit(EXIT_FAILURE); item->name = strdup(name); item->db = db; if (stack_push(&db_stack, item)) - goto out_of_memory; + exit(EXIT_FAILURE); return; error: LOG_ERROR_SCT("DB", "%s", "Database already in use"); - exit(EXIT_FAILURE); -out_of_memory: - LOG_ERROR_SCT("DB", "%s", "Out of memory"); - exit(EXIT_FAILURE); + if (check) + exit(EXIT_FAILURE); + __dec(db, name); } int @@ -192,8 +191,7 @@ retry: goto init; if (!__check_schema(db)) goto init; - if (check) - __inc(db, PROTECTED_DATABASE); + __inc(db, PROTECTED_DATABASE, check); return true; /* Need init */ diff --git a/src/main/server.c b/src/main/server.c index e13c0d1cc537faf8610c70f5c54c43c477f98fdb..29ff8e20e029f50753716296f4585e2fe13fa5c6 100644 --- a/src/main/server.c +++ b/src/main/server.c @@ -7,6 +7,7 @@ #include <lektor/reg.h> #include <lektor/database.h> #include <mthread/mthread.h> +#include <lektor/commands.h> #include <assert.h> #include <signal.h> @@ -67,6 +68,8 @@ main(int argc, char *argv[]) normal_launch: LOG_INFO_SCT("GENERAL", "Lektor launched by user %s (shell: %s, home: %s)", pw->pw_name, pw->pw_shell, pw->pw_dir); + if (env_get(LKT_ENV_RESTART)) + LOG_INFO_SCT("GENERAL", "%s", "Lektord has been restarted"); reg_set(server_reg); mthread_init(); pthread_create(&th, NULL, mthread_main, NULL); @@ -76,10 +79,15 @@ normal_launch: executable_name = exe; /* Init the server */ - struct lkt_state srv; char *db_path = safe_malloc(PATH_MAX * sizeof(char)); char *kara_dir = safe_malloc(PATH_MAX * sizeof(char)); - memset(&srv, 0, sizeof(struct lkt_state)); + struct lkt_state srv = { + .kara_prefix = kara_dir, + }; + if (lkt_queue_new(&srv.queue)) { + LOG_ERROR_SCT("INIT", "%s", "Faield to create server queue"); + exit(EXIT_FAILURE); + } /* Initialize the system. */ if (!database_new(&srv.db)) { @@ -114,11 +122,20 @@ normal_launch: srv.kara_prefix = kara_dir; database_config_queue_default(srv.db); - if (autoclear) + if (!env_get(LKT_ENV_RESTART) && autoclear) database_queue_clear(srv.db); RETURN_IF(load_module_by_name(&srv, "player", &srv.win), "Can't load module player", 3); RETURN_IF(load_module_by_name(&srv, "repo", &srv.repo), "Can't load module repo", 3); + + /* Get ENV */ + /* Not working -> race condition with player module */ + char *env_current = env_get(LKT_ENV_CURRENT); + if (env_current && env_current[0] && !STR_MATCH(env_current, "NULL")) { + LOG_INFO_SCT("INIT", "Restart playback from %s", env_current); + lkt_queue_send(&srv.queue, lkt_event_play_pos, (void *) (size_t) strtol(env_current, NULL, 0)); + } + lkt_listen(&srv); return EXIT_FAILURE; } diff --git a/src/module/module_sdl2.c b/src/module/module_sdl2.c index e2f75ec4dad88d46a1334a7c2ae185d37f1829ff..120ce9680049c67fbfb8cbe5eb9ca60c16131cd0 100644 --- a/src/module/module_sdl2.c +++ b/src/module/module_sdl2.c @@ -6,6 +6,7 @@ #include <lektor/module/mpv.h> #include <mthread/mthread.h> #include <lektor/thread.h> +#include <lektor/reg.h> #include <sched.h> #include <assert.h> @@ -165,8 +166,10 @@ loop: break; case SDL_WINDOWEVENT: - if (event.window.event == SDL_WINDOWEVENT_EXPOSED) + if (event.window.event == SDL_WINDOWEVENT_EXPOSED) { + lkt_set_event_available(win->srv, lkt_event_play); redraw = 1; + } break; case SDL_KEYUP: @@ -405,3 +408,15 @@ module_sdl2_handle_events(struct lkt_win *const win, volatile sqlite3 *db, UNUSED(win, db, mpd_idle_events); return true; } + +REG_BEGIN(sdl2_reg) +REG_ADD_NAMED("new", module_sdl2_new) +REG_ADD_NAMED("free", module_sdl2_free) +REG_ADD_NAMED("handle", module_sdl2_handle_events) +REG_ADD(module_sdl2_close) +REG_ADD(module_sdl2_toggle_pause) +REG_ADD(module_sdl2_load_file) +REG_ADD(module_sdl2_set_volume) +REG_ADD(module_sdl2_get_duration) +REG_ADD(module_sdl2_get_elapsed) +REG_END() diff --git a/src/module/module_x11.c b/src/module/module_x11.c index d9231806ea682d52f5f3d3984b9c9b2b5990618a..9c10b1f480e556035f16fbd84ee1662eb5bd2390 100644 --- a/src/module/module_x11.c +++ b/src/module/module_x11.c @@ -5,6 +5,7 @@ #include <lektor/module/mpv.h> #include <lektor/database.h> #include <lektor/commands.h> +#include <lektor/reg.h> #include <string.h> #include <unistd.h> @@ -276,3 +277,15 @@ module_x11_handle_events(struct lkt_win *const win, volatile sqlite3 *db, ! lmpv_handle(win, xwin->mpv, db, mpd_idle_events, &xwin->mpv_time_pos, &xwin->mpv_duration); } + +REG_BEGIN(x11_reg) +REG_ADD_NAMED("new", module_x11_new) +REG_ADD_NAMED("free", module_x11_free) +REG_ADD_NAMED("handle", module_x11_handle_events) +REG_ADD(module_x11_close) +REG_ADD(module_x11_toggle_pause) +REG_ADD(module_x11_load_file) +REG_ADD(module_x11_set_volume) +REG_ADD(module_x11_get_duration) +REG_ADD(module_x11_get_elapsed) +REG_END() diff --git a/src/net/listen.c b/src/net/listen.c index 37d1cbe38b34d83dbcaca97953d4277f0b17ad47..332edcb229a82606b6354ad2e181ab00fc7037f2 100644 --- a/src/net/listen.c +++ b/src/net/listen.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include <common/common.h> +#include <common/queue.h> #include <lektor/commands.h> #include <lektor/database.h> #include <lektor/net.h> @@ -49,6 +50,12 @@ struct lkt_client { int continuation; }; +void +lkt_set_event_available(struct lkt_state *srv, enum lkt_event_type type) +{ + lkt_queue_make_available(&srv->queue, type); +} + static inline bool lkt_close_client(struct lkt_state *srv, size_t c) { @@ -713,6 +720,30 @@ lkt_client_auth(struct lkt_state *srv, size_t c, bool set) return srv->clients[c - 1].authentificated |= set; } +static inline void +handle_queue_events(struct lkt_state *srv) +{ + lkt_event evt; + char *string = calloc(BUFFER_MAX, sizeof(char)); + if (!string) + return; +redo: + evt = lkt_queue_handle(&srv->queue); + + switch (evt.type) { + case lkt_event_play_pos: + safe_snprintf(string, BUFFER_MAX, "%ld", (size_t) evt.attr); + command_play(srv->db, &srv->win, &string, &srv->mpd_idle_events); + break; + + case lkt_event_null: + default: + free(string); + return; + } + goto redo; +} + void lkt_listen(struct lkt_state *srv) { @@ -727,11 +758,13 @@ lkt_listen(struct lkt_state *srv) srv->fds[0].events = POLLIN; srv->fds_len = 1; + /* Listen */ for (;;) { if (handle_network_events(srv) < 0) break; if (handle_idle_events(srv) < 0) break; srv->win.handle_events(&srv->win, srv->db, &srv->mpd_idle_events); + handle_queue_events(srv); } } diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000000000000000000000000000000000000..c872e1f43f88ef9e4e22b3ca97e58f37b616cd5e --- /dev/null +++ b/src/queue.c @@ -0,0 +1,93 @@ +#define _POSIX_C_SOURCE 200809L + +#include <errno.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <common/common.h> +#include <common/queue.h> +#include <pthread.h> + +int +lkt_queue_new(struct queue *ret) +{ + if (!ret) + return 1; + + pthread_mutex_t mxt = PTHREAD_MUTEX_INITIALIZER; + struct queue _ret = { + .contents = malloc(LKT_DEFAULT_LIST_SIZE * sizeof(lkt_event)), + .size = LKT_DEFAULT_LIST_SIZE, + .last = 0, + .available = 0, + .lock = mxt, + }; + + if (_ret.contents == NULL) + return 1; + + *ret = _ret; + return 0; +} + +void +lkt_queue_free(struct queue *queue) +{ + pthread_mutex_lock(&queue->lock); + if (queue && queue->contents) + free((void *) queue->contents); + pthread_mutex_unlock(&queue->lock); +} + +void +lkt_queue_send(struct queue *queue, enum lkt_event_type _type, void *_attr) +{ + pthread_mutex_lock(&queue->lock); + if (!queue) + goto end; + + volatile lkt_event *new; + if (queue->size == queue->last) { + new = realloc((void *) queue->contents, queue->size * 2 * sizeof(lkt_event)); + + if (NULL == new) + goto end; + + queue->contents = new; + queue->size *= 2; + } + + lkt_event evt = { + .type = _type, + .attr = _attr, + }; + queue->contents[(queue->last)++] = evt; +end: + pthread_mutex_unlock(&queue->lock); +} + +lkt_event +lkt_queue_handle(struct queue *queue) +{ + pthread_mutex_lock(&queue->lock); + lkt_event ret = {0}; + + if (!queue || !queue->last || + !(queue->contents[0].type & queue->available)) + goto end; + + ret = queue->contents[0]; + memmove((void *) queue->contents, (void *) (queue->contents + 1), --(queue->last)); +end: + pthread_mutex_unlock(&queue->lock); + return ret; +} + +void +lkt_queue_make_available(struct queue *queue, enum lkt_event_type type) +{ + pthread_mutex_lock(&queue->lock); + queue->available |= type; + pthread_mutex_unlock(&queue->lock); +}