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 event 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 *ev_codes = NULL;
180         uint16_t ev_codes_count = 0;
181         char *end;
182         int i;
183
184         /* Get event codes table from type */
185         for (i = 0; ARRAY_SIZE(table_index); i++)
186                 if (table_index[i].type == type) {
187                         ev_codes = table_index[i].codes;
188                         ev_codes_count = table_index[i].count;
189                         break;
190                 }
191         if (!ev_codes)
192                 goto err;
193
194         /* Get event code from linux/input.h definition */
195         for (i = 0; i < ev_codes_count; i++)
196                 if (strcmp(str, ev_codes[i].string) == 0) {
197                         *code = ev_codes[i].value;
198                         return 0;
199                 }
200
201         /* Get event code from a numeric string (base 16 or 10) */
202         *code = strtoul(str, &end, 16);
203         if (errno || *end)
204                 *code = strtoul(str, &end, 10);
205         if (errno || *end)
206                 goto err;
207
208         /* Check converted event code number */
209         for (i = 0; i < ev_codes_count; i++)
210                 if (ev_codes[i].value == *code)
211                         return 0;
212
213 err:
214         fprintf(stderr, "invalid event code: type=%d code=%s\n", type, str);
215
216         return -1;
217 }
218
219 static struct evpoll *build_evpoll(struct list_head *list)
220 {
221         struct evpoll *evp;
222         struct cfg_token *cfg;
223         uint16_t type;
224
225         evp = alloc_evpoll();
226         if (!evp)
227                 return NULL;
228
229         /* Set event debounce delay */
230         cfg = get_cfg_token_next("debounce_delay", list, NULL);
231         if (cfg)
232                 set_evpoll_debounce_delay(evp, atoi(cfg->value));
233
234         /* Set event program name */
235         cfg = get_cfg_token_next("run", list, NULL);
236         if (cfg)
237                 set_evpoll_progname(evp, cfg->value);
238
239         /* Set event type */
240         cfg = get_cfg_token_next("type", list, NULL);
241         if (!cfg) {
242                 set_evpoll_type_all(evp);
243                 return evp;
244         }
245         if (string_to_type(cfg->value, &type) == -1)
246                 goto err;
247         set_evpoll_type(evp, type);
248
249         /* Set event code */
250         cfg = get_cfg_token_next("code", list, NULL);
251         if (!cfg) {
252                 set_evpoll_typecode_all(evp, type);
253                 return evp;
254         }
255         do {
256                 uint16_t code;
257
258                 if (string_to_code(cfg->value, type, &code) == -1)
259                         goto err;
260                 set_evpoll_typecode(evp, type, code);
261         } while ((cfg = get_cfg_token_next("code", list, cfg)));
262
263         return evp;
264
265 err:
266         free_evpoll(evp);
267         return NULL;
268 }
269
270 int parse_config(const char *conffile, struct list_head *evpoll_list)
271 {
272         int err = 0;
273         FILE *file;
274         char line[512];
275
276         file = fopen(conffile, "r");
277         if (!file) {
278                 fprintf(stderr, "fopen %s: %s\n", conffile, strerror(errno));
279                 return -1;
280         }
281
282         /* FIXME: handle line length over sizeof(line) */
283         while (fgets(line, sizeof(line), file)) {
284                 LIST_HEAD(config_list);
285                 struct cfg_token *cfg, *tmp;
286                 struct evpoll *evp;
287                 char *token = line;
288                 char *end;
289
290                 dbg("config line  [%s]", line);
291
292                 /* Discard trailing comments */
293                 end = strchr(token, '#');
294                 if (end)
295                         *end = '\0';
296
297                 /* Convert configuration line into a cfg_token linked list */
298                 while (token) {
299                         char *delim = strchr(token, ';');
300
301                         if (delim)
302                                 *delim = '\0';
303
304                         cfg = init_cfg_token(token);
305                         if (!cfg) {
306                                 fprintf(stderr, "invalid token [%s]\n", token);
307                                 err = -1;
308                                 goto free_config_list;
309                         }
310
311                         list_add(&cfg->list, &config_list);
312                         if (delim)
313                                 token = delim + 1;
314                         else
315                                 token = NULL;
316                 }
317
318                 /* Convert the cfg_token linked list into evpoll */
319                 evp = build_evpoll(&config_list);
320                 if (evp)
321                         insert_evpoll(evp, evpoll_list);
322
323 free_config_list:
324                 list_for_each_entry_safe(cfg, tmp, &config_list, list) {
325                         list_del(&cfg->list);
326                         free(cfg);
327                 }
328                 if (err)
329                         break;
330         };
331
332         return err;
333 }