conf: introduce conf_parse_file_f()
[platform/upstream/kmscon.git] / src / conf.c
1 /*
2  * App Configuration
3  *
4  * Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included
15  * in all copies or substantial portions of the Software.
16  *
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.
24  */
25
26 /*
27  * Configuration
28  * Implementation of the configuration parsers.
29  */
30
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <getopt.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <xkbcommon/xkbcommon.h>
41 #include "conf.h"
42 #include "log.h"
43 #include "shl_misc.h"
44
45 #define LOG_SUBSYSTEM "config"
46
47 void conf_free_value(struct conf_option *opt)
48 {
49         if (*(void**)opt->mem && *(void**)opt->mem != opt->def) {
50                 free(*(void**)opt->mem);
51                 *(void**)opt->mem = NULL;
52         }
53 }
54
55 int conf_parse_bool(struct conf_option *opt, bool on, const char *arg)
56 {
57         *(bool*)opt->mem = on;
58         return 0;
59 }
60
61 void conf_default_bool(struct conf_option *opt)
62 {
63         *(bool*)opt->mem = (bool)opt->def;
64 }
65
66 int conf_parse_int(struct conf_option *opt, bool on, const char *arg)
67 {
68         *(int*)opt->mem = atoi(arg);
69         return 0;
70 }
71
72 void conf_default_int(struct conf_option *opt)
73 {
74         *(int*)opt->mem = (int)(unsigned long)opt->def;
75 }
76
77 int conf_parse_uint(struct conf_option *opt, bool on, const char *arg)
78 {
79         *(unsigned int*)opt->mem = atoi(arg);
80         return 0;
81 }
82
83 void conf_default_uint(struct conf_option *opt)
84 {
85         *(unsigned int*)opt->mem = (unsigned int)(unsigned long)opt->def;
86 }
87
88 int conf_parse_string(struct conf_option *opt, bool on, const char *arg)
89 {
90         char *val = strdup(arg);
91         if (!val)
92                 return -ENOMEM;
93
94         opt->type->free(opt);
95         *(void**)opt->mem = val;
96         return 0;
97 }
98
99 void conf_default_string(struct conf_option *opt)
100 {
101         *(void**)opt->mem = opt->def;
102 }
103
104 int conf_parse_string_list(struct conf_option *opt, bool on, const char *arg)
105 {
106         int ret;
107         char **list;
108
109         ret = shl_split_string(arg, &list, NULL, ',', true);
110         if (ret)
111                 return ret;
112
113         opt->type->free(opt);
114         *(void**)opt->mem = list;
115         return 0;
116 }
117
118 void conf_default_string_list(struct conf_option *opt)
119 {
120         *(void**)opt->mem = opt->def;
121 }
122
123 static int parse_single_grab(char *arg, unsigned int *mods,
124                              uint32_t *keysym, bool allow_mods)
125 {
126         char *tmp, *start, *end;
127
128         tmp = arg;
129         do {
130                 while (*tmp == ' ')
131                         ++tmp;
132                 if (!allow_mods)
133                         break;
134                 if (*tmp != '<')
135                         break;
136
137                 start = tmp;
138                 while (*tmp && *tmp != '>')
139                         ++tmp;
140
141                 if (*tmp != '>') {
142                         log_error("missing '>' near '%s'", start);
143                         return -EFAULT;
144                 }
145
146                 *tmp++ = 0;
147                 ++start;
148                 if (!strcasecmp(start, "shift")) {
149                         *mods |= SHL_SHIFT_MASK;
150                 } else if (!strcasecmp(start, "lock")) {
151                         *mods |= SHL_LOCK_MASK;
152                 } else if (!strcasecmp(start, "control") ||
153                            !strcasecmp(start, "ctrl")) {
154                         *mods |= SHL_CONTROL_MASK;
155                 } else if (!strcasecmp(start, "alt")) {
156                         *mods |= SHL_ALT_MASK;
157                 } else if (!strcasecmp(start, "logo")) {
158                         *mods |= SHL_LOGO_MASK;
159                 } else {
160                         log_error("invalid modifier '%s'", start);
161                         return -EFAULT;
162                 }
163         } while (1);
164
165         while (*tmp == ' ')
166                 ++tmp;
167
168         start = tmp;
169         end = start;
170         do {
171                 while (*tmp && *tmp != ' ')
172                         ++tmp;
173                 end = tmp;
174                 if (!*tmp)
175                         break;
176                 while (*tmp == ' ')
177                         ++tmp;
178         } while (1);
179
180         if (start == end)
181                 return 0;
182         if (*end)
183                 *end = 0;
184
185         *keysym = xkb_keysym_from_name(start);
186         if (!*keysym) {
187                 log_error("invalid key '%s'", start);
188                 return -EFAULT;
189         }
190
191         return 1;
192 }
193
194 int conf_parse_grab(struct conf_option *opt, bool on, const char *arg)
195 {
196         char **list, **keys;
197         unsigned int list_num, key_num, i, j, k, l;
198         int ret;
199         struct conf_grab *grab;
200
201         ret = shl_split_string(arg, &list, &list_num, ',', false);
202         if (ret)
203                 return ret;
204
205         grab = malloc(sizeof(*grab));
206         if (!grab) {
207                 ret = -ENOMEM;
208                 goto err_list;
209         }
210         memset(grab, 0, sizeof(*grab));
211
212         if (list_num) {
213                 grab->mods = malloc(sizeof(*grab->mods) * list_num);
214                 if (!grab->mods) {
215                         ret = -ENOMEM;
216                         goto err_grab;
217                 }
218                 memset(grab->mods, 0, sizeof(*grab->mods) * list_num);
219
220                 grab->num_syms = malloc(sizeof(*grab->num_syms) * list_num);
221                 if (!grab->num_syms) {
222                         ret = -ENOMEM;
223                         goto err_grab;
224                 }
225                 memset(grab->num_syms, 0, sizeof(*grab->num_syms) * list_num);
226
227                 grab->keysyms = malloc(sizeof(*grab->keysyms) * list_num);
228                 if (!grab->keysyms) {
229                         ret = -ENOMEM;
230                         goto err_grab;
231                 }
232                 memset(grab->keysyms, 0, sizeof(*grab->keysyms) * list_num);
233         }
234
235         l = 0;
236         for (i = 0; i < list_num; ++i) {
237                 ret = shl_split_string(list[i], &keys, &key_num, '+', false);
238                 if (ret)
239                         goto err_all;
240                 if (!key_num) {
241                         free(keys);
242                         continue;
243                 }
244
245                 grab->keysyms[l] = malloc(sizeof(*grab->keysyms[l] * key_num));
246                 if (!grab->keysyms[l]) {
247                         ret = -ENOMEM;
248                         free(keys);
249                         goto err_all;
250                 }
251
252                 k = 0;
253                 for (j = 0; j < key_num; ++j) {
254                         ret = parse_single_grab(keys[j], &grab->mods[l],
255                                                 &grab->keysyms[l][k],
256                                                 j == 0);
257                         if (ret < 0) {
258                                 log_error("cannot parse grab '%s' in '%s'",
259                                           list[i], arg);
260                                 free(keys);
261                                 goto err_all;
262                         }
263                         k += ret;
264                 }
265
266                 free(keys);
267                 if (!k)
268                         continue;
269                 grab->num_syms[l] = k;
270                 ++l;
271                 ++grab->num;
272         }
273
274         free(list);
275         opt->type->free(opt);
276         *(void**)opt->mem = grab;
277         return 0;
278
279 err_all:
280         for (i = 0; i < list_num; ++i)
281                 free(grab->keysyms[i]);
282 err_grab:
283         free(grab->keysyms);
284         free(grab->num_syms);
285         free(grab->mods);
286         free(grab);
287 err_list:
288         free(list);
289         return ret;
290 }
291
292 void conf_free_grab(struct conf_option *opt)
293 {
294         struct conf_grab *grab;
295         unsigned int i;
296
297         if (!*(void**)opt->mem || *(void**)opt->mem == opt->def)
298                 return;
299
300         grab = *(void**)opt->mem;
301         *(void**)opt->mem = NULL;
302
303         for (i = 0; i < grab->num; ++i)
304                 free(grab->keysyms[i]);
305
306         free(grab->keysyms);
307         free(grab->num_syms);
308         free(grab->mods);
309         free(grab);
310 }
311
312 void conf_default_grab(struct conf_option *opt)
313 {
314         *(void**)opt->mem = opt->def;
315 }
316
317 const struct conf_type conf_bool = {
318         .flags = 0,
319         .parse = conf_parse_bool,
320         .free = NULL,
321         .set_default = conf_default_bool,
322 };
323
324 const struct conf_type conf_int = {
325         .flags = CONF_HAS_ARG,
326         .parse = conf_parse_int,
327         .free = NULL,
328         .set_default = conf_default_int,
329 };
330
331 const struct conf_type conf_uint = {
332         .flags = CONF_HAS_ARG,
333         .parse = conf_parse_uint,
334         .free = NULL,
335         .set_default = conf_default_uint,
336 };
337
338 const struct conf_type conf_string = {
339         .flags = CONF_HAS_ARG,
340         .parse = conf_parse_string,
341         .free = conf_free_value,
342         .set_default = conf_default_string,
343 };
344
345 const struct conf_type conf_string_list = {
346         .flags = CONF_HAS_ARG,
347         .parse = conf_parse_string_list,
348         .free = conf_free_value,
349         .set_default = conf_default_string_list,
350 };
351
352 const struct conf_type conf_grab = {
353         .flags = CONF_HAS_ARG,
354         .parse = conf_parse_grab,
355         .free = conf_free_grab,
356         .set_default = conf_default_grab,
357 };
358
359 /* free all memory that we allocated and reset to initial state */
360 void conf_free(struct conf_option *opts, size_t len)
361 {
362         unsigned int i;
363
364         for (i = 0; i < len; ++i) {
365                 if (opts[i].type->free)
366                         opts[i].type->free(&opts[i]);
367         }
368 }
369
370 /*
371  * Parse command line arguments
372  * This temporarily allocates the short_options and long_options arrays so we
373  * can use the getopt_long() library call. It locks all arguments after they
374  * have been set so command-line options will always overwrite config-options.
375  */
376 int conf_parse_argv(struct conf_option *opts, size_t len,
377                     int argc, char **argv)
378 {
379         char *short_options;
380         struct option *long_options;
381         struct option *opt;
382         size_t i, pos;
383         int c, ret;
384
385         if (!argv || argc < 1)
386                 return -EINVAL;
387
388         short_options = malloc(sizeof(char) * (len + 1) * 2);
389         if (!short_options) {
390                 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
391                 return -ENOMEM;
392         }
393
394         long_options = malloc(sizeof(struct option) * len * 2);
395         if (!long_options) {
396                 log_error("cannot allocate enough memory to parse command line arguments (%d): %m", errno);
397                 free(short_options);
398                 return -ENOMEM;
399         }
400
401         pos = 0;
402         short_options[pos++] = ':';
403         opt = long_options;
404         for (i = 0; i < len; ++i) {
405                 if (opts[i].short_name) {
406                         short_options[pos++] = opts[i].short_name;
407                         if (opts[i].type->flags & CONF_HAS_ARG)
408                                 short_options[pos++] = ':';
409                 }
410
411                 if (opts[i].long_name) {
412                         /* skip the "no-" prefix */
413                         opt->name = &opts[i].long_name[3];
414                         opt->has_arg = !!(opts[i].type->flags & CONF_HAS_ARG);
415                         opt->flag = NULL;
416                         opt->val = 100000 + i;
417                         ++opt;
418
419                         /* boolean args are also added with "no-" prefix */
420                         if (!(opts[i].type->flags & CONF_HAS_ARG)) {
421                                 opt->name = opts[i].long_name;
422                                 opt->has_arg = 0;
423                                 opt->flag = NULL;
424                                 opt->val = 200000 + i;
425                                 ++opt;
426                         }
427                 }
428         }
429         short_options[pos++] = 0;
430
431         opterr = 0;
432         while (1) {
433                 c = getopt_long(argc, argv, short_options,
434                                 long_options, NULL);
435                 if (c <= 0) {
436                         break;
437                 } else if (c == ':') {
438                         fprintf(stderr, "Missing argument for: %s\n",
439                                 argv[optind - 1]);
440                         return -EFAULT;
441                 } else if (c == '?') {
442                         if (optopt && optopt < 100000)
443                                 fprintf(stderr, "Unknown argument: -%c\n",
444                                         optopt);
445                         else if (!optopt)
446                                 fprintf(stderr, "Unknown argument: %s\n",
447                                         argv[optind - 1]);
448                         else
449                                 fprintf(stderr, "Parameter takes no argument: %s\n",
450                                         argv[optind - 1]);
451                         return -EFAULT;
452                 } else if (c < 100000) {
453                         for (i = 0; i < len; ++i) {
454                                 if (opts[i].short_name == c) {
455                                         ret = opts[i].type->parse(&opts[i],
456                                                                   true,
457                                                                   optarg);
458                                         if (ret)
459                                                 return ret;
460                                         opts[i].flags |= CONF_LOCKED;
461                                         opts[i].flags |= CONF_DONE;
462                                         break;
463                                 }
464                         }
465                 } else if (c < 200000) {
466                         i = c - 100000;
467                         ret = opts[i].type->parse(&opts[i], true, optarg);
468                         if (ret)
469                                 return ret;
470                         opts[i].flags |= CONF_LOCKED;
471                         opts[i].flags |= CONF_DONE;
472                 } else {
473                         i = c - 200000;
474                         ret = opts[i].type->parse(&opts[i], false, NULL);
475                         if (ret)
476                                 return ret;
477                         opts[i].flags |= CONF_LOCKED;
478                         opts[i].flags |= CONF_DONE;
479                 }
480         }
481
482         free(long_options);
483         free(short_options);
484
485         /* set default values if not configured */
486         for (i = 0; i < len; ++i) {
487                 if (!(opts[i].flags & CONF_DONE) &&
488                     opts[i].type->set_default) {
489                         opts[i].type->set_default(&opts[i]);
490                 }
491         }
492
493         /* Perform aftercheck:
494          * All arguments that provide an aftercheck will be passed the remaining
495          * arguments in order. If they return a negative error code, it is
496          * interpreted as fatal error and returned to the caller. A positive
497          * error code is interpreted as the amount of remaining arguments that
498          * have been consumed by this aftercheck. 0 means nothing has been
499          * consumed.
500          * The next argument's aftercheck will only get the now remaning
501          * arguments passed in. If not all arguments are consumed, then this
502          * function will report an error to the caller. */
503         for (i = 0; i < len; ++i) {
504                 if (opts[i].aftercheck) {
505                         ret = opts[i].aftercheck(&opts[i], argc, argv, optind);
506                         if (ret < 0)
507                                 return ret;
508                         optind += ret;
509                 }
510         }
511
512         if (optind < argc) {
513                 fprintf(stderr, "Unparsed remaining arguments starting with: %s\n",
514                         argv[optind]);
515                 return -EFAULT;
516         }
517
518         return 0;
519 }
520
521 static int parse_kv_pair(struct conf_option *opts, size_t len,
522                          const char *key, const char *value)
523 {
524         unsigned int i;
525         int ret;
526         bool set;
527         struct conf_option *opt;
528
529         for (i = 0; i < len; ++i) {
530                 opt = &opts[i];
531                 if (!opt->long_name)
532                         continue;
533
534                 if (!strcmp(key, opt->long_name))
535                         set = false;
536                 else if (!strcmp(key, &opt->long_name[3]))
537                         set = true;
538                 else
539                         continue;
540
541                 if (opt->type->flags & CONF_HAS_ARG && !value) {
542                         log_error("config option '%s' requires an argument", key);
543                         return -EFAULT;
544                 } else if (!(opt->type->flags & CONF_HAS_ARG) && value) {
545                         log_error("config option '%s' does not take arguments", key);
546                         return -EFAULT;
547                 }
548
549                 /* ignore if already set by command-line arguments */
550                 if (opt->flags & CONF_LOCKED)
551                         return 0;
552
553                 ret = opt->type->parse(opt, set, value);
554                 if (ret)
555                         return ret;
556
557                 opt->flags |= CONF_DONE;
558                 return 0;
559         }
560
561         log_error("unknown config option '%s'", key);
562         return -EFAULT;
563 }
564
565 static void strip_spaces(char **buf)
566 {
567         char *tail;
568
569         while (**buf == ' ' || **buf == '\r' || **buf == '\t')
570                 ++*buf;
571
572         if (!**buf)
573                 return;
574
575         tail = *buf;
576         while (*tail)
577                 ++tail;
578
579         --tail;
580
581         while (*tail == ' ' || *tail == '\r' || *tail == '\t')
582                 *tail-- = 0;
583 }
584
585 static int parse_line(struct conf_option *opts, size_t olen,
586                       char **buf, size_t *size)
587 {
588         char *key;
589         char *value = NULL;
590         char *line;
591         char c;
592         size_t len, klen;
593         int ret;
594
595         line = *buf;
596         len = *size;
597
598         /* parse key */
599         key = line;
600         while (len) {
601                 c = *line;
602                 if (c == '\n' ||
603                     c == '#' ||
604                     c == '=')
605                         break;
606                 ++line;
607                 --len;
608         }
609
610         if (!len) {
611                 *line = 0;
612                 goto done;
613         } else if (*line == '\n') {
614                 *line = 0;
615                 goto done;
616         } else if (*line == '#') {
617                 *line = 0;
618                 goto skip_comment;
619         } else if (*line != '=') {
620                 return -EFAULT;
621         }
622
623         *line++ = 0;
624         --len;
625
626         /* parse value */
627         value = line;
628         while (len) {
629                 c = *line;
630                 if (c == '\n' ||
631                     c == '#')
632                         break;
633                 ++line;
634                 --len;
635         }
636
637         if (!len) {
638                 *line = 0;
639                 goto done;
640         } else if (*line == '\n') {
641                 *line = 0;
642                 goto done;
643         } else if (*line == '#') {
644                 *line = 0;
645                 goto skip_comment;
646         } else {
647                 return -EFAULT;
648         }
649
650 skip_comment:
651         while (len) {
652                 c = *line;
653                 if (c == '\n')
654                         break;
655                 ++line;
656                 --len;
657         }
658
659 done:
660         strip_spaces(&key);
661         
662         klen = strlen(key);
663         if (klen > 0) {
664                 if (value)
665                         strip_spaces(&value);
666
667                 ret = parse_kv_pair(opts, olen, key, value);
668                 if (ret)
669                         return ret;
670         }
671
672         if (!len) {
673                 *buf = NULL;
674                 *size = 0;
675         } else {
676                 *buf = ++line;
677                 *size = --len;
678         }
679
680         return 0;
681 }
682
683 static int parse_buffer(struct conf_option *opts, size_t len,
684                         char *buf, size_t size)
685 {
686         int ret = 0;
687
688         while (!ret && size > 0)
689                 ret = parse_line(opts, len, &buf, &size);
690
691         return ret;
692 }
693
694 /* chunk size when reading config files */
695 #define CONF_BUFSIZE 4096
696
697 /* This reads the file at \path in memory and parses it as if it was given as
698  * command line options. */
699 int conf_parse_file(struct conf_option *opts, size_t len, const char *path)
700 {
701         int fd, ret;
702         size_t size, pos;
703         char *buf, *tmp;
704
705         if (!opts || !len || !path)
706                 return -EINVAL;
707
708         if (access(path, F_OK))
709                 return 0;
710
711         if (access(path, R_OK)) {
712                 log_error("read access to config file %s denied", path);
713                 return -EACCES;
714         }
715
716         log_info("reading config file %s", path);
717         fd = open(path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
718         if (fd < 0) {
719                 log_error("cannot open %s (%d): %m", path, errno);
720                 return -EFAULT;
721         }
722
723         buf = NULL;
724         size = 0;
725         pos = 0;
726
727         do {
728                 if (size - pos < CONF_BUFSIZE) {
729                         tmp = realloc(buf, size + CONF_BUFSIZE + 1);
730                         if (!tmp) {
731                                 log_error("cannot allocate enough memory to parse config file %s (%d): %m",
732                                           path, errno);
733                                 ret = -ENOMEM;
734                                 goto out_free;
735                         }
736                         buf = tmp;
737                         size += CONF_BUFSIZE;
738                 }
739
740                 ret = read(fd, &buf[pos], CONF_BUFSIZE);
741                 if (ret < 0) {
742                         log_error("cannot read from config file %s (%d): %m",
743                                   path, errno);
744                         ret = -EFAULT;
745                         goto out_free;
746                 }
747                 pos += ret;
748         } while (ret > 0);
749
750         buf[pos] = 0;
751         ret = parse_buffer(opts, len, buf, pos);
752
753 out_free:
754         free(buf);
755         close(fd);
756         return ret;
757 }
758
759 int conf_parse_file_f(struct conf_option *opts, size_t len,
760                       const char *format, ...)
761 {
762         va_list list;
763         char *path;
764         int ret;
765
766         if (!opts || !len || !format)
767                 return -EINVAL;
768
769         va_start(list, format);
770         ret = vasprintf(&path, format, list);
771         va_end(list);
772
773         if (ret < 0) {
774                 log_error("cannot allocate memory for config-file path");
775                 return -ENOMEM;
776         }
777
778         ret = conf_parse_file(opts, len, path);
779         free(path);
780         return ret;
781 }
782
783 int conf_parse_standard_files(struct conf_option *opts, size_t len,
784                               const char *fname)
785 {
786         int ret;
787         const char *home;
788
789         ret = conf_parse_file_f(opts, len, "/etc/%s.conf", fname);
790         if (ret)
791                 return ret;
792
793         home = getenv("HOME");
794         if (home) {
795                 ret = conf_parse_file_f(opts, len, "%s/.%s.conf", home, fname);
796                 if (ret)
797                         return ret;
798         }
799
800         return 0;
801 }