input-eventd: handle new version for input-tables.h
[nas-tools.git] / input-eventd / config.c
1 /*
2  * This file is part of input-eventd.
3  *
4  * Copyright (C) 2011 Simon Guinot <simon.guinot@sequanux.org>
5  *
6  * input-eventd is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * input-eventd is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with input-eventd.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21  * - Configuration file example:
22  *
23  * type=EV_KEY; code=KEY_POWER; debounce_delay=1000; run=/bin/key
24  * type=EV_SW; debounce_delay=1000; run=/bin/sw
25  * run=/bin/dbg; invalid=invalid # Some comments
26  *
27  * - Convert each configuration file line into a cfg_token linked list:
28  *
29  * ,______________,   ,______________,   ,_____________,   ,______________,
30  * |  cfg_token   |-->|  cfg_token   |-->|  cfg_token  |-->|  cfg_token   |
31  * |--------------|   |--------------|   |-------------|   |--------------|
32  * |key: type     |   |key: code     |   |key: debounce|   |key: run      |
33  * |val: EV_KEY   |   |val: KEY_POWER|   |val: 1000    |   |val: /bin/prog|
34  * `--------------'   `--------------'   `-------------'   `--------------'
35  *
36  * ,______________,   ,_____________,   ,______________,
37  * |  cfg_token   |-->|  cfg_token  |-->|  cfg_token   |
38  * |--------------|   |-------------|   |--------------|
39  * |key: type     |   |key: debounce|   |key: run      |
40  * |val: EV_SW    |   |val: 1000    |   |val: /bin/sw  |
41  * `--------------'   `-------------'   `--------------'
42  *
43  * ,______________,   ,_____________,
44  * |  cfg_token   |-->|  cfg_token  |
45  * |--------------|   |-------------|
46  * |key: run      |   |key: invalid |
47  * |val: /bin/dbg |   |val: invalid |
48  * `--------------'   `-------------'
49  *
50  * - Build an evpoll linked list:
51  *
52  * ,_____________________,   ,_____________________,   ,_____________________,
53  * |       evpoll        |-->|       evpoll        |-->|       evpoll        |
54  * |---------------------|   |---------------------|   |---------------------|
55  * |prognam  : /bin/key  |   |prognam  : /bin/sw   |   |prognam  : /bin/dbg  |
56  * |debounce : 1000      |   |debounce : 1000      |   |debounce : 0         |
57  * |types    : EV_KEY    |   |types    : EV_SW     |   |types    : all       |
58  * |codes    : KEY_POWER |   |codes    : all       |   |codes    : all       |
59  * `---------------------'   `---------------------'   `---------------------'
60  */
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <stdint.h>
65 #include <unistd.h>
66 #include <ctype.h>
67 #include <errno.h>
68
69 #include "list.h"
70 #include "input-event.h"
71 #include "input-tables.h"
72 #include "debug.h"
73
74 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
75
76 struct cfg_token {
77         const char *key;
78         const char *value;
79         struct list_head list;
80 };
81
82 /*
83  * Remove leading/trailing white-space and control characters.
84  */
85 static char *strip(char *token)
86 {
87         int len = strlen(token);
88
89         while (len-- && (iscntrl(token[len]) || isspace(token[len])))
90                 token[len] = '\0';
91
92         while (len-- && (iscntrl(*token) || isspace(*token)))
93                 token++;
94
95         return token;
96 }
97
98 static struct cfg_token *
99 get_cfg_token_next(const char *key, struct list_head *list,
100                         struct cfg_token *cfg)
101 {
102         cfg = list_prepare_entry(cfg, list, list);
103         list_for_each_entry_continue(cfg, list, list)
104                 if (strcmp(key, cfg->key) == 0)
105                         return cfg;
106
107         return NULL;
108 }
109
110 static struct cfg_token *init_cfg_token(char *token)
111 {
112         struct cfg_token *cfg;
113         char *key = token;
114         char *value;
115
116         value = strchr(token, '=');
117         if (!value)
118                 return NULL;
119         *value++ = '\0';
120
121         cfg = (struct cfg_token *) calloc(1, sizeof(struct cfg_token));
122         if (!cfg) {
123                 fprintf(stderr, "calloc: %s\n", strerror(errno));
124                 return NULL;
125         }
126
127         cfg->key = strip(key);
128         cfg->value = strip(value);
129
130         dbg("config token [%s=%s]", cfg->key, cfg->value);
131
132         return cfg;
133 }
134
135 /*
136  * Convert a string into an event code number. A valid string should be:
137  * - an event type definition as found in linux/input.h.
138  *   Example: "EV_KEY", "EV_SW", "EV_PWR", etc...
139  * - or a numeric string (base 16 or 10)
140  */
141 static int string_to_type(const char *str, uint16_t *type)
142 {
143         char *end;
144         int i;
145
146         /* Get event type from linux/input.h definition */
147         for (i = 0; i < ARRAY_SIZE(event_types); i++)
148                 if (strcmp(str, event_types[i].string) == 0) {
149                         *type = event_types[i].value;
150                         return 0;
151                 }
152
153         /* Get event type from a numeric string (base 16 or 10) */
154         *type = strtoul(str, &end, 16);
155         if (errno || *end)
156                 *type = strtoul(str, &end, 10);
157         if (errno || *end)
158                 goto err;
159
160         /* Check converted event code number */
161         for (i = 0; i < ARRAY_SIZE(event_types); i++)
162                 if (event_types[i].value == *type)
163                         return 0;
164
165 err:
166         fprintf(stderr, "invalid type: %s\n", str);
167
168         return -1;
169 }
170
171 /*
172  * Convert a string into an event code number. A valid string should be:
173  * - an event code definition as found in linux/input.h.
174  *   Example: "KEY_POWER", "KEY_SLEEP", "KEY_WAKEUP", etc...
175  * - or a numeric string (base 16 or 10)
176  */
177 static int string_to_code(const char *str, uint16_t type, uint16_t *code)
178 {
179         struct input_entry *event_codes = NULL;
180         char *end;
181         int i;
182
183         /* Get event codes table from type */
184         for (i = 0; table_index[i].codes; i++)
185                 if (table_index[i].type == type) {
186                         event_codes = table_index[i].codes;
187                         break;
188                 }
189         if (!event_codes)
190                 goto err;
191
192         /* Get event code from linux/input.h definition */
193         for (i = 0; event_codes[i].string[0] != '\0'; i++)
194                 if (strcmp(str, event_codes[i].string) == 0) {
195                         *code = event_codes[i].value;
196                         return 0;
197                 }
198
199         /* Get event code from a numeric string (base 16 or 10) */
200         *code = strtoul(str, &end, 16);
201         if (errno || *end)
202                 *code = strtoul(str, &end, 10);
203         if (errno || *end)
204                 goto err;
205
206         /* Check converted event code number */
207         for (i = 0; event_codes[i].string[0] != '\0'; i++)
208                 if (event_codes[i].value == *code)
209                         return 0;
210
211 err:
212         fprintf(stderr, "invalid code: type=%d code=%s\n", type, str);
213
214         return -1;
215 }
216
217 static struct evpoll *build_evpoll(struct list_head *list)
218 {
219         struct evpoll *evp;
220         struct cfg_token *cfg;
221         uint16_t type;
222
223         evp = alloc_evpoll();
224         if (!evp)
225                 return NULL;
226
227         /* Set event debounce delay */
228         cfg = get_cfg_token_next("debounce_delay", list, NULL);
229         if (cfg)
230                 set_evpoll_debounce_delay(evp, atoi(cfg->value));
231
232         /* Set event program name */
233         cfg = get_cfg_token_next("run", list, NULL);
234         if (cfg)
235                 set_evpoll_progname(evp, cfg->value);
236
237         /* Set event type */
238         cfg = get_cfg_token_next("type", list, NULL);
239         if (!cfg) {
240                 set_evpoll_type_all(evp);
241                 return evp;
242         }
243         if (string_to_type(cfg->value, &type) == -1)
244                 goto err;
245         set_evpoll_type(evp, type);
246
247         /* Set event code */
248         cfg = get_cfg_token_next("code", list, NULL);
249         if (!cfg) {
250                 set_evpoll_typecode_all(evp, type);
251                 return evp;
252         }
253         do {
254                 uint16_t code;
255
256                 if (string_to_code(cfg->value, type, &code) == -1)
257                         goto err;
258                 set_evpoll_typecode(evp, type, code);
259         } while ((cfg = get_cfg_token_next("code", list, cfg)));
260
261         return evp;
262
263 err:
264         free_evpoll(evp);
265         return NULL;
266 }
267
268 int parse_config(const char *conffile, struct list_head *evpoll_list)
269 {
270         int err = 0;
271         FILE *file;
272         char line[512];
273
274         file = fopen(conffile, "r");
275         if (!file) {
276                 fprintf(stderr, "fopen %s: %s\n", conffile, strerror(errno));
277                 return -1;
278         }
279
280         /* FIXME: handle line length over sizeof(line) */
281         while (fgets(line, sizeof(line), file)) {
282                 LIST_HEAD(config_list);
283                 struct cfg_token *cfg, *tmp;
284                 struct evpoll *evp;
285                 char *token = line;
286                 char *end;
287
288                 dbg("config line  [%s]", line);
289
290                 /* Discard trailing comments */
291                 end = strchr(token, '#');
292                 if (end)
293                         *end = '\0';
294
295                 /* Convert configuration line into a cfg_token linked list */
296                 while (token) {
297                         char *delim = strchr(token, ';');
298
299                         if (delim)
300                                 *delim = '\0';
301
302                         cfg = init_cfg_token(token);
303                         if (!cfg) {
304                                 fprintf(stderr, "invalid token [%s]\n", token);
305                                 err = -1;
306                                 goto free_config_list;
307                         }
308
309                         list_add(&cfg->list, &config_list);
310                         if (delim)
311                                 token = delim + 1;
312                         else
313                                 token = NULL;
314                 }
315
316                 /* Convert the cfg_token linked list into evpoll */
317                 evp = build_evpoll(&config_list);
318                 if (evp)
319                         insert_evpoll(evp, evpoll_list);
320
321 free_config_list:
322                 list_for_each_entry_safe(cfg, tmp, &config_list, list) {
323                         list_del(&cfg->list);
324                         free(cfg);
325                 }
326                 if (err)
327                         break;
328         };
329
330         return err;
331 }