hostapd: add ucode support, use ucode for the main ubus object
[openwrt/openwrt.git] / package / network / services / hostapd / src / src / utils / ucode.c
1 #include <unistd.h>
2 #include "ucode.h"
3 #include "utils/eloop.h"
4 #include "crypto/crypto.h"
5 #include "crypto/sha1.h"
6 #include <libubox/uloop.h>
7 #include <ucode/compiler.h>
8
9 static uc_value_t *registry;
10 static uc_vm_t vm;
11 static struct uloop_timeout gc_timer;
12
13 static void uc_gc_timer(struct uloop_timeout *timeout)
14 {
15 ucv_gc(&vm);
16 }
17
18 uc_value_t *uc_wpa_printf(uc_vm_t *vm, size_t nargs)
19 {
20 uc_value_t *level = uc_fn_arg(0);
21 uc_value_t *ret, **args;
22 uc_cfn_ptr_t _sprintf;
23 int l = MSG_INFO;
24 int i, start = 0;
25
26 _sprintf = uc_stdlib_function("sprintf");
27 if (!sprintf)
28 return NULL;
29
30 if (ucv_type(level) == UC_INTEGER) {
31 l = ucv_int64_get(level);
32 start++;
33 }
34
35 if (nargs <= start)
36 return NULL;
37
38 ret = _sprintf(vm, nargs - start);
39 if (ucv_type(ret) != UC_STRING)
40 return NULL;
41
42 wpa_printf(l, "%s", ucv_string_get(ret));
43 ucv_put(ret);
44
45 return NULL;
46 }
47
48 uc_value_t *uc_wpa_getpid(uc_vm_t *vm, size_t nargs)
49 {
50 return ucv_int64_new(getpid());
51 }
52
53 uc_value_t *uc_wpa_sha1(uc_vm_t *vm, size_t nargs)
54 {
55 u8 hash[SHA1_MAC_LEN];
56 char hash_hex[2 * ARRAY_SIZE(hash) + 1];
57 uc_value_t *val;
58 size_t *lens;
59 const u8 **args;
60 int i;
61
62 if (!nargs)
63 return NULL;
64
65 args = alloca(nargs * sizeof(*args));
66 lens = alloca(nargs * sizeof(*lens));
67 for (i = 0; i < nargs; i++) {
68 val = uc_fn_arg(i);
69 if (ucv_type(val) != UC_STRING)
70 return NULL;
71
72 args[i] = ucv_string_get(val);
73 lens[i] = ucv_string_length(val);
74 }
75
76 if (sha1_vector(nargs, args, lens, hash))
77 return NULL;
78
79 for (i = 0; i < ARRAY_SIZE(hash); i++)
80 sprintf(hash_hex + 2 * i, "%02x", hash[i]);
81
82 return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
83 }
84
85 uc_vm_t *wpa_ucode_create_vm(void)
86 {
87 static uc_parse_config_t config = {
88 .strict_declarations = true,
89 .lstrip_blocks = true,
90 .trim_blocks = true,
91 .raw_mode = true
92 };
93
94 uc_search_path_init(&config.module_search_path);
95 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.so");
96 uc_search_path_add(&config.module_search_path, HOSTAPD_UC_PATH "*.uc");
97
98 uc_vm_init(&vm, &config);
99
100 uc_stdlib_load(uc_vm_scope_get(&vm));
101 eloop_add_uloop();
102 gc_timer.cb = uc_gc_timer;
103
104 return &vm;
105 }
106
107 int wpa_ucode_run(const char *script)
108 {
109 uc_source_t *source;
110 uc_program_t *prog;
111 uc_value_t *ops;
112 char *err;
113 int ret;
114
115 source = uc_source_new_file(script);
116 if (!source)
117 return -1;
118
119 prog = uc_compile(vm.config, source, &err);
120 uc_source_put(source);
121 if (!prog) {
122 wpa_printf(MSG_ERROR, "Error loading ucode: %s\n", err);
123 return -1;
124 }
125
126 ret = uc_vm_execute(&vm, prog, &ops);
127 uc_program_put(prog);
128 if (ret || !ops)
129 return -1;
130
131 registry = ucv_array_new(&vm);
132 uc_vm_registry_set(&vm, "hostap.registry", registry);
133 ucv_array_set(registry, 0, ucv_get(ops));
134
135 return 0;
136 }
137
138 int wpa_ucode_call_prepare(const char *fname)
139 {
140 uc_value_t *obj, *func;
141
142 if (!registry)
143 return -1;
144
145 obj = ucv_array_get(registry, 0);
146 if (!obj)
147 return -1;
148
149 func = ucv_object_get(obj, fname, NULL);
150 if (!ucv_is_callable(func))
151 return -1;
152
153 uc_vm_stack_push(&vm, ucv_get(obj));
154 uc_vm_stack_push(&vm, ucv_get(func));
155
156 return 0;
157 }
158
159 uc_value_t *wpa_ucode_global_init(const char *name, uc_resource_type_t *global_type)
160 {
161 uc_value_t *global = uc_resource_new(global_type, NULL);
162 uc_value_t *proto;
163
164 uc_vm_registry_set(&vm, "hostap.global", global);
165 proto = ucv_prototype_get(global);
166 ucv_object_add(proto, "data", ucv_get(ucv_object_new(&vm)));
167
168 #define ADD_CONST(x) ucv_object_add(proto, #x, ucv_int64_new(x))
169 ADD_CONST(MSG_EXCESSIVE);
170 ADD_CONST(MSG_MSGDUMP);
171 ADD_CONST(MSG_DEBUG);
172 ADD_CONST(MSG_INFO);
173 ADD_CONST(MSG_WARNING);
174 ADD_CONST(MSG_ERROR);
175 #undef ADD_CONST
176
177 ucv_object_add(uc_vm_scope_get(&vm), name, ucv_get(global));
178
179 return global;
180 }
181
182 void wpa_ucode_registry_add(uc_value_t *reg, uc_value_t *val, int *idx)
183 {
184 uc_value_t *data;
185 int i = 0;
186
187 while (ucv_array_get(reg, i))
188 i++;
189
190 ucv_array_set(reg, i, ucv_get(val));
191
192 data = ucv_object_new(&vm);
193 ucv_object_add(ucv_prototype_get(val), "data", ucv_get(data));
194
195 *idx = i + 1;
196 }
197
198 uc_value_t *wpa_ucode_registry_get(uc_value_t *reg, int idx)
199 {
200 if (!idx)
201 return NULL;
202
203 return ucv_array_get(reg, idx - 1);
204 }
205
206 uc_value_t *wpa_ucode_registry_remove(uc_value_t *reg, int idx)
207 {
208 uc_value_t *val = wpa_ucode_registry_get(reg, idx);
209
210 if (val)
211 ucv_array_set(reg, idx - 1, NULL);
212
213 return val;
214 }
215
216
217 uc_value_t *wpa_ucode_call(size_t nargs)
218 {
219 if (uc_vm_call(&vm, true, nargs) != EXCEPTION_NONE)
220 return NULL;
221
222 if (!gc_timer.pending)
223 uloop_timeout_set(&gc_timer, 10);
224
225 return uc_vm_stack_pop(&vm);
226 }
227
228 void wpa_ucode_free_vm(void)
229 {
230 if (!vm.config)
231 return;
232
233 uc_search_path_free(&vm.config->module_search_path);
234 uc_vm_free(&vm);
235 registry = NULL;
236 vm = (uc_vm_t){};
237 }