ucode: check for errors in ftruncate()
[project/udebug.git] / lib-ucode.c
1 #include <math.h>
2 #include <libubox/utils.h>
3 #include <libubox/usock.h>
4 #include <libubox/udebug.h>
5 #include <ucode/module.h>
6 #include "udebug-pcap.h"
7
8 static uc_resource_type_t *rbuf_type, *wbuf_type, *snapshot_type, *pcap_type;
9 static uc_value_t *registry;
10 static struct udebug u;
11 static uc_vm_t *_vm;
12
13 struct uc_pcap {
14 struct pcap_context pcap;
15 int fd;
16 FILE *f;
17 };
18
19 static size_t add_registry(uc_value_t *val)
20 {
21 size_t i = 0;
22
23 while (ucv_array_get(registry, i))
24 i += 2;
25
26 ucv_array_set(registry, i, ucv_get(val));
27
28 return i;
29 }
30
31 static void
32 uc_udebug_notify_cb(struct udebug *ctx, struct udebug_remote_buf *rb)
33 {
34 uintptr_t idx = (uintptr_t)rb->priv;
35 uc_value_t *cb, *this;
36 uc_vm_t *vm = _vm;
37
38 this = ucv_array_get(registry, idx);
39 cb = ucv_array_get(registry, idx + 1);
40
41 if (!ucv_is_callable(cb))
42 return;
43
44 uc_vm_stack_push(vm, ucv_get(this));
45 uc_vm_stack_push(vm, ucv_get(cb));
46 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
47 return;
48
49 ucv_put(uc_vm_stack_pop(vm));
50 }
51
52 static uc_value_t *
53 uc_udebug_init(uc_vm_t *vm, size_t nargs)
54 {
55 uc_value_t *arg = uc_fn_arg(0);
56 uc_value_t *flag_auto = uc_fn_arg(1);
57 const char *path = NULL;
58
59 if (ucv_type(arg) == UC_STRING)
60 path = ucv_string_get(arg);
61
62 udebug_init(&u);
63 u.notify_cb = uc_udebug_notify_cb;
64 if (flag_auto && !ucv_is_truish(flag_auto)) {
65 if (udebug_connect(&u, path))
66 return NULL;
67 } else {
68 udebug_auto_connect(&u, path);
69 }
70
71 return ucv_boolean_new(true);
72 }
73
74 static uc_value_t *
75 uc_udebug_get_ring(uc_vm_t *vm, size_t nargs)
76 {
77 struct udebug_remote_buf *rb;
78 struct udebug_packet_info *info;
79 uc_value_t *arg = uc_fn_arg(0);
80 uc_value_t *id, *proc, *name, *pid;
81 int ifname_len, ifdesc_len;
82 char *ifname_buf, *ifdesc_buf;
83 uc_value_t *res;
84 uintptr_t idx;
85
86 #define R_IFACE_DESC "%s:%d"
87
88 if (ucv_type(arg) != UC_OBJECT)
89 return NULL;
90
91 id = ucv_object_get(arg, "id", NULL);
92 proc = ucv_object_get(arg, "proc_name", NULL);
93 name = ucv_object_get(arg, "ring_name", NULL);
94 pid = ucv_object_get(arg, "pid", NULL);
95
96 if (ucv_type(id) != UC_INTEGER ||
97 ucv_type(proc) != UC_STRING ||
98 ucv_type(name) != UC_STRING ||
99 ucv_type(pid) != UC_INTEGER)
100 return NULL;
101
102 ifname_len = strlen(ucv_string_get(name)) + 1;
103 ifdesc_len = sizeof(R_IFACE_DESC) + strlen(ucv_string_get(proc)) + 10;
104 rb = calloc_a(sizeof(*rb),
105 &info, sizeof(*info),
106 &ifname_buf, ifname_len,
107 &ifdesc_buf, ifdesc_len);
108 rb->meta = info;
109
110 strcpy(ifname_buf, ucv_string_get(name));
111 info->attr[UDEBUG_META_IFACE_NAME] = ifname_buf;
112 snprintf(ifdesc_buf, ifdesc_len, R_IFACE_DESC,
113 ucv_string_get(proc), (unsigned int)ucv_int64_get(pid));
114 info->attr[UDEBUG_META_IFACE_DESC] = ifdesc_buf;
115
116 if (udebug_remote_buf_map(&u, rb, (uint32_t)ucv_int64_get(id))) {
117 free(rb);
118 return NULL;
119 }
120
121 res = uc_resource_new(rbuf_type, rb);
122 idx = add_registry(res);
123 rb->priv = (void *)idx;
124
125 return res;
126 }
127
128 static void rbuf_free(void *ptr)
129 {
130 struct udebug_remote_buf *rb = ptr;
131 uintptr_t idx;
132
133 if (!rb)
134 return;
135
136 idx = (uintptr_t)rb->priv;
137 ucv_array_set(registry, idx, NULL);
138 ucv_array_set(registry, idx + 1, NULL);
139 udebug_remote_buf_unmap(&u, rb);
140 free(rb);
141 }
142
143 static uc_value_t *
144 uc_udebug_rbuf_fetch(uc_vm_t *vm, size_t nargs)
145 {
146 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
147 struct udebug_snapshot *s;
148
149 if (!rb)
150 return NULL;
151
152 s = udebug_remote_buf_snapshot(rb);
153 if (!s)
154 return NULL;
155
156 return uc_resource_new(snapshot_type, s);
157 }
158
159 static uc_value_t *
160 uc_udebug_rbuf_set_poll_cb(uc_vm_t *vm, size_t nargs)
161 {
162 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
163 uc_value_t *val = uc_fn_arg(0);
164 uintptr_t idx;
165
166 if (!rb)
167 return NULL;
168
169 idx = (uintptr_t)rb->priv;
170 ucv_array_set(registry, idx + 1, ucv_get(val));
171 if (!u.fd.registered)
172 udebug_add_uloop(&u);
173 udebug_remote_buf_set_poll(&u, rb, ucv_is_callable(val));
174
175 return NULL;
176 }
177
178 static uc_value_t *
179 uc_udebug_rbuf_set_fetch_duration(uc_vm_t *vm, size_t nargs)
180 {
181 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
182 uc_value_t *val = uc_fn_arg(0);
183 uint64_t ts;
184 double t;
185
186 if (!rb)
187 return NULL;
188
189 t = ucv_double_get(val);
190 if (isnan(t))
191 return NULL;
192
193 ts = udebug_timestamp();
194 ts -= (uint64_t)(fabs(t) * UDEBUG_TS_SEC);
195 udebug_remote_buf_set_start_time(rb, ts);
196
197 return ucv_boolean_new(true);
198 }
199
200 static uc_value_t *
201 uc_udebug_rbuf_set_fetch_count(uc_vm_t *vm, size_t nargs)
202 {
203 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
204 uc_value_t *val = uc_fn_arg(0);
205 uint32_t count;
206
207 if (!rb)
208 return NULL;
209
210 count = ucv_int64_get(val);
211 udebug_remote_buf_set_start_offset(rb, count);
212
213 return ucv_boolean_new(true);
214 }
215
216 static uc_value_t *
217 uc_udebug_rbuf_change_flags(uc_vm_t *vm, size_t nargs)
218 {
219 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
220 uc_value_t *mask = uc_fn_arg(0);
221 uc_value_t *set = uc_fn_arg(1);
222
223 if (!rb)
224 return NULL;
225
226 if (ucv_type(mask) != UC_INTEGER || ucv_type(set) != UC_INTEGER)
227 return NULL;
228
229 udebug_remote_buf_set_flags(rb, ucv_int64_get(mask), ucv_int64_get(set));
230 return ucv_boolean_new(true);
231 }
232
233 static uc_value_t *
234 uc_udebug_rbuf_get_flags(uc_vm_t *vm, size_t nargs)
235 {
236 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
237 if (!rb)
238 return NULL;
239
240 return ucv_int64_new(udebug_buf_flags(&rb->buf));
241 }
242
243 static uc_value_t *
244 uc_udebug_rbuf_close(uc_vm_t *vm, size_t nargs)
245 {
246 void **p = uc_fn_this("udebug.rbuf");
247
248 if (!p)
249 return NULL;
250
251 rbuf_free(*p);
252 *p = NULL;
253
254 return NULL;
255 }
256
257 static void
258 uc_udebug_pcap_init(struct uc_pcap *p, uc_value_t *args)
259 {
260 uc_value_t *hw, *os, *app;
261 struct pcap_meta meta = {};
262
263 if (ucv_type(args) == UC_OBJECT) {
264 hw = ucv_object_get(args, "hw", NULL);
265 os = ucv_object_get(args, "os", NULL);
266 app = ucv_object_get(args, "app", NULL);
267
268 meta.hw = ucv_string_get(hw);
269 meta.os = ucv_string_get(os);
270 meta.app = ucv_string_get(app);
271 }
272
273 pcap_init(&p->pcap, &meta);
274 }
275
276 static void
277 write_retry(int fd, const void *data, size_t len)
278 {
279 do {
280 ssize_t cur;
281
282 cur = write(fd, data, len);
283 if (cur < 0) {
284 if (errno == EINTR)
285 continue;
286
287 return;
288 }
289
290 data += cur;
291 len -= cur;
292 } while (len > 0);
293 }
294
295 static void
296 uc_udebug_pcap_write_block(struct uc_pcap *p)
297 {
298 size_t len;
299 void *data;
300
301 data = pcap_block_get(&len);
302 write_retry(p->fd, data, len);
303 }
304
305 static uc_value_t *
306 uc_debug_pcap_init(int fd, uc_value_t *args)
307 {
308 struct uc_pcap *p;
309
310 if (fd < 0)
311 return NULL;
312
313 p = calloc(1, sizeof(*p));
314 p->fd = fd;
315 uc_udebug_pcap_init(p, args);
316 uc_udebug_pcap_write_block(p);
317
318 return uc_resource_new(pcap_type, p);
319 }
320
321 static uc_value_t *
322 uc_udebug_pcap_file(uc_vm_t *vm, size_t nargs)
323 {
324 uc_value_t *file = uc_fn_arg(0);
325 uc_value_t *args = uc_fn_arg(1);
326 int fd = -1;
327
328 if (ucv_type(file) == UC_STRING) {
329 fd = open(ucv_string_get(file), O_WRONLY | O_CREAT, 0644);
330 if (ftruncate(fd, 0) < 0) {
331 close(fd);
332 return NULL;
333 }
334 } else if (!file)
335 fd = STDOUT_FILENO;
336
337 return uc_debug_pcap_init(fd, args);
338 }
339
340 static uc_value_t *
341 uc_udebug_pcap_udp(uc_vm_t *vm, size_t nargs)
342 {
343 uc_value_t *host = uc_fn_arg(0);
344 uc_value_t *port = uc_fn_arg(1);
345 uc_value_t *args = uc_fn_arg(2);
346 const char *port_str;
347 int fd = -1;
348
349 if (ucv_type(host) != UC_STRING)
350 return NULL;
351
352 if (ucv_type(port) == UC_STRING)
353 port_str = ucv_string_get(port);
354 else if (ucv_type(port) == UC_INTEGER)
355 port_str = usock_port(ucv_int64_get(port));
356 else
357 return NULL;
358
359 fd = usock(USOCK_UDP, ucv_string_get(host), port_str);
360
361 return uc_debug_pcap_init(fd, args);
362 }
363
364 static struct udebug_snapshot *
365 uc_get_snapshot(uc_value_t *val)
366 {
367 return ucv_resource_data(val, "udebug.snapshot");
368 }
369
370 static uc_value_t *
371 uc_udebug_pcap_write(uc_vm_t *vm, size_t nargs)
372 {
373 struct uc_pcap *p = uc_fn_thisval("udebug.pcap");
374 uc_value_t *arg = uc_fn_arg(0);
375 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
376 struct udebug_snapshot **s;
377 struct udebug_iter it;
378
379 if (!p)
380 return NULL;
381
382 s = alloca(n * sizeof(*s));
383 if (ucv_type(arg) == UC_ARRAY)
384 for (size_t i = 0; i < n; i++) {
385 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
386 return NULL;
387 } else {
388 if ((s[0] = uc_get_snapshot(arg)) == NULL)
389 return NULL;
390 }
391
392 udebug_iter_start(&it, s, n);
393 while (udebug_iter_next(&it)) {
394 struct udebug_remote_buf *rb;
395
396 rb = udebug_remote_buf_get(&u, it.s->rbuf_idx);
397 if (!pcap_interface_is_valid(&p->pcap, rb->pcap_iface)) {
398 if (pcap_interface_rbuf_init(&p->pcap, rb))
399 continue;
400
401 uc_udebug_pcap_write_block(p);
402 }
403
404 if (pcap_snapshot_packet_init(&u, &it))
405 continue;
406
407 uc_udebug_pcap_write_block(p);
408 }
409
410 return NULL;
411 }
412
413 static void
414 uc_udebug_pcap_free(void *ptr)
415 {
416 struct uc_pcap *p = ptr;
417
418 if (!p)
419 return;
420
421 if (p->fd >= 0)
422 close(p->fd);
423 free(p);
424 }
425
426 static uc_value_t *
427 uc_udebug_pcap_close(uc_vm_t *vm, size_t nargs)
428 {
429 void **p = uc_fn_this("udebug.pcap");
430
431 if (!p)
432 return NULL;
433
434 uc_udebug_pcap_free(*p);
435 *p = NULL;
436
437 return NULL;
438 }
439
440 static uc_value_t *
441 uc_udebug_snapshot_get_ring(uc_vm_t *vm, size_t nargs)
442 {
443 struct udebug_snapshot *s = uc_fn_thisval("udebug.snapshot");
444 struct udebug_remote_buf *rb;
445 uintptr_t idx;
446
447 if (!s)
448 return NULL;
449
450 rb = udebug_remote_buf_get(&u, s->rbuf_idx);
451 if (!rb)
452 return NULL;
453
454 idx = (uintptr_t)rb->priv;
455 return ucv_array_get(registry, idx);
456 }
457
458 static uc_value_t *
459 uc_udebug_foreach_packet(uc_vm_t *vm, size_t nargs)
460 {
461 uc_value_t *arg = uc_fn_arg(0);
462 uc_value_t *fn = uc_fn_arg(1);
463 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
464 struct udebug_snapshot **s;
465 struct udebug_iter it;
466
467 if (!ucv_is_callable(fn))
468 return NULL;
469
470 s = alloca(n * sizeof(*s));
471 if (ucv_type(arg) == UC_ARRAY)
472 for (size_t i = 0; i < n; i++) {
473 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
474 return NULL;
475 } else {
476 if ((s[0] = uc_get_snapshot(arg)) == NULL)
477 return NULL;
478 }
479
480 udebug_iter_start(&it, s, n);
481 while (udebug_iter_next(&it)) {
482 uc_value_t *s_obj;
483
484 if (ucv_type(arg) == UC_ARRAY)
485 s_obj = ucv_array_get(arg, it.s_idx);
486 else
487 s_obj = arg;
488
489 uc_vm_stack_push(vm, ucv_get(_uc_fn_this_res(vm)));
490 uc_vm_stack_push(vm, ucv_get(fn));
491 uc_vm_stack_push(vm, ucv_get(s_obj));
492 uc_vm_stack_push(vm, ucv_string_new_length(it.data, it.len));
493
494 if (uc_vm_call(vm, true, 2) != EXCEPTION_NONE)
495 break;
496
497 ucv_put(uc_vm_stack_pop(vm));
498 }
499
500 return NULL;
501 }
502
503 static uc_value_t *
504 uc_udebug_create_ring(uc_vm_t *vm, size_t nargs)
505 {
506 uc_value_t *name, *flags_arr, *size, *entries;
507 uc_value_t *meta_obj = uc_fn_arg(0);
508 struct udebug_buf_flag *flags;
509 struct udebug_buf_meta *meta;
510 struct udebug_buf *buf;
511 size_t flag_str_len = 0;
512 size_t flags_len = 0;
513 char *name_buf, *flag_name_buf;
514
515 if (ucv_type(meta_obj) != UC_OBJECT)
516 return NULL;
517
518 name = ucv_object_get(meta_obj, "name", NULL);
519 flags_arr = ucv_object_get(meta_obj, "flags", NULL);
520 size = ucv_object_get(meta_obj, "size", NULL);
521 entries = ucv_object_get(meta_obj, "entries", NULL);
522
523 if (ucv_type(name) != UC_STRING ||
524 ucv_type(size) != UC_INTEGER || ucv_type(entries) != UC_INTEGER)
525 return NULL;
526
527 if (ucv_type(flags_arr) == UC_ARRAY) {
528 flags_len = ucv_array_length(flags_arr);
529 for (size_t i = 0; i < flags_len; i++) {
530 uc_value_t *f = ucv_array_get(flags_arr, i);
531 if (ucv_type(f) != UC_STRING)
532 return NULL;
533 flag_str_len += strlen(ucv_string_get(f)) + 1;
534 }
535 }
536
537 buf = calloc_a(sizeof(*buf),
538 &name_buf, strlen(ucv_string_get(name)) + 1,
539 &meta, sizeof(meta),
540 &flags, flags_len * sizeof(*flags),
541 &flag_name_buf, flag_str_len);
542 meta->name = strcpy(name_buf, ucv_string_get(name));
543 meta->format = UDEBUG_FORMAT_STRING;
544 meta->flags = flags;
545
546 for (size_t i = 0; i < flags_len; i++) {
547 uc_value_t *f = ucv_array_get(flags_arr, i);
548 const char *str = ucv_string_get(f);
549 size_t len = strlen(str) + 1;
550
551 flags->name = memcpy(name_buf, str, len);
552 flags->mask = 1ULL << i;
553 name_buf += len;
554 meta->n_flags++;
555 }
556
557 if (udebug_buf_init(buf, ucv_int64_get(size), ucv_int64_get(entries))) {
558 free(buf);
559 return NULL;
560 }
561
562 udebug_buf_add(&u, buf, meta);
563
564 return uc_resource_new(wbuf_type, buf);
565 }
566
567 static void wbuf_free(void *ptr)
568 {
569 if (!ptr)
570 return;
571
572 udebug_buf_free(ptr);
573 free(ptr);
574 }
575
576 static uc_value_t *
577 uc_udebug_wbuf_flags(uc_vm_t *vm, size_t nargs)
578 {
579 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
580
581 if (!buf)
582 return NULL;
583
584 return ucv_int64_new(udebug_buf_flags(buf));
585 }
586
587 static uc_value_t *
588 uc_udebug_wbuf_close(uc_vm_t *vm, size_t nargs)
589 {
590 void **p = uc_fn_this("udebug.wbuf");
591
592 if (!p)
593 return NULL;
594
595 wbuf_free(*p);
596 *p = NULL;
597
598 return NULL;
599 }
600
601 static void
602 uc_udebug_wbuf_add_string(struct udebug_buf *buf, uc_value_t *val)
603 {
604 udebug_entry_init(buf);
605 udebug_entry_append(buf, ucv_string_get(val), ucv_string_length(val));
606 udebug_entry_add(buf);
607 }
608
609 static uc_value_t *
610 uc_udebug_wbuf_add(uc_vm_t *vm, size_t nargs)
611 {
612 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
613 uc_value_t *arg = uc_fn_arg(0);
614
615 if (!buf || ucv_type(arg) != UC_STRING)
616 return NULL;
617
618 uc_udebug_wbuf_add_string(buf, arg);
619
620 return ucv_boolean_new(true);
621 }
622
623 static const uc_function_list_t pcap_fns[] = {
624 { "close", uc_udebug_pcap_close },
625 { "write", uc_udebug_pcap_write },
626 };
627
628 static const uc_function_list_t snapshot_fns[] = {
629 { "get_ring", uc_udebug_snapshot_get_ring }
630 };
631
632 static const uc_function_list_t wbuf_fns[] = {
633 { "add", uc_udebug_wbuf_add },
634 { "flags", uc_udebug_wbuf_flags },
635 { "close", uc_udebug_wbuf_close },
636 };
637
638 static const uc_function_list_t rbuf_fns[] = {
639 { "set_poll_cb", uc_udebug_rbuf_set_poll_cb },
640 { "fetch", uc_udebug_rbuf_fetch },
641 { "change_flags", uc_udebug_rbuf_change_flags },
642 { "get_flags", uc_udebug_rbuf_get_flags },
643 { "set_fetch_duration", uc_udebug_rbuf_set_fetch_duration },
644 { "set_fetch_count", uc_udebug_rbuf_set_fetch_count },
645 { "close", uc_udebug_rbuf_close },
646 };
647
648 static const uc_function_list_t global_fns[] = {
649 { "init", uc_udebug_init },
650 { "create_ring", uc_udebug_create_ring },
651 { "get_ring", uc_udebug_get_ring },
652 { "pcap_file", uc_udebug_pcap_file },
653 { "pcap_udp", uc_udebug_pcap_udp },
654 { "foreach_packet", uc_udebug_foreach_packet },
655 };
656
657 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
658 {
659 _vm = vm;
660 uc_function_list_register(scope, global_fns);
661
662 wbuf_type = uc_type_declare(vm, "udebug.wbuf", wbuf_fns, wbuf_free);
663 rbuf_type = uc_type_declare(vm, "udebug.rbuf", rbuf_fns, rbuf_free);
664 snapshot_type = uc_type_declare(vm, "udebug.snapshot", snapshot_fns, free);
665 pcap_type = uc_type_declare(vm, "udebug.pcap", pcap_fns, uc_udebug_pcap_free);
666
667 registry = ucv_array_new(vm);
668 uc_vm_registry_set(vm, "udebug.registry", registry);
669 }