4 * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files
8 * (the "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 * Implementation of the configuration parsers.
43 #define LOG_SUBSYSTEM "config"
45 struct conf_obj conf_global;
46 static char *def_argv[] = { NULL, "-i", NULL };
48 static void print_help()
51 * Usage/Help information
52 * This should be scaled to a maximum of 80 characters per line:
55 * | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 |
56 * "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
57 * 80 char line starting with tab:
58 * |10| 20 | 30 | 40 | 50 | 60 | 70 | 80 |
59 * "\t901234567890123456789012345678901234567890123456789012345678901234567890\n"
64 "\t%1$s -h [options]\n"
65 "\t%1$s -l [options] -- /bin/sh [sh-arguments]\n"
67 "You can prefix boolean options with \"no-\" to negate it. If an argument is\n"
68 "given multiple times, only the last argument matters if not otherwise stated.\n"
71 "\t-h, --help [off] Print this help and exit\n"
72 "\t-v, --verbose [off] Print verbose messages\n"
73 "\t --debug [off] Enable debug mode\n"
74 "\t --silent [off] Suppress notices and warnings\n"
75 "\t-s, --switchvt [off] Automatically switch to VT\n"
76 "\t --seat <seat-name> [seat0] Select seat; default: seat0\n"
79 "\t-l, --login [/bin/sh]\n"
80 "\t Start the given login process instead\n"
81 "\t of the default process; all arguments\n"
82 "\t following '--' will be be parsed as\n"
83 "\t argv to this process. No more options\n"
84 "\t after '--' will be parsed so use it at\n"
85 "\t the end of the argument string\n"
86 "\t-t, --term <TERM> [vt220]\n"
87 "\t Value of the TERM environment variable\n"
88 "\t for the child process\n"
91 "\t --fbdev [off] Use fbdev instead of DRM\n"
93 "Input Device Options:\n"
94 "\t --xkb-layout <layout> [us] Set XkbLayout for input devices\n"
95 "\t --xkb-variant <variant> [-] Set XkbVariant for input devices\n"
96 "\t --xkb-options <options> [-] Set XkbOptions for input devices\n"
99 "\t --font-engine <engine> [pango] Font engine\n",
103 * | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 |
104 * "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n"
105 * 80 char line starting with tab:
106 * |10| 20 | 30 | 40 | 50 | 60 | 70 | 80 |
107 * "\t901234567890123456789012345678901234567890123456789012345678901234567890\n"
111 void conf_free_value(struct conf_option *opt)
113 if (*(void**)opt->mem != opt->def)
114 free(*(void**)opt->mem);
117 int conf_parse_bool(struct conf_option *opt, bool on, const char *arg)
119 *(bool*)opt->mem = on;
123 void conf_default_bool(struct conf_option *opt)
125 *(bool*)opt->mem = (bool)opt->def;
128 int conf_parse_string(struct conf_option *opt, bool on, const char *arg)
130 char *val = strdup(arg);
134 opt->type->free(opt);
135 *(void**)opt->mem = val;
139 void conf_default_string(struct conf_option *opt)
141 *(void**)opt->mem = opt->def;
144 const struct conf_type conf_bool = {
146 .parse = conf_parse_bool,
148 .set_default = conf_default_bool,
151 const struct conf_type conf_string = {
152 .flags = CONF_HAS_ARG,
153 .parse = conf_parse_string,
154 .free = conf_free_value,
155 .set_default = conf_default_string,
158 static int aftercheck_debug(struct conf_option *opt, int argc, char **argv,
161 /* --debug implies --verbose */
162 if (conf_global.debug)
163 conf_global.verbose = 1;
168 static int aftercheck_help(struct conf_option *opt, int argc, char **argv,
171 /* exit after printing --help information */
172 if (conf_global.help) {
174 conf_global.exit = true;
180 static int aftercheck_login(struct conf_option *opt, int argc, char **argv,
185 /* parse "--login [...] -- args" arguments */
186 if (conf_global.login) {
188 fprintf(stderr, "Arguments for --login missing\n");
192 conf_global.argv = &argv[idx];
195 def_argv[0] = getenv("SHELL") ? : _PATH_BSHELL;
196 conf_global.argv = def_argv;
203 struct conf_option options[] = {
204 CONF_OPTION_BOOL('h', "help", aftercheck_help, &conf_global.help, false),
205 CONF_OPTION_BOOL('v', "verbose", NULL, &conf_global.verbose, false),
206 CONF_OPTION_BOOL(0, "debug", aftercheck_debug, &conf_global.debug, false),
207 CONF_OPTION_BOOL(0, "silent", NULL, &conf_global.silent, false),
208 CONF_OPTION_BOOL(0, "fbdev", NULL, &conf_global.use_fbdev, false),
209 CONF_OPTION_BOOL('s', "switchvt", NULL, &conf_global.switchvt, false),
210 CONF_OPTION_BOOL('l', "login", aftercheck_login, &conf_global.login, false),
211 CONF_OPTION_STRING('t', "term", NULL, &conf_global.term, "vt220"),
212 CONF_OPTION_STRING(0, "xkb-layout", NULL, &conf_global.xkb_layout, "us"),
213 CONF_OPTION_STRING(0, "xkb-variant", NULL, &conf_global.xkb_variant, ""),
214 CONF_OPTION_STRING(0, "xkb-options", NULL, &conf_global.xkb_options, ""),
215 CONF_OPTION_STRING(0, "seat", NULL, &conf_global.seat, "seat0"),
216 CONF_OPTION_STRING(0, "font-engine", NULL, &conf_global.font_engine, "pango"),
219 /* free all memory that we allocated and reset to initial state */
224 num = sizeof(options) / sizeof(*options);
225 for (i = 0; i < num; ++i) {
226 if (options[i].type->free)
227 options[i].type->free(&options[i]);
230 memset(&conf_global, 0, sizeof(conf_global));
234 * Parse command line arguments
235 * This temporarily allocates the short_options and long_options arrays so we
236 * can use the getopt_long() library call. It locks all arguments after they
237 * have been set so command-line options will always overwrite config-options.
239 int conf_parse_argv(int argc, char **argv)
242 struct option *long_options;
247 if (!argv || argc < 1)
250 len = sizeof(options) / sizeof(*options);
252 short_options = malloc(sizeof(char) * (len + 1) * 2);
253 if (!short_options) {
254 log_error("cannot allocate enough memory to parse command line arguments (%d): %m");
258 long_options = malloc(sizeof(struct option) * len * 2);
260 log_error("cannot allocate enough memory to parse command line arguments (%d): %m");
266 short_options[pos++] = ':';
268 for (i = 0; i < len; ++i) {
269 if (options[i].short_name) {
270 short_options[pos++] = options[i].short_name;
271 if (options[i].type->flags & CONF_HAS_ARG)
272 short_options[pos++] = ':';
275 if (options[i].long_name) {
276 /* skip the "no-" prefix */
277 opt->name = &options[i].long_name[3];
278 opt->has_arg = !!(options[i].type->flags & CONF_HAS_ARG);
280 opt->val = 100000 + i;
283 /* boolean args are also added with "no-" prefix */
284 if (!(options[i].type->flags & CONF_HAS_ARG)) {
285 opt->name = options[i].long_name;
288 opt->val = 200000 + i;
293 short_options[pos++] = 0;
297 c = getopt_long(argc, argv, short_options,
301 } else if (c == ':') {
302 fprintf(stderr, "Missing argument for: %s\n",
305 } else if (c == '?') {
306 if (optopt && optopt < 100000)
307 fprintf(stderr, "Unknown argument: -%c\n",
310 fprintf(stderr, "Unknown argument: %s\n",
313 fprintf(stderr, "Parameter takes no argument: %s\n",
316 } else if (c < 100000) {
317 for (i = 0; i < len; ++i) {
318 if (options[i].short_name == c) {
319 ret = options[i].type->parse(&options[i],
324 options[i].flags |= CONF_LOCKED;
325 options[i].flags |= CONF_DONE;
329 } else if (c < 200000) {
331 ret = options[i].type->parse(&options[i], true, optarg);
334 options[i].flags |= CONF_LOCKED;
335 options[i].flags |= CONF_DONE;
338 ret = options[i].type->parse(&options[i], false, NULL);
341 options[i].flags |= CONF_LOCKED;
342 options[i].flags |= CONF_DONE;
349 /* set default values if not configured */
350 for (i = 0; i < len; ++i) {
351 if (!(options[i].flags & CONF_DONE) &&
352 options[i].type->set_default) {
353 options[i].type->set_default(&options[i]);
357 /* Perform aftercheck:
358 * All arguments that provide an aftercheck will be passed the remaining
359 * arguments in order. If they return a negative error code, it is
360 * interpreted as fatal error and returned to the caller. A positive
361 * error code is interpreted as the amount of remaining arguments that
362 * have been consumed by this aftercheck. 0 means nothing has been
364 * The next argument's aftercheck will only get the now remaning
365 * arguments passed in. If not all arguments are consumed, then this
366 * function will report an error to the caller. */
367 for (i = 0; i < len; ++i) {
368 if (options[i].aftercheck) {
369 ret = options[i].aftercheck(&options[i], argc, argv, optind);
377 fprintf(stderr, "Unparsed remaining arguments starting with: %s\n",
385 static int parse_kv_pair(const char *key, const char *value)
390 struct conf_option *opt;
392 num = sizeof(options) / sizeof(*options);
393 for (i = 0; i < num; ++i) {
398 if (!strcmp(key, opt->long_name))
400 else if (!strcmp(key, &opt->long_name[3]))
405 if (opt->type->flags & CONF_HAS_ARG && !value) {
406 log_error("config option '%s' requires an argument", key);
408 } else if (!(opt->type->flags & CONF_HAS_ARG) && value) {
409 log_error("config option '%s' does not take arguments", key);
413 /* ignore if already set by command-line arguments */
414 if (opt->flags & CONF_LOCKED)
417 ret = opt->type->parse(opt, set, value);
421 opt->flags |= CONF_DONE;
425 log_error("unknown config option '%s'", key);
429 static void strip_spaces(char **buf)
433 while (**buf == ' ' || **buf == '\r' || **buf == '\t')
445 while (*tail == ' ' || *tail == '\r' || *tail == '\t')
449 static int parse_line(char **buf, size_t *size)
476 } else if (*line == '\n') {
479 } else if (*line == '#') {
482 } else if (*line != '=') {
503 } else if (*line == '\n') {
506 } else if (*line == '#') {
528 strip_spaces(&value);
530 ret = parse_kv_pair(key, value);
546 static int parse_buffer(char *buf, size_t size)
550 while (!ret && size > 0)
551 ret = parse_line(&buf, &size);
556 /* chunk size when reading config files */
557 #define CONF_BUFSIZE 4096
559 /* This reads the file at \path in memory and parses it as if it was given as
560 * command line options. */
561 int conf_parse_file(const char *path)
570 log_info("reading config file %s", path);
571 fd = open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
573 log_error("cannot open %s (%d): %m", path, errno);
582 if (size - pos < CONF_BUFSIZE) {
583 tmp = realloc(buf, size + CONF_BUFSIZE + 1);
585 log_error("cannot allocate enough memory to parse config file %s (%d): %m",
591 size += CONF_BUFSIZE;
594 ret = read(fd, &buf[pos], CONF_BUFSIZE);
596 log_error("cannot read from config file %s (%d): %m",
605 ret = parse_buffer(buf, pos);
613 int conf_parse_all_files(void)
616 const char *file, *home;
621 file = "/etc/kmscon.conf";
622 if (!access(file, F_OK)) {
623 if (access(file, R_OK))
624 log_warning("config file %s exists but read access was denied",
627 ret = conf_parse_file(file);
633 home = getenv("HOME");
635 ret = asprintf(&path, "%s/.kmscon.conf", home);
637 log_warning("cannot allocate enough resources to build a config-path");
641 if (!access(path, F_OK)) {
642 if (access(path, R_OK))
643 log_warning("config file %s exists but read access was denied",
646 ret = conf_parse_file(path);