From 06c6e56d4c06bea9208af7b461630f48cdee3cc7 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 19 Oct 2012 16:15:34 +0200 Subject: [PATCH] kmscon: implement per-seat configuration Sorry for the huge commit, but this reworks the whole configuration handler. We now provide conf_ctx contexts which contain a pointer to the backing storage and the config-options that are used. It is also possible to copy config-options now. So we can use the main-config as default value for seat-configurations. Signed-off-by: David Herrmann --- src/conf.c | 1009 +++++++++++++++++++++++++++++---------------- src/conf.h | 129 ++++-- src/kmscon_conf.c | 155 +++---- src/kmscon_conf.h | 32 +- src/kmscon_main.c | 48 ++- src/kmscon_seat.c | 30 +- src/kmscon_seat.h | 3 + src/pty.c | 28 +- src/shl_misc.h | 33 ++ 9 files changed, 925 insertions(+), 542 deletions(-) diff --git a/src/conf.c b/src/conf.c index 7624b47..9954ff5 100644 --- a/src/conf.c +++ b/src/conf.c @@ -44,356 +44,137 @@ #define LOG_SUBSYSTEM "config" -void conf_free_value(struct conf_option *opt) -{ - if (*(void**)opt->mem && *(void**)opt->mem != opt->def) { - free(*(void**)opt->mem); - *(void**)opt->mem = NULL; - } -} +/* + * Config Contexts + */ -int conf_parse_bool(struct conf_option *opt, bool on, const char *arg) -{ - *(bool*)opt->mem = on; - return 0; -} +struct conf_ctx { + struct conf_option *opts; + size_t onum; + void *mem; +}; -void conf_default_bool(struct conf_option *opt) +int conf_ctx_new(struct conf_ctx **out, const struct conf_option *opts, + size_t onum, void *mem) { - *(bool*)opt->mem = (bool)opt->def; -} + struct conf_ctx *ctx; + size_t size; -int conf_parse_int(struct conf_option *opt, bool on, const char *arg) -{ - *(int*)opt->mem = atoi(arg); - return 0; -} + if (!out || !opts || !onum) + return -EINVAL; -void conf_default_int(struct conf_option *opt) -{ - *(int*)opt->mem = (int)(unsigned long)opt->def; -} - -int conf_parse_uint(struct conf_option *opt, bool on, const char *arg) -{ - *(unsigned int*)opt->mem = atoi(arg); - return 0; -} - -void conf_default_uint(struct conf_option *opt) -{ - *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def; -} - -int conf_parse_string(struct conf_option *opt, bool on, const char *arg) -{ - char *val = strdup(arg); - if (!val) + size = sizeof(*ctx) + onum * sizeof(*opts); + ctx = malloc(size); + if (!ctx) { + log_error("cannot allocate memory for config context"); return -ENOMEM; + } + memset(ctx, 0, size); + ctx->opts = (void*)((char*)ctx + sizeof(*ctx)); + ctx->onum = onum; + ctx->mem = mem; + memcpy(ctx->opts, opts, onum * sizeof(*opts)); - opt->type->free(opt); - *(void**)opt->mem = val; + conf_ctx_reset(ctx); + + *out = ctx; return 0; } -void conf_default_string(struct conf_option *opt) +void conf_ctx_free(struct conf_ctx *ctx) { - *(void**)opt->mem = opt->def; -} - -int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg) -{ - int ret; - char **list; - - ret = shl_split_string(arg, &list, NULL, ',', true); - if (ret) - return ret; - - opt->type->free(opt); - *(void**)opt->mem = list; - return 0; -} - -void conf_default_string_list(struct conf_option *opt) -{ - *(void**)opt->mem = opt->def; -} - -static int parse_single_grab(char *arg, unsigned int *mods, - uint32_t *keysym, bool allow_mods) -{ - char *tmp, *start, *end; - - tmp = arg; - do { - while (*tmp == ' ') - ++tmp; - if (!allow_mods) - break; - if (*tmp != '<') - break; - - start = tmp; - while (*tmp && *tmp != '>') - ++tmp; - - if (*tmp != '>') { - log_error("missing '>' near '%s'", start); - return -EFAULT; - } - - *tmp++ = 0; - ++start; - if (!strcasecmp(start, "shift")) { - *mods |= SHL_SHIFT_MASK; - } else if (!strcasecmp(start, "lock")) { - *mods |= SHL_LOCK_MASK; - } else if (!strcasecmp(start, "control") || - !strcasecmp(start, "ctrl")) { - *mods |= SHL_CONTROL_MASK; - } else if (!strcasecmp(start, "alt")) { - *mods |= SHL_ALT_MASK; - } else if (!strcasecmp(start, "logo")) { - *mods |= SHL_LOGO_MASK; - } else { - log_error("invalid modifier '%s'", start); - return -EFAULT; - } - } while (1); - - while (*tmp == ' ') - ++tmp; - - start = tmp; - end = start; - do { - while (*tmp && *tmp != ' ') - ++tmp; - end = tmp; - if (!*tmp) - break; - while (*tmp == ' ') - ++tmp; - } while (1); - - if (start == end) - return 0; - if (*end) - *end = 0; - - *keysym = xkb_keysym_from_name(start); - if (!*keysym) { - log_error("invalid key '%s'", start); - return -EFAULT; - } - - return 1; -} - -int conf_parse_grab(struct conf_option *opt, bool on, const char *arg) -{ - char **list, **keys; - unsigned int list_num, key_num, i, j, k, l; - int ret; - struct conf_grab *grab; - - ret = shl_split_string(arg, &list, &list_num, ',', false); - if (ret) - return ret; - - grab = malloc(sizeof(*grab)); - if (!grab) { - ret = -ENOMEM; - goto err_list; - } - memset(grab, 0, sizeof(*grab)); - - if (list_num) { - grab->mods = malloc(sizeof(*grab->mods) * list_num); - if (!grab->mods) { - ret = -ENOMEM; - goto err_grab; - } - memset(grab->mods, 0, sizeof(*grab->mods) * list_num); - - grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num); - if (!grab->num_syms) { - ret = -ENOMEM; - goto err_grab; - } - memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num); - - grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num); - if (!grab->keysyms) { - ret = -ENOMEM; - goto err_grab; - } - memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num); - } - - l = 0; - for (i = 0; i < list_num; ++i) { - ret = shl_split_string(list[i], &keys, &key_num, '+', false); - if (ret) - goto err_all; - if (!key_num) { - free(keys); - continue; - } - - grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l] * key_num)); - if (!grab->keysyms[l]) { - ret = -ENOMEM; - free(keys); - goto err_all; - } - - k = 0; - for (j = 0; j < key_num; ++j) { - ret = parse_single_grab(keys[j], &grab->mods[l], - &grab->keysyms[l][k], - j == 0); - if (ret < 0) { - log_error("cannot parse grab '%s' in '%s'", - list[i], arg); - free(keys); - goto err_all; - } - k += ret; - } - - free(keys); - if (!k) - continue; - grab->num_syms[l] = k; - ++l; - ++grab->num; - } - - free(list); - opt->type->free(opt); - *(void**)opt->mem = grab; - return 0; - -err_all: - for (i = 0; i < list_num; ++i) - free(grab->keysyms[i]); -err_grab: - free(grab->keysyms); - free(grab->num_syms); - free(grab->mods); - free(grab); -err_list: - free(list); - return ret; -} - -void conf_free_grab(struct conf_option *opt) -{ - struct conf_grab *grab; - unsigned int i; - - if (!*(void**)opt->mem || *(void**)opt->mem == opt->def) + if (!ctx) return; - grab = *(void**)opt->mem; - *(void**)opt->mem = NULL; - - for (i = 0; i < grab->num; ++i) - free(grab->keysyms[i]); - - free(grab->keysyms); - free(grab->num_syms); - free(grab->mods); - free(grab); + conf_ctx_reset(ctx); + free(ctx); } -void conf_default_grab(struct conf_option *opt) -{ - *(void**)opt->mem = opt->def; -} - -const struct conf_type conf_bool = { - .flags = 0, - .parse = conf_parse_bool, - .free = NULL, - .set_default = conf_default_bool, -}; - -const struct conf_type conf_int = { - .flags = CONF_HAS_ARG, - .parse = conf_parse_int, - .free = NULL, - .set_default = conf_default_int, -}; - -const struct conf_type conf_uint = { - .flags = CONF_HAS_ARG, - .parse = conf_parse_uint, - .free = NULL, - .set_default = conf_default_uint, -}; - -const struct conf_type conf_string = { - .flags = CONF_HAS_ARG, - .parse = conf_parse_string, - .free = conf_free_value, - .set_default = conf_default_string, -}; - -const struct conf_type conf_string_list = { - .flags = CONF_HAS_ARG, - .parse = conf_parse_string_list, - .free = conf_free_value, - .set_default = conf_default_string_list, -}; - -const struct conf_type conf_grab = { - .flags = CONF_HAS_ARG, - .parse = conf_parse_grab, - .free = conf_free_grab, - .set_default = conf_default_grab, -}; - -/* free all memory that we allocated and reset to initial state */ -void conf_free(struct conf_option *opts, size_t len) +void conf_ctx_reset(struct conf_ctx *ctx) { unsigned int i; - for (i = 0; i < len; ++i) { - if (opts[i].type->free) - opts[i].type->free(&opts[i]); + if (!ctx) + return; + + for (i = 0; i < ctx->onum; ++i) { + if (ctx->opts[i].type->free) + ctx->opts[i].type->free(&ctx->opts[i]); + ctx->opts[i].flags = 0; + if (ctx->opts[i].type->set_default) + ctx->opts[i].type->set_default(&ctx->opts[i]); } } +void *conf_ctx_get_mem(struct conf_ctx *ctx) +{ + if (!ctx) + return NULL; + + return ctx->mem; +} + +/* + * Copy all entries from \src into \ctx + * This calls the "copy" callback for each option inside of \ctx with the + * corresponding option inside of \src if both have the same type. If the types + * do not match, nothing is done. + */ +int conf_ctx_parse_ctx(struct conf_ctx *ctx, const struct conf_ctx *src) +{ + unsigned int i; + struct conf_option *d, *s; + int ret; + + if (!ctx || !src) + return -EINVAL; + + for (i = 0; i < ctx->onum && i < src->onum; ++i) { + d = &ctx->opts[i]; + s = &src->opts[i]; + + if (d->type != s->type) + continue; + if (!d->type->copy) + continue; + + ret = d->type->copy(d, s); + if (ret) + return ret; + } + + return 0; +} + /* * Parse command line arguments * This temporarily allocates the short_options and long_options arrays so we * can use the getopt_long() library call. It locks all arguments after they * have been set so command-line options will always overwrite config-options. */ -int conf_parse_argv(struct conf_option *opts, size_t len, - int argc, char **argv) +int conf_ctx_parse_argv(struct conf_ctx *ctx, int argc, char **argv) { char *short_options; struct option *long_options; struct option *opt; + struct conf_option *o; size_t i, pos; int c, ret; - if (!argv || argc < 1) + if (!ctx || !argv) return -EINVAL; - short_options = malloc(sizeof(char) * (len + 1) * 2); + short_options = malloc(sizeof(char) * (ctx->onum + 1) * 2); if (!short_options) { - log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno); + log_error("out of memory to parse cmd-line arguments (%d): %m", + errno); return -ENOMEM; } - long_options = malloc(sizeof(struct option) * len * 2); + long_options = malloc(sizeof(struct option) * ctx->onum * 2); if (!long_options) { - log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno); + log_error("out of memory to parse cmd-line arguments (%d): %m", + errno); free(short_options); return -ENOMEM; } @@ -401,24 +182,25 @@ int conf_parse_argv(struct conf_option *opts, size_t len, pos = 0; short_options[pos++] = ':'; opt = long_options; - for (i = 0; i < len; ++i) { - if (opts[i].short_name) { - short_options[pos++] = opts[i].short_name; - if (opts[i].type->flags & CONF_HAS_ARG) + for (i = 0; i < ctx->onum; ++i) { + if (ctx->opts[i].short_name) { + short_options[pos++] = ctx->opts[i].short_name; + if (ctx->opts[i].type->flags & CONF_HAS_ARG) short_options[pos++] = ':'; } - if (opts[i].long_name) { + if (ctx->opts[i].long_name) { /* skip the "no-" prefix */ - opt->name = &opts[i].long_name[3]; - opt->has_arg = !!(opts[i].type->flags & CONF_HAS_ARG); + opt->name = &ctx->opts[i].long_name[3]; + opt->has_arg = !!(ctx->opts[i].type->flags & + CONF_HAS_ARG); opt->flag = NULL; opt->val = 100000 + i; ++opt; /* boolean args are also added with "no-" prefix */ - if (!(opts[i].type->flags & CONF_HAS_ARG)) { - opt->name = opts[i].long_name; + if (!(ctx->opts[i].type->flags & CONF_HAS_ARG)) { + opt->name = ctx->opts[i].long_name; opt->has_arg = 0; opt->flag = NULL; opt->val = 200000 + i; @@ -446,50 +228,56 @@ int conf_parse_argv(struct conf_option *opts, size_t len, fprintf(stderr, "Unknown argument: %s\n", argv[optind - 1]); else - fprintf(stderr, "Parameter takes no argument: %s\n", + fprintf(stderr, "Option takes no arg: %s\n", argv[optind - 1]); return -EFAULT; } else if (c < 100000) { - for (i = 0; i < len; ++i) { - if (opts[i].short_name == c) { - ret = opts[i].type->parse(&opts[i], - true, - optarg); + for (i = 0; i < ctx->onum; ++i) { + o = &ctx->opts[i]; + + if (o->short_name != c) + continue; + + if (o->type->parse) { + ret = o->type->parse(o, true, optarg); if (ret) return ret; - opts[i].flags |= CONF_LOCKED; - opts[i].flags |= CONF_DONE; - break; } + + o->flags |= CONF_LOCKED; + o->flags |= CONF_DONE; + break; } } else if (c < 200000) { i = c - 100000; - ret = opts[i].type->parse(&opts[i], true, optarg); - if (ret) - return ret; - opts[i].flags |= CONF_LOCKED; - opts[i].flags |= CONF_DONE; + o = &ctx->opts[i]; + + if (o->type->parse) { + ret = o->type->parse(o, true, optarg); + if (ret) + return ret; + } + + o->flags |= CONF_LOCKED; + o->flags |= CONF_DONE; } else { i = c - 200000; - ret = opts[i].type->parse(&opts[i], false, NULL); - if (ret) - return ret; - opts[i].flags |= CONF_LOCKED; - opts[i].flags |= CONF_DONE; + o = &ctx->opts[i]; + + if (o->type->parse) { + ret = o->type->parse(o, false, NULL); + if (ret) + return ret; + } + + o->flags |= CONF_LOCKED; + o->flags |= CONF_DONE; } } free(long_options); free(short_options); - /* set default values if not configured */ - for (i = 0; i < len; ++i) { - if (!(opts[i].flags & CONF_DONE) && - opts[i].type->set_default) { - opts[i].type->set_default(&opts[i]); - } - } - /* Perform aftercheck: * All arguments that provide an aftercheck will be passed the remaining * arguments in order. If they return a negative error code, it is @@ -500,9 +288,10 @@ int conf_parse_argv(struct conf_option *opts, size_t len, * The next argument's aftercheck will only get the now remaning * arguments passed in. If not all arguments are consumed, then this * function will report an error to the caller. */ - for (i = 0; i < len; ++i) { - if (opts[i].aftercheck) { - ret = opts[i].aftercheck(&opts[i], argc, argv, optind); + for (i = 0; i < ctx->onum; ++i) { + o = &ctx->opts[i]; + if (o->aftercheck) { + ret = o->aftercheck(o, argc, argv, optind); if (ret < 0) return ret; optind += ret; @@ -510,7 +299,7 @@ int conf_parse_argv(struct conf_option *opts, size_t len, } if (optind < argc) { - fprintf(stderr, "Unparsed remaining arguments starting with: %s\n", + fprintf(stderr, "Unparsed remaining args starting with: %s\n", argv[optind]); return -EFAULT; } @@ -539,10 +328,12 @@ static int parse_kv_pair(struct conf_option *opts, size_t len, continue; if (opt->type->flags & CONF_HAS_ARG && !value) { - log_error("config option '%s' requires an argument", key); + log_error("config option '%s' requires an argument", + key); return -EFAULT; } else if (!(opt->type->flags & CONF_HAS_ARG) && value) { - log_error("config option '%s' does not take arguments", key); + log_error("config option '%s' does not take arguments", + key); return -EFAULT; } @@ -550,9 +341,11 @@ static int parse_kv_pair(struct conf_option *opts, size_t len, if (opt->flags & CONF_LOCKED) return 0; - ret = opt->type->parse(opt, set, value); - if (ret) - return ret; + if (opt->type->parse) { + ret = opt->type->parse(opt, set, value); + if (ret) + return ret; + } opt->flags |= CONF_DONE; return 0; @@ -696,7 +489,8 @@ static int parse_buffer(struct conf_option *opts, size_t len, /* This reads the file at \path in memory and parses it as if it was given as * command line options. */ -int conf_parse_file(struct conf_option *opts, size_t len, const char *path) +static int conf_parse_file(struct conf_option *opts, size_t len, + const char *path) { int fd, ret; size_t size, pos; @@ -756,14 +550,13 @@ out_free: return ret; } -int conf_parse_file_f(struct conf_option *opts, size_t len, - const char *format, ...) +int conf_ctx_parse_file(struct conf_ctx *ctx, const char *format, ...) { va_list list; char *path; int ret; - if (!opts || !len || !format) + if (!ctx || !format) return -EINVAL; va_start(list, format); @@ -775,7 +568,505 @@ int conf_parse_file_f(struct conf_option *opts, size_t len, return -ENOMEM; } - ret = conf_parse_file(opts, len, path); + ret = conf_parse_file(ctx->opts, ctx->onum, path); free(path); return ret; } + +/* + * Config Types + * Each option that can be parsed must be a specific config-type. A config-type + * is used to parse, free and reset the value. It must implement the following + * callbacks: + * set_default: This should link opt->mem to opt->def and must not fail. It is + * called during initialization and reset. + * free: This should free any allocated memory and reset the option to the + * initial state. It must not fail. + * parse: This should parse a command-line option. Return 0 on success. + * copy: Copy data from source into destination. Return 0 on success. + * + * The backing memory is zeroed on reset so a config-type must be able to handle + * this as "not set". Also, the "free" callback should reset it to zero (which + * is the initial state). + */ + +/* Miscellaneous helper */ + +static void conf_free_value(struct conf_option *opt) +{ + if (*(void**)opt->mem) { + if (*(void**)opt->mem != opt->def) + free(*(void**)opt->mem); + *(void**)opt->mem = NULL; + } +} + +/* Boolean Option */ + +static void conf_default_bool(struct conf_option *opt) +{ + *(bool*)opt->mem = (bool)opt->def; +} + +static void conf_free_bool(struct conf_option *opt) +{ + *(bool*)opt->mem = false; +} + +static int conf_parse_bool(struct conf_option *opt, bool on, const char *arg) +{ + *(bool*)opt->mem = on; + return 0; +} + +static int conf_copy_bool(struct conf_option *opt, + const struct conf_option *src) +{ + *(bool*)opt->mem = *(bool*)src->mem; + return 0; +} + +const struct conf_type conf_bool = { + .flags = 0, + .set_default = conf_default_bool, + .free = conf_free_bool, + .parse = conf_parse_bool, + .copy = conf_copy_bool, +}; + +/* Int Option */ + +static int conf_parse_int(struct conf_option *opt, bool on, const char *arg) +{ + *(int*)opt->mem = atoi(arg); + return 0; +} + +static void conf_free_int(struct conf_option *opt) +{ + *(int*)opt->mem = 0; +} + +static void conf_default_int(struct conf_option *opt) +{ + *(int*)opt->mem = (int)(unsigned long)opt->def; +} + +static int conf_copy_int(struct conf_option *opt, + const struct conf_option *src) +{ + *(int*)opt->mem = *(int*)src->mem; + return 0; +} + +const struct conf_type conf_int = { + .flags = CONF_HAS_ARG, + .set_default = conf_default_int, + .free = conf_free_int, + .parse = conf_parse_int, + .copy = conf_copy_int, +}; + +/* Unsigned Int Option */ + +static void conf_default_uint(struct conf_option *opt) +{ + *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def; +} + +static void conf_free_uint(struct conf_option *opt) +{ + *(unsigned int*)opt->mem = 0; +} + +static int conf_parse_uint(struct conf_option *opt, bool on, const char *arg) +{ + *(unsigned int*)opt->mem = atoi(arg); + return 0; +} + +static int conf_copy_uint(struct conf_option *opt, + const struct conf_option *src) +{ + *(unsigned int*)opt->mem = *(unsigned int*)src->mem; + return 0; +} + +const struct conf_type conf_uint = { + .flags = CONF_HAS_ARG, + .set_default = conf_default_uint, + .free = conf_free_uint, + .parse = conf_parse_uint, + .copy = conf_copy_uint, +}; + +/* String Option */ + +static void conf_default_string(struct conf_option *opt) +{ + opt->type->free(opt); + *(void**)opt->mem = opt->def; +} + +static int conf_parse_string(struct conf_option *opt, bool on, const char *arg) +{ + char *val = strdup(arg); + if (!val) + return -ENOMEM; + + opt->type->free(opt); + *(void**)opt->mem = val; + return 0; +} + +static int conf_copy_string(struct conf_option *opt, + const struct conf_option *src) +{ + char *val; + + if (!*(void**)src->mem) { + val = NULL; + } else { + val = strdup(*(void**)src->mem); + if (!val) + return -ENOMEM; + } + + opt->type->free(opt); + *(void**)opt->mem = val; + return 0; +} + +const struct conf_type conf_string = { + .flags = CONF_HAS_ARG, + .set_default = conf_default_string, + .free = conf_free_value, + .parse = conf_parse_string, + .copy = conf_copy_string, +}; + +/* Stringlist Option */ + +static void conf_default_string_list(struct conf_option *opt) +{ + opt->type->free(opt); + *(void**)opt->mem = opt->def; +} + +static int conf_parse_string_list(struct conf_option *opt, bool on, + const char *arg) +{ + int ret; + char **list; + + ret = shl_split_string(arg, &list, NULL, ',', true); + if (ret) + return ret; + + opt->type->free(opt); + *(char***)opt->mem = list; + return 0; +} + +static int conf_copy_string_list(struct conf_option *opt, + const struct conf_option *src) +{ + int ret; + char **t; + + if (!(void***)src->mem) { + t = NULL; + } else { + ret = shl_dup_array(&t, *(char***)src->mem); + if (ret) + return ret; + } + + opt->type->free(opt); + *(char***)opt->mem = t; + return 0; +} + +const struct conf_type conf_string_list = { + .flags = CONF_HAS_ARG, + .set_default = conf_default_string_list, + .free = conf_free_value, + .parse = conf_parse_string_list, + .copy = conf_copy_string_list, +}; + +/* Grab Option */ + +static void conf_default_grab(struct conf_option *opt) +{ + opt->type->free(opt); + *(void**)opt->mem = opt->def; +} + +static void conf_free_grab(struct conf_option *opt) +{ + struct conf_grab *grab; + unsigned int i; + + grab = *(void**)opt->mem; + *(void**)opt->mem = NULL; + + if (!grab || grab == opt->def) + return; + + for (i = 0; i < grab->num; ++i) + free(grab->keysyms[i]); + + free(grab->keysyms); + free(grab->num_syms); + free(grab->mods); + free(grab); +} + +static int parse_single_grab(char *arg, unsigned int *mods, + uint32_t *keysym, bool allow_mods) +{ + char *tmp, *start, *end; + + tmp = arg; + do { + while (*tmp == ' ') + ++tmp; + if (!allow_mods) + break; + if (*tmp != '<') + break; + + start = tmp; + while (*tmp && *tmp != '>') + ++tmp; + + if (*tmp != '>') { + log_error("missing '>' near '%s'", start); + return -EFAULT; + } + + *tmp++ = 0; + ++start; + if (!strcasecmp(start, "shift")) { + *mods |= SHL_SHIFT_MASK; + } else if (!strcasecmp(start, "lock")) { + *mods |= SHL_LOCK_MASK; + } else if (!strcasecmp(start, "control") || + !strcasecmp(start, "ctrl")) { + *mods |= SHL_CONTROL_MASK; + } else if (!strcasecmp(start, "alt")) { + *mods |= SHL_ALT_MASK; + } else if (!strcasecmp(start, "logo")) { + *mods |= SHL_LOGO_MASK; + } else { + log_error("invalid modifier '%s'", start); + return -EFAULT; + } + } while (1); + + while (*tmp == ' ') + ++tmp; + + start = tmp; + end = start; + do { + while (*tmp && *tmp != ' ') + ++tmp; + end = tmp; + if (!*tmp) + break; + while (*tmp == ' ') + ++tmp; + } while (1); + + if (start == end) + return 0; + if (*end) + *end = 0; + + *keysym = xkb_keysym_from_name(start); + if (!*keysym) { + log_error("invalid key '%s'", start); + return -EFAULT; + } + + return 1; +} + +static int conf_parse_grab(struct conf_option *opt, bool on, const char *arg) +{ + char **list, **keys; + unsigned int list_num, key_num, i, j, k, l; + int ret; + struct conf_grab *grab; + + ret = shl_split_string(arg, &list, &list_num, ',', false); + if (ret) + return ret; + + grab = malloc(sizeof(*grab)); + if (!grab) { + ret = -ENOMEM; + goto err_list; + } + memset(grab, 0, sizeof(*grab)); + + if (list_num) { + grab->mods = malloc(sizeof(*grab->mods) * list_num); + if (!grab->mods) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->mods, 0, sizeof(*grab->mods) * list_num); + + grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num); + if (!grab->num_syms) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num); + + grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num); + if (!grab->keysyms) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num); + } + + l = 0; + for (i = 0; i < list_num; ++i) { + ret = shl_split_string(list[i], &keys, &key_num, '+', false); + if (ret) + goto err_all; + if (!key_num) { + free(keys); + continue; + } + + grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l]) * key_num); + if (!grab->keysyms[l]) { + ret = -ENOMEM; + free(keys); + goto err_all; + } + + k = 0; + for (j = 0; j < key_num; ++j) { + ret = parse_single_grab(keys[j], &grab->mods[l], + &grab->keysyms[l][k], + j == 0); + if (ret < 0) { + log_error("cannot parse grab '%s' in '%s'", + list[i], arg); + free(keys); + goto err_all; + } + k += ret; + } + + free(keys); + if (!k) + continue; + grab->num_syms[l] = k; + ++l; + ++grab->num; + } + + free(list); + opt->type->free(opt); + *(void**)opt->mem = grab; + return 0; + +err_all: + for (i = 0; i < list_num; ++i) + free(grab->keysyms[i]); +err_grab: + free(grab->keysyms); + free(grab->num_syms); + free(grab->mods); + free(grab); +err_list: + free(list); + return ret; +} + +static int conf_copy_grab(struct conf_option *opt, + const struct conf_option *src) +{ + struct conf_grab *grab, *s; + int ret; + unsigned int i; + + s = *(void**)src->mem; + + if (!s) { + opt->type->free(opt); + *(void**)opt->mem = NULL; + return 0; + } + + grab = malloc(sizeof(*grab)); + if (!grab) + return -ENOMEM; + memset(grab, 0, sizeof(*grab)); + grab->num = s->num; + + if (grab->num) { + grab->mods = malloc(sizeof(*grab->mods) * grab->num); + if (!grab->mods) { + ret = -ENOMEM; + goto err_grab; + } + memcpy(grab->mods, s->mods, sizeof(*grab->mods) * grab->num); + + grab->num_syms = malloc(sizeof(*grab->num_syms) * grab->num); + if (!grab->num_syms) { + ret = -ENOMEM; + goto err_grab; + } + memcpy(grab->num_syms, s->num_syms, + sizeof(*grab->num_syms) * grab->num); + + grab->keysyms = malloc(sizeof(*grab->keysyms) * grab->num); + if (!grab->keysyms) { + ret = -ENOMEM; + goto err_grab; + } + memset(grab->keysyms, 0, sizeof(*grab->keysyms) * grab->num); + } + + for (i = 0; i < grab->num; ++i) { + grab->keysyms[i] = malloc(sizeof(*s->keysyms[i]) * + s->num_syms[i]); + if (!grab->keysyms[i]) { + ret = -ENOMEM; + goto err_all; + } + memcpy(grab->keysyms[i], s->keysyms[i], + sizeof(*s->keysyms[i]) * s->num_syms[i]); + } + + opt->type->free(opt); + *(void**)opt->mem = grab; + return 0; + +err_all: + for (i = 0; i < grab->num; ++i) + free(grab->keysyms[i]); +err_grab: + free(grab->keysyms); + free(grab->num_syms); + free(grab->mods); + free(grab); + return ret; +} + +const struct conf_type conf_grab = { + .flags = CONF_HAS_ARG, + .set_default = conf_default_grab, + .free = conf_free_grab, + .parse = conf_parse_grab, + .copy = conf_copy_grab, +}; diff --git a/src/conf.h b/src/conf.h index 9982f71..5c02fda 100644 --- a/src/conf.h +++ b/src/conf.h @@ -37,7 +37,67 @@ #include #include "shl_misc.h" -/* parsed types */ +struct conf_type; +struct conf_option; +struct conf_ctx; + +/* Conf Types */ + +#define CONF_HAS_ARG 0x0001 + +struct conf_type { + unsigned int flags; + void (*set_default) (struct conf_option *opt); + void (*free) (struct conf_option *opt); + int (*parse) (struct conf_option *opt, bool on, const char *arg); + int (*copy) (struct conf_option *opt, const struct conf_option *src); +}; + +/* + * Bool: expects "mem" to point to a "bool" + * Initial state is "false". + */ + +extern const struct conf_type conf_bool; + +/* + * Int: expects "mem" to point to an "int" + * Initial state is "0". + */ + +extern const struct conf_type conf_int; + +/* + * Uint: expects "mem" to point to an "uint" + * Initial state is "0" + */ + +extern const struct conf_type conf_uint; + +/* + * String: expects "mem" to point to an "char*" + * Initial state is NULL. Memory is allocated by the parser and a string is + * always zero-terminated. + */ + +extern const struct conf_type conf_string; + +/* + * Stringlist: expects "mem" to point to an "char**" + * Initial state is NULL. The list is NULL-terminated and each entry is a + * zero-terminated string. Memory is allocated by the parser. + */ + +extern const struct conf_type conf_string_list; + +/* + * Grabs: expects "mem" to point to an "struct conf_grab*" + * Initial state is NULL. See below for the type definition. The memory for the + * type is allocated by the parser. + * Two small helpers are available to ease the use. + */ + +extern const struct conf_type conf_grab; struct conf_grab { unsigned int num; @@ -63,25 +123,36 @@ static inline bool conf_grab_matches(const struct conf_grab *grab, .keysyms = (uint32_t*[]) { (uint32_t[]) { (_sym) } }, \ } -/* configuration parser */ +/* + * Configuration Context + * A configuration context is initialized with an array of config-options and + * then can be used to parse different sources. The backing memory is managed by + * the user, not by this context. + * All options are set to their default values on startup and reset. + */ -struct conf_type; -struct conf_option; +struct conf_ctx; + +int conf_ctx_new(struct conf_ctx **out, const struct conf_option *opts, + size_t onum, void *mem); +void conf_ctx_free(struct conf_ctx *ctx); +void conf_ctx_reset(struct conf_ctx *ctx); +void *conf_ctx_get_mem(struct conf_ctx *ctx); + +int conf_ctx_parse_ctx(struct conf_ctx *ctx, const struct conf_ctx *src); +int conf_ctx_parse_argv(struct conf_ctx *ctx, int argc, char **argv); +int conf_ctx_parse_file(struct conf_ctx *ctx, const char *format, ...); + +/* + * Configuration Options + * A configuration option specifies the name of the option, the type, the + * backing memory, the default value and more. Each option is represented by + * this structure. + */ -/* config option flags */ #define CONF_DONE 0x0001 #define CONF_LOCKED 0x0002 -/* config type flags */ -#define CONF_HAS_ARG 0x0001 - -struct conf_type { - unsigned int flags; - int (*parse) (struct conf_option *opt, bool on, const char *arg); - void (*free) (struct conf_option *opt); - void (*set_default) (struct conf_option *opt); -}; - struct conf_option { unsigned int flags; char short_name; @@ -144,32 +215,4 @@ struct conf_option { _mem, \ _def) -void conf_free_value(struct conf_option *opt); -int conf_parse_bool(struct conf_option *opt, bool on, const char *arg); -void conf_default_bool(struct conf_option *opt); -int conf_parse_int(struct conf_option *opt, bool on, const char *arg); -void conf_default_int(struct conf_option *opt); -int conf_parse_uint(struct conf_option *opt, bool on, const char *arg); -void conf_default_uint(struct conf_option *opt); -int conf_parse_string(struct conf_option *opt, bool on, const char *arg); -void conf_default_string(struct conf_option *opt); -int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg); -void conf_default_string_list(struct conf_option *opt); -int conf_parse_grab(struct conf_option *opt, bool on, const char *arg); -void conf_default_grab(struct conf_option *opt); - -extern const struct conf_type conf_bool; -extern const struct conf_type conf_int; -extern const struct conf_type conf_uint; -extern const struct conf_type conf_string; -extern const struct conf_type conf_string_list; -extern const struct conf_type conf_grab; - -void conf_free(struct conf_option *opts, size_t len); -int conf_parse_argv(struct conf_option *opts, size_t len, - int argc, char **argv); -int conf_parse_file(struct conf_option *opts, size_t len, const char *path); -int conf_parse_file_f(struct conf_option *opts, size_t len, - const char *format, ...); - #endif /* CONFIG_CONFIG_H */ diff --git a/src/kmscon_conf.c b/src/kmscon_conf.c index a863388..3301cd9 100644 --- a/src/kmscon_conf.c +++ b/src/kmscon_conf.c @@ -38,8 +38,6 @@ #include "shl_misc.h" struct kmscon_conf_t kmscon_conf; -static struct conf_option *kmscon_opt; -static size_t kmscon_onum; static void print_help() { @@ -149,6 +147,21 @@ static void print_help() */ } +static void conf_default_vt(struct conf_option *opt) +{ + opt->type->free(opt); + *(void**)opt->mem = opt->def; +} + +static void conf_free_vt(struct conf_option *opt) +{ + if (*(void**)opt->mem) { + if (*(void**)opt->mem != opt->def) + free(*(void**)opt->mem); + *(void**)opt->mem = NULL; + } +} + static int conf_parse_vt(struct conf_option *opt, bool on, const char *arg) { static const char prefix[] = "/dev/"; @@ -178,18 +191,35 @@ static int conf_parse_vt(struct conf_option *opt, bool on, const char *arg) return 0; } -static void conf_default_vt(struct conf_option *opt) +static int conf_copy_vt(struct conf_option *opt, + const struct conf_option *src) { - *(void**)opt->mem = opt->def; + char *val; + + if (!*(void**)src->mem) { + val = NULL; + } else { + val = strdup(*(void**)src->mem); + if (!val) + return -ENOMEM; + } + + opt->type->free(opt); + *(void**)opt->mem = val; + return 0; } static const struct conf_type conf_vt = { .flags = CONF_HAS_ARG, - .parse = conf_parse_vt, - .free = conf_free_value, .set_default = conf_default_vt, + .free = conf_free_vt, + .parse = conf_parse_vt, + .copy = conf_copy_vt, }; +#define KMSCON_CONF_FROM_FIELD(_mem, _name) \ + shl_offsetof((_mem), struct kmscon_conf_t, _name) + static int aftercheck_debug(struct conf_option *opt, int argc, char **argv, int idx) { @@ -281,20 +311,12 @@ static struct conf_grab def_grab_session_close = static struct conf_grab def_grab_terminal_new = CONF_SINGLE_GRAB(SHL_CONTROL_MASK | SHL_ALT_MASK, XKB_KEY_Return); -void kmscon_conf_init(struct kmscon_conf_t *conf) +int kmscon_conf_new(struct conf_ctx **out, struct kmscon_conf_t *conf) { - if (!conf) - return; + struct conf_ctx *ctx; + int ret; - memset(conf, 0, sizeof(*conf)); -} - -int kmscon_conf_new(struct conf_option **out, size_t *size_out, - struct kmscon_conf_t *conf) -{ - struct conf_option *opt; - - if (!out || !size_out || !conf) + if (!out || !conf) return -EINVAL; struct conf_option options[] = { @@ -349,71 +371,47 @@ int kmscon_conf_new(struct conf_option **out, size_t *size_out, CONF_OPTION_UINT(0, "font-dpi", NULL, &conf->font_ppi, 96), }; - opt = malloc(sizeof(options)); - if (!opt) - return -ENOMEM; - memcpy(opt, options, sizeof(options)); - - *out = opt; - *size_out = sizeof(options) / sizeof(*options); - return 0; -} - -void kmscon_conf_free(struct conf_option *opt, size_t onum) -{ - if (!opt || !onum) - return; - - conf_free(opt, onum); - free(opt); -} - -int kmscon_conf_parse_argv(struct conf_option *opt, size_t onum, - int argc, char **argv) -{ - if (!opt || !onum) - return -EINVAL; - - return conf_parse_argv(opt, onum, argc, argv); -} - -int kmscon_load_config(int argc, char **argv) -{ - int ret; - - if (!kmscon_opt || !kmscon_onum) { - kmscon_conf_init(&kmscon_conf); - ret = kmscon_conf_new(&kmscon_opt, &kmscon_onum, &kmscon_conf); - if (ret) - return ret; - } - - ret = kmscon_conf_parse_argv(kmscon_opt, kmscon_onum, argc, argv); + ret = conf_ctx_new(&ctx, options, sizeof(options) / sizeof(*options), + conf); if (ret) return ret; - if (KMSCON_CONF_BOOL(exit)) + *out = ctx; + return 0; +} + +void kmscon_conf_free(struct conf_ctx *ctx) +{ + conf_ctx_free(ctx); +} + +int kmscon_conf_load_main(struct conf_ctx *ctx, int argc, char **argv) +{ + int ret; + + ret = conf_ctx_parse_argv(ctx, argc, argv); + if (ret) + return ret; + + if (kmscon_conf.exit) return 0; - if (!KMSCON_CONF_BOOL(debug) && !KMSCON_CONF_BOOL(verbose) && - KMSCON_CONF_BOOL(silent)) + if (!kmscon_conf.debug && !kmscon_conf.verbose && kmscon_conf.silent) log_set_config(&LOG_CONFIG_WARNING(0, 0, 0, 0)); else - log_set_config(&LOG_CONFIG_INFO(KMSCON_CONF_BOOL(debug), - KMSCON_CONF_BOOL(verbose))); + log_set_config(&LOG_CONFIG_INFO(kmscon_conf.debug, + kmscon_conf.verbose)); log_print_init("kmscon"); - ret = conf_parse_file_f(kmscon_opt, kmscon_onum, - "/etc/kmscon/kmscon.conf"); + ret = conf_ctx_parse_file(ctx, "/etc/kmscon/kmscon.conf"); if (ret) return ret; /* TODO: Deprecated! Remove this! */ if (!access("/etc/kmscon.conf", F_OK)) { log_error("/etc/kmscon.conf is deprecated, please use /etc/kmscon/kmscon.conf"); - ret = conf_parse_file_f(kmscon_opt, kmscon_onum, - "/etc/kmscon.conf"); + ret = conf_ctx_parse_file(ctx, "/etc/kmscon.conf"); if (ret) return ret; } @@ -421,12 +419,23 @@ int kmscon_load_config(int argc, char **argv) return 0; } -void kmscon_free_config(void) +int kmscon_conf_load_seat(struct conf_ctx *ctx, const struct conf_ctx *main, + const char *seat) { - if (!kmscon_opt || !kmscon_onum) - return; + int ret; - kmscon_conf_free(kmscon_opt, kmscon_onum); - kmscon_opt = NULL; - kmscon_onum = 0; + if (!ctx || !main || !seat) + return -EINVAL; + + log_debug("parsing seat configuration for seat %s", seat); + + ret = conf_ctx_parse_ctx(ctx, main); + if (ret) + return ret; + + ret = conf_ctx_parse_file(ctx, "/etc/kmscon/%s.seat.conf", seat); + if (ret) + return ret; + + return 0; } diff --git a/src/kmscon_conf.h b/src/kmscon_conf.h index 1d73a99..ba0a68e 100644 --- a/src/kmscon_conf.h +++ b/src/kmscon_conf.h @@ -131,32 +131,10 @@ struct kmscon_conf_t { extern struct kmscon_conf_t kmscon_conf; -void kmscon_conf_init(struct kmscon_conf_t *conf); -int kmscon_conf_new(struct conf_option **out, size_t *size_out, - struct kmscon_conf_t *conf); -void kmscon_conf_free(struct conf_option *opt, size_t onum); - -int kmscon_conf_parse_argv(struct conf_option *opt, size_t onum, - int argc, char **argv); - -int kmscon_load_config(int argc, char **argv); -void kmscon_free_config(void); - -#define KMSCON_CONF_FROM_FIELD(_ptr, _field) \ - shl_offsetof(_ptr, struct kmscon_conf_t, _field) -#define KMSCON_CONF_OFFSET(_name) \ - offsetof(struct kmscon_conf_t, _name) -#define KMSCON_CONF(_name, _type) \ - (*((_type*)(((char*)&kmscon_conf) + KMSCON_CONF_OFFSET(_name)))) -#define KMSCON_CONF_BOOL(_name) \ - KMSCON_CONF(_name, bool) -#define KMSCON_CONF_UINT(_name) \ - KMSCON_CONF(_name, unsigned int) -#define KMSCON_CONF_STRING(_name) \ - KMSCON_CONF(_name, char*) -#define KMSCON_CONF_GRAB(_name) \ - KMSCON_CONF(_name, struct conf_grab*) -#define KMSCON_CONF_STRINGLIST(_name) \ - KMSCON_CONF(_name, char**) +int kmscon_conf_new(struct conf_ctx **out, struct kmscon_conf_t *conf); +void kmscon_conf_free(struct conf_ctx *ctx); +int kmscon_conf_load_main(struct conf_ctx *ctx, int argc, char **argv); +int kmscon_conf_load_seat(struct conf_ctx *ctx, const struct conf_ctx *main, + const char *seat); #endif /* KMSCON_MAIN_H */ diff --git a/src/kmscon_main.c b/src/kmscon_main.c index 485e2a4..fe74465 100644 --- a/src/kmscon_main.c +++ b/src/kmscon_main.c @@ -56,10 +56,14 @@ struct app_seat { bool awake; char *name; struct kmscon_seat *seat; + struct conf_ctx *conf_ctx; + struct kmscon_conf_t *conf; struct shl_dlist videos; }; struct kmscon_app { + struct conf_ctx *conf; + struct ev_eloop *eloop; struct ev_eloop *vt_eloop; unsigned int vt_exit_count; @@ -113,11 +117,11 @@ static int app_seat_new(struct kmscon_app *app, struct app_seat **out, bool found; found = false; - if (KMSCON_CONF_BOOL(all_seats)) { + if (kmscon_conf.all_seats) { found = true; } else { - for (i = 0; KMSCON_CONF_STRINGLIST(seats)[i]; ++i) { - if (!strcmp(KMSCON_CONF_STRINGLIST(seats)[i], sname)) { + for (i = 0; kmscon_conf.seats[i]; ++i) { + if (!strcmp(kmscon_conf.seats[i], sname)) { found = true; break; } @@ -148,13 +152,15 @@ static int app_seat_new(struct kmscon_app *app, struct app_seat **out, goto err_free; } - ret = kmscon_seat_new(&seat->seat, app->eloop, app->vtm, sname, - app_seat_event, seat); + ret = kmscon_seat_new(&seat->seat, app->conf, app->eloop, app->vtm, + sname, app_seat_event, seat); if (ret) { log_error("cannot create seat object on seat %s: %d", sname, ret); goto err_name; } + seat->conf_ctx = kmscon_seat_get_conf(seat->seat); + seat->conf = conf_ctx_get_mem(seat->conf_ctx); shl_dlist_link(&app->seats, &seat->list); *out = seat; @@ -202,7 +208,7 @@ static int app_seat_add_video(struct app_seat *seat, unsigned int mode; struct app_video *vid; - if (KMSCON_CONF_BOOL(fbdev)) { + if (seat->conf->fbdev) { if (type != UTERM_MONITOR_FBDEV && type != UTERM_MONITOR_FBDEV_DRM) { log_info("ignoring video device %s on seat %s as it is not an fbdev device", @@ -237,7 +243,7 @@ static int app_seat_add_video(struct app_seat *seat, } if (type == UTERM_MONITOR_DRM) { - if (KMSCON_CONF_BOOL(dumb)) + if (seat->conf->dumb) mode = UTERM_VIDEO_DUMB; else mode = UTERM_VIDEO_DRM; @@ -398,10 +404,11 @@ static void destroy_app(struct kmscon_app *app) ev_eloop_unref(app->eloop); } -static int setup_app(struct kmscon_app *app) +static int setup_app(struct kmscon_app *app, struct conf_ctx *conf) { int ret; + app->conf = conf; shl_dlist_init(&app->seats); ret = ev_eloop_new(&app->eloop, log_llog); @@ -455,16 +462,23 @@ err_app: int main(int argc, char **argv) { int ret; + struct conf_ctx *conf; struct kmscon_app app; - ret = kmscon_load_config(argc, argv); + ret = kmscon_conf_new(&conf, &kmscon_conf); if (ret) { - log_error("cannot parse configuration: %d", ret); + log_error("cannot create configuration: %d", ret); goto err_out; } - if (KMSCON_CONF_BOOL(exit)) { - kmscon_free_config(); + ret = kmscon_conf_load_main(conf, argc, argv); + if (ret) { + log_error("cannot load configuration: %d", ret); + goto err_conf; + } + + if (kmscon_conf.exit) { + kmscon_conf_free(conf); return 0; } @@ -472,18 +486,18 @@ int main(int argc, char **argv) kmscon_text_load_all(); memset(&app, 0, sizeof(app)); - ret = setup_app(&app); + ret = setup_app(&app, conf); if (ret) goto err_unload; - if (KMSCON_CONF_BOOL(switchvt)) { + if (kmscon_conf.switchvt) { log_debug("activating VTs during startup"); uterm_vt_master_activate_all(app.vtm); } ev_eloop_run(app.eloop, -1); - if (KMSCON_CONF_BOOL(switchvt)) { + if (kmscon_conf.switchvt) { /* The VT subsystem needs to acknowledge the VT-leave so if it * returns -EINPROGRESS we need to wait for the VT-leave SIGUSR2 * signal to arrive. Therefore, we use a separate eloop object @@ -509,9 +523,9 @@ int main(int argc, char **argv) err_unload: kmscon_text_unload_all(); kmscon_font_unload_all(); +err_conf: + kmscon_conf_free(conf); err_out: - kmscon_free_config(); - if (ret) log_err("cannot initialize kmscon, errno %d: %s", ret, strerror(-ret)); diff --git a/src/kmscon_seat.c b/src/kmscon_seat.c index 2178226..dee8083 100644 --- a/src/kmscon_seat.c +++ b/src/kmscon_seat.c @@ -67,6 +67,9 @@ struct kmscon_seat { struct ev_eloop *eloop; struct uterm_vt_master *vtm; + struct kmscon_conf_t conf; + struct conf_ctx *conf_ctx; + char *name; bool awake; struct uterm_input *input; @@ -355,6 +358,7 @@ static void seat_input_event(struct uterm_input *input, } int kmscon_seat_new(struct kmscon_seat **out, + struct conf_ctx *main_conf, struct ev_eloop *eloop, struct uterm_vt_master *vtm, const char *seatname, @@ -386,6 +390,19 @@ int kmscon_seat_new(struct kmscon_seat **out, goto err_free; } + ret = kmscon_conf_new(&seat->conf_ctx, &seat->conf); + if (ret) { + log_error("cannot create seat configuration object: %d", ret); + goto err_name; + } + + ret = kmscon_conf_load_seat(seat->conf_ctx, main_conf, seat->name); + if (ret) { + log_error("cannot parse seat configuration on seat %s: %d", + seat->name, ret); + goto err_conf; + } + ret = uterm_input_new(&seat->input, seat->eloop, kmscon_conf.xkb_layout, kmscon_conf.xkb_variant, @@ -393,7 +410,7 @@ int kmscon_seat_new(struct kmscon_seat **out, kmscon_conf.xkb_repeat_delay, kmscon_conf.xkb_repeat_rate); if (ret) - goto err_name; + goto err_conf; ret = uterm_input_register_cb(seat->input, seat_input_event, seat); if (ret) @@ -445,6 +462,8 @@ err_input_cb: uterm_input_unregister_cb(seat->input, seat_input_event, seat); err_input: uterm_input_unref(seat->input); +err_conf: + kmscon_conf_free(seat->conf_ctx); err_name: free(seat->name); err_free: @@ -477,6 +496,7 @@ void kmscon_seat_free(struct kmscon_seat *seat) uterm_vt_deallocate(seat->vt); uterm_input_unregister_cb(seat->input, seat_input_event, seat); uterm_input_unref(seat->input); + kmscon_conf_free(seat->conf_ctx); free(seat->name); uterm_vt_master_unref(seat->vtm); ev_eloop_unref(seat->eloop); @@ -549,6 +569,14 @@ struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat) return seat->eloop; } +struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat) +{ + if (!seat) + return NULL; + + return seat->conf_ctx; +} + int kmscon_seat_register_session(struct kmscon_seat *seat, struct kmscon_session **out, kmscon_session_cb_t cb, diff --git a/src/kmscon_seat.h b/src/kmscon_seat.h index 0fff870..069dec5 100644 --- a/src/kmscon_seat.h +++ b/src/kmscon_seat.h @@ -34,6 +34,7 @@ #include #include +#include "conf.h" #include "eloop.h" #include "uterm.h" @@ -63,6 +64,7 @@ typedef void (*kmscon_session_cb_t) (struct kmscon_session *session, void *data); int kmscon_seat_new(struct kmscon_seat **out, + struct conf_ctx *main_conf, struct ev_eloop *eloop, struct uterm_vt_master *vtm, const char *seatname, @@ -80,6 +82,7 @@ void kmscon_seat_remove_input(struct kmscon_seat *seat, const char *node); const char *kmscon_seat_get_name(struct kmscon_seat *seat); struct uterm_input *kmscon_seat_get_input(struct kmscon_seat *seat); struct ev_eloop *kmscon_seat_get_eloop(struct kmscon_seat *seat); +struct conf_ctx *kmscon_seat_get_conf(struct kmscon_seat *seat); int kmscon_seat_register_session(struct kmscon_seat *seat, struct kmscon_session **out, diff --git a/src/pty.c b/src/pty.c index b30cda0..61d6b6c 100644 --- a/src/pty.c +++ b/src/pty.c @@ -39,6 +39,7 @@ #include "eloop.h" #include "log.h" #include "pty.h" +#include "shl_misc.h" #include "shl_ring.h" #define LOG_SUBSYSTEM "pty" @@ -141,35 +142,18 @@ int kmscon_pty_set_term(struct kmscon_pty *pty, const char *term) int kmscon_pty_set_argv(struct kmscon_pty *pty, char **argv) { - char **t, *off; - unsigned int size, i; + char **t; + int ret; if (!pty || !argv || !*argv || !**argv) return -EINVAL; - size = 0; - for (i = 0; argv[i]; ++i) - size += strlen(argv[i]) + 1; - ++i; + ret = shl_dup_array(&t, argv); + if (ret) + return ret; - size += i * sizeof(char*); - - t = malloc(size); - if (!t) - return -ENOMEM; free(pty->argv); pty->argv = t; - - off = (char*)t + i * sizeof(char*); - while (*argv) { - *t++ = off; - for (i = 0; argv[0][i]; ++i) - *off++ = argv[0][i]; - *off++ = 0; - argv++; - } - *t = NULL; - return 0; } diff --git a/src/shl_misc.h b/src/shl_misc.h index 803e083..271e238 100644 --- a/src/shl_misc.h +++ b/src/shl_misc.h @@ -140,6 +140,39 @@ static inline int shl_split_string(const char *arg, char ***out, return 0; } +static inline int shl_dup_array(char ***out, char **argv) +{ + char **t, *off; + unsigned int size, i; + + if (!out || !argv) + return -EINVAL; + + size = 0; + for (i = 0; argv[i]; ++i) + size += strlen(argv[i]) + 1; + ++i; + + size += i * sizeof(char*); + + t = malloc(size); + if (!t) + return -ENOMEM; + *out = t; + + off = (char*)t + i * sizeof(char*); + while (*argv) { + *t++ = off; + for (i = 0; argv[0][i]; ++i) + *off++ = argv[0][i]; + *off++ = 0; + argv++; + } + *t = NULL; + + return 0; +} + /* TODO: xkbcommon should provide these flags! * We currently copy them into each library API we use so we need to keep * them in sync. Currently, they're used in uterm-input and tsm-vte. */