add signal handler
[project/procd.git] / hotplug-rule.c
1 #include <sys/stat.h>
2
3 #include <libgen.h>
4 #include <regex.h>
5
6 #include <json/json.h>
7 #include <libubox/avl-cmp.h>
8 #include <libubox/blobmsg_json.h>
9
10 #include "hotplug.h"
11
12 static struct blob_buf b;
13
14 static int rule_process_expr(struct blob_attr *cur, struct blob_attr *msg);
15 static int rule_process_cmd(struct blob_attr *cur, struct blob_attr *msg);
16
17 static char *__msg_find_var(struct blob_attr *msg, const char *name)
18 {
19         struct blob_attr *cur;
20         int rem;
21
22         blob_for_each_attr(cur, msg, rem) {
23                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
24                         continue;
25
26                 if (strcmp(blobmsg_name(cur), name) != 0)
27                         continue;
28
29                 return blobmsg_data(cur);
30         }
31
32         return NULL;
33 }
34
35 static char *msg_find_var(struct blob_attr *msg, const char *name)
36 {
37         char *str;
38
39         if (!strcmp(name, "DEVICENAME") || !strcmp(name, "DEVNAME")) {
40                 str = __msg_find_var(msg, "DEVPATH");
41                 if (!str)
42                         return NULL;
43
44                 return basename(str);
45         }
46
47         return __msg_find_var(msg, name);
48 }
49
50 static void
51 rule_get_tuple(struct blob_attr *cur, struct blob_attr **tb, int t1, int t2)
52 {
53         static struct blobmsg_policy expr_tuple[3] = {
54                 { .type = BLOBMSG_TYPE_STRING },
55                 {},
56                 {},
57         };
58
59         expr_tuple[1].type = t1;
60         expr_tuple[2].type = t2;
61         blobmsg_parse_array(expr_tuple, 3, tb, blobmsg_data(cur), blobmsg_data_len(cur));
62 }
63
64 static int handle_if(struct blob_attr *expr, struct blob_attr *msg)
65 {
66         struct blob_attr *tb[4];
67         int ret;
68
69         static const struct blobmsg_policy if_tuple[4] = {
70                 { .type = BLOBMSG_TYPE_STRING },
71                 { .type = BLOBMSG_TYPE_ARRAY },
72                 { .type = BLOBMSG_TYPE_ARRAY },
73                 { .type = BLOBMSG_TYPE_ARRAY },
74         };
75
76         blobmsg_parse_array(if_tuple, 4, tb, blobmsg_data(expr), blobmsg_data_len(expr));
77
78         if (!tb[1] || !tb[2])
79                 return 0;
80
81         ret = rule_process_expr(tb[1], msg);
82         if (ret < 0)
83                 return 0;
84
85         if (ret)
86                 return rule_process_cmd(tb[2], msg);
87
88         if (!tb[3])
89                 return 0;
90
91         return rule_process_cmd(tb[3], msg);
92 }
93
94 static int handle_case(struct blob_attr *expr, struct blob_attr *msg)
95 {
96         struct blob_attr *tb[3], *cur;
97         const char *var;
98         int rem;
99
100         rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, BLOBMSG_TYPE_TABLE);
101         if (!tb[1] || !tb[2])
102                 return 0;
103
104         var = msg_find_var(msg, blobmsg_data(tb[1]));
105         if (!var)
106                 return 0;
107
108         blobmsg_for_each_attr(cur, tb[2], rem) {
109                 if (!strcmp(var, blobmsg_name(cur)))
110                         return rule_process_cmd(cur, msg);
111         }
112
113         return 0;
114 }
115
116 static int handle_return(struct blob_attr *expr, struct blob_attr *msg)
117 {
118         return -2;
119 }
120
121 static int handle_include(struct blob_attr *expr, struct blob_attr *msg)
122 {
123         struct blob_attr *tb[3];
124         struct rule_file *r;
125
126         rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
127         if (!tb[1])
128                 return 0;
129
130         r = rule_file_get(blobmsg_data(tb[1]));
131         if (!r)
132                 return 0;
133
134         return rule_process_cmd(r->data, msg);
135 }
136
137 static const struct rule_handler cmd[] = {
138         { "if", handle_if },
139         { "case", handle_case },
140         { "return", handle_return },
141         { "include", handle_include },
142 };
143
144 static int eq_regex_cmp(const char *str, const char *pattern, bool regex)
145 {
146         regex_t reg;
147         int ret;
148
149         if (!regex)
150                 return !strcmp(str, pattern);
151
152         if (regcomp(&reg, pattern, REG_EXTENDED | REG_NOSUB))
153                 return 0;
154
155         ret = !regexec(&reg, str, 0, NULL, 0);
156         regfree(&reg);
157
158         return ret;
159 }
160
161 static int expr_eq_regex(struct blob_attr *expr, struct blob_attr *msg, bool regex)
162 {
163         struct blob_attr *tb[3], *cur;
164         const char *var;
165         int rem;
166
167         rule_get_tuple(expr, tb, BLOBMSG_TYPE_STRING, 0);
168         if (!tb[1] || !tb[2])
169                 return -1;
170
171         var = msg_find_var(msg, blobmsg_data(tb[1]));
172         if (!var)
173                 return 0;
174
175         switch(blobmsg_type(tb[2])) {
176         case BLOBMSG_TYPE_STRING:
177                 return eq_regex_cmp(var, blobmsg_data(tb[2]), regex);
178         case BLOBMSG_TYPE_ARRAY:
179                 blobmsg_for_each_attr(cur, tb[2], rem) {
180                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
181                                 rule_error(cur, "Unexpected element type");
182                                 return -1;
183                         }
184
185                         if (eq_regex_cmp(var, blobmsg_data(cur), regex))
186                                 return 1;
187                 }
188                 return 0;
189         default:
190                 rule_error(tb[2], "Unexpected element type");
191                 return -1;
192         }
193 }
194
195 static int handle_expr_eq(struct blob_attr *expr, struct blob_attr *msg)
196 {
197         return expr_eq_regex(expr, msg, false);
198 }
199
200 static int handle_expr_regex(struct blob_attr *expr, struct blob_attr *msg)
201 {
202         return expr_eq_regex(expr, msg, true);
203 }
204
205 static int handle_expr_has(struct blob_attr *expr, struct blob_attr *msg)
206 {
207         struct blob_attr *tb[3], *cur;
208         int rem;
209
210         rule_get_tuple(expr, tb, 0, 0);
211         if (!tb[1])
212                 return -1;
213
214         switch(blobmsg_type(tb[1])) {
215         case BLOBMSG_TYPE_STRING:
216                 return !!msg_find_var(msg, blobmsg_data(tb[1]));
217         case BLOBMSG_TYPE_ARRAY:
218                 blobmsg_for_each_attr(cur, tb[1], rem) {
219                         if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
220                                 rule_error(cur, "Unexpected element type");
221                                 return -1;
222                         }
223
224                         if (msg_find_var(msg, blobmsg_data(cur)))
225                                 return 1;
226                 }
227                 return 0;
228         default:
229                 rule_error(tb[1], "Unexpected element type");
230                 return -1;
231         }
232 }
233
234 static int expr_and_or(struct blob_attr *expr, struct blob_attr *msg, bool and)
235 {
236         struct blob_attr *cur;
237         int ret, rem;
238         int i = 0;
239
240         blobmsg_for_each_attr(cur, expr, rem) {
241                 if (i++ < 1)
242                         continue;
243
244                 ret = rule_process_expr(cur, msg);
245                 if (ret < 0)
246                         return ret;
247
248                 if (ret != and)
249                         return ret;
250         }
251
252         return and;
253 }
254
255 static int handle_expr_and(struct blob_attr *expr, struct blob_attr *msg)
256 {
257         return expr_and_or(expr, msg, 1);
258 }
259
260 static int handle_expr_or(struct blob_attr *expr, struct blob_attr *msg)
261 {
262         return expr_and_or(expr, msg, 0);
263 }
264
265 static int handle_expr_not(struct blob_attr *expr, struct blob_attr *msg)
266 {
267         struct blob_attr *tb[3];
268
269         rule_get_tuple(expr, tb, BLOBMSG_TYPE_ARRAY, 0);
270         if (!tb[1])
271                 return -1;
272
273         return rule_process_expr(tb[1], msg);
274 }
275
276 static const struct rule_handler expr[] = {
277         { "eq", handle_expr_eq },
278         { "regex", handle_expr_regex },
279         { "has", handle_expr_has },
280         { "and", handle_expr_and },
281         { "or", handle_expr_or },
282         { "not", handle_expr_not },
283 };
284
285 static int
286 __rule_process_type(struct blob_attr *cur, struct blob_attr *msg,
287                     const struct rule_handler *h, int n, bool *found)
288 {
289         const char *name = blobmsg_data(blobmsg_data(cur));
290         int i;
291
292         for (i = 0; i < n; i++) {
293                 if (strcmp(name, h[i].name) != 0)
294                         continue;
295
296                 *found = true;
297                 return h[i].handler(cur, msg);
298         }
299
300         *found = false;
301         return -1;
302 }
303
304 static int rule_process_expr(struct blob_attr *cur, struct blob_attr *msg)
305 {
306         bool found;
307         int ret;
308
309         if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
310             blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
311                 rule_error(cur, "Unexpected element type");
312                 return -1;
313         }
314
315         ret = __rule_process_type(cur, msg, expr, ARRAY_SIZE(expr), &found);
316         if (!found)
317                 rule_error(cur, "Unknown expression type");
318
319         return ret;
320 }
321
322 static void cmd_add_string(const char *pattern, struct blob_attr *msg)
323 {
324         char *dest, *next, *str;
325         int len = 0;
326         bool var = false;
327
328         dest = blobmsg_alloc_string_buffer(&b, NULL, 1);
329         str = alloca(strlen(pattern) + 1);
330         strcpy(str, pattern);
331         next = str;
332
333         while (*str) {
334                 const char *cur;
335                 int cur_len = 0;
336
337                 next = strchr(str, '%');
338                 if (!next)
339                         next = str + strlen(str);
340
341                 if (var) {
342                         if (next > str) {
343                                 *next = 0;
344                                 cur = msg_find_var(msg, str);
345                                 if (cur)
346                                         cur_len = strlen(cur);
347                         } else {
348                                 cur = str - 1;
349                                 cur_len = 1;
350                         }
351                 } else {
352                         cur = str;
353                         cur_len = next - str;
354                 }
355
356                 if (cur_len) {
357                         dest = blobmsg_realloc_string_buffer(&b, cur_len + 1);
358                         memcpy(dest + len, cur, cur_len);
359                         len += cur_len;
360                 }
361
362                 var = !var;
363                 str = next + 1;
364         }
365
366         dest[len] = 0;
367         blobmsg_add_string_buffer(&b);
368 }
369
370 static int cmd_process_strings(struct blob_attr *attr, struct blob_attr *msg)
371 {
372         struct blob_attr *cur;
373         int args = -1;
374         int rem;
375         void *c;
376
377         blob_buf_init(&b, 0);
378         c = blobmsg_open_array(&b, NULL);
379         blobmsg_for_each_attr(cur, attr, rem) {
380                 if (args++ < 0)
381                         continue;
382
383                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) {
384                         rule_error(attr, "Invalid argument in command");
385                         return -1;
386                 }
387
388                 cmd_add_string(blobmsg_data(cur), msg);
389         }
390
391         blobmsg_close_array(&b, c);
392
393         return 0;
394 }
395
396 static int __rule_process_cmd(struct blob_attr *cur, struct blob_attr *msg)
397 {
398         const char *name;
399         bool found;
400         int ret;
401
402         if (blobmsg_type(cur) != BLOBMSG_TYPE_ARRAY ||
403             blobmsg_type(blobmsg_data(cur)) != BLOBMSG_TYPE_STRING) {
404                 rule_error(cur, "Unexpected element type");
405                 return -1;
406         }
407
408         ret = __rule_process_type(cur, msg, cmd, ARRAY_SIZE(cmd), &found);
409         if (found)
410                 return ret;
411
412         name = blobmsg_data(blobmsg_data(cur));
413         ret = cmd_process_strings(cur, msg);
414         if (ret)
415                 return ret;
416
417         rule_handle_command(name, blob_data(b.head));
418
419         return 0;
420 }
421
422 static int rule_process_cmd(struct blob_attr *block, struct blob_attr *msg)
423 {
424         struct blob_attr *cur;
425         int rem;
426         int ret;
427         int i = 0;
428
429         if (blobmsg_type(block) != BLOBMSG_TYPE_ARRAY) {
430                 rule_error(block, "Unexpected element type");
431                 return -1;
432         }
433
434         blobmsg_for_each_attr(cur, block, rem) {
435                 switch(blobmsg_type(cur)) {
436                 case BLOBMSG_TYPE_STRING:
437                         if (!i)
438                                 return __rule_process_cmd(block, msg);
439                 default:
440                         ret = rule_process_cmd(cur, msg);
441                         if (ret < -1)
442                                 return ret;
443                         break;
444                 }
445                 i++;
446         }
447
448         return 0;
449 }
450
451 void rule_process_msg(struct rule_file *f, struct blob_attr *msg)
452 {
453         rule_process_cmd(f->data, msg);
454 }
455
456 static struct rule_file *
457 rule_file_load(const char *filename)
458 {
459         struct rule_file *r;
460         struct stat st;
461
462         json_object *obj = NULL;
463
464         blob_buf_init(&b, 0);
465
466         if (stat(filename, &st))
467                 return NULL;
468
469         obj = json_object_from_file((char *) filename);
470         if (is_error(obj))
471                 return NULL;
472
473         if (!json_object_is_type(obj, json_type_array)) {
474                 json_object_put(obj);
475                 return NULL;
476         }
477
478         blobmsg_add_json_element(&b, filename, obj);
479         json_object_put(obj);
480
481         r = calloc(1, sizeof(*r) + blob_len(b.head));
482         memcpy(r->data, blob_data(b.head), blob_len(b.head));
483         r->avl.key = blobmsg_name(r->data);
484
485         return r;
486 }
487
488 static struct avl_tree rule_files;
489
490 struct rule_file *
491 rule_file_get(const char *filename)
492 {
493         struct rule_file *r;
494
495         if (!rule_files.comp)
496                 avl_init(&rule_files, avl_strcmp, false, NULL);
497
498         r = avl_find_element(&rule_files, filename, r, avl);
499         if (r)
500                 return r;
501
502         r = rule_file_load(filename);
503         if (!r)
504                 return NULL;
505
506         avl_insert(&rule_files, &r->avl);
507         return r;
508 }
509
510 void
511 rule_file_free_all(void)
512 {
513         struct rule_file *r, *next;
514
515         avl_remove_all_elements(&rule_files, r, avl, next)
516                 free(r);
517
518         blob_buf_free(&b);
519 }