4420658b4f5e2498b792496a9f45e24a70c0693e
[project/rpcd.git] / luci2.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <arpa/inet.h>
26 #include <signal.h>
27
28 #include "luci2.h"
29
30 static struct blob_buf buf;
31 static struct uci_context *cursor;
32
33 enum {
34         RPC_S_PID,
35         RPC_S_SIGNAL,
36         __RPC_S_MAX,
37 };
38
39 static const struct blobmsg_policy rpc_signal_policy[__RPC_S_MAX] = {
40         [RPC_S_PID]    = { .name = "pid",    .type = BLOBMSG_TYPE_INT32 },
41         [RPC_S_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
42 };
43
44
45 static int
46 rpc_errno_status(void)
47 {
48         switch (errno)
49         {
50         case EACCES:
51                 return UBUS_STATUS_PERMISSION_DENIED;
52
53         case ENOTDIR:
54                 return UBUS_STATUS_INVALID_ARGUMENT;
55
56         case ENOENT:
57                 return UBUS_STATUS_NOT_FOUND;
58
59         case EINVAL:
60                 return UBUS_STATUS_INVALID_ARGUMENT;
61
62         default:
63                 return UBUS_STATUS_UNKNOWN_ERROR;
64         }
65 }
66
67 static void
68 log_read(FILE *log, int logsize)
69 {
70         int len;
71         char *logbuf;
72
73         if (logsize == 0)
74                 logsize = RPC_LUCI2_DEF_LOGSIZE;
75
76         len = (logsize > RPC_LUCI2_MAX_LOGSIZE) ? RPC_LUCI2_MAX_LOGSIZE : logsize;
77         logbuf = blobmsg_alloc_string_buffer(&buf, "log", len + 1);
78
79         if (!logbuf)
80                 return;
81
82         while (logsize > RPC_LUCI2_MAX_LOGSIZE)
83         {
84                 len = logsize % RPC_LUCI2_MAX_LOGSIZE;
85
86                 if (len == 0)
87                         len = RPC_LUCI2_MAX_LOGSIZE;
88
89                 fread(logbuf, 1, len, log);
90                 logsize -= len;
91         }
92
93         len = fread(logbuf, 1, logsize, log);
94         *(logbuf + len) = 0;
95
96         blobmsg_add_string_buffer(&buf);
97 }
98
99 static int
100 rpc_luci2_system_log(struct ubus_context *ctx, struct ubus_object *obj,
101                      struct ubus_request_data *req, const char *method,
102                      struct blob_attr *msg)
103 {
104         FILE *log;
105         int logsize = 0;
106         const char *logfile = NULL;
107         struct stat st;
108         struct uci_package *p;
109         struct uci_element *e;
110         struct uci_section *s;
111         struct uci_ptr ptr = { .package = "system" };
112
113         uci_load(cursor, ptr.package, &p);
114
115         if (!p)
116                 return UBUS_STATUS_NOT_FOUND;
117
118         uci_foreach_element(&p->sections, e)
119         {
120                 s = uci_to_section(e);
121
122                 if (strcmp(s->type, "system"))
123                         continue;
124
125                 ptr.o = NULL;
126                 ptr.option = "log_type";
127                 ptr.section = e->name;
128                 uci_lookup_ptr(cursor, &ptr, NULL, true);
129                 break;
130         }
131
132         if (ptr.o && ptr.o->type == UCI_TYPE_STRING &&
133             !strcmp(ptr.o->v.string, "file"))
134         {
135                 ptr.o = NULL;
136                 ptr.option = "log_file";
137                 uci_lookup_ptr(cursor, &ptr, NULL, true);
138
139                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
140                         logfile = ptr.o->v.string;
141                 else
142                         logfile = "/var/log/messages";
143
144                 if (stat(logfile, &st) || !(log = fopen(logfile, "r")))
145                         goto fail;
146
147                 logsize = st.st_size;
148         }
149         else
150         {
151                 ptr.o = NULL;
152                 ptr.option = "log_size";
153                 uci_lookup_ptr(cursor, &ptr, NULL, true);
154
155                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
156                         logsize = atoi(ptr.o->v.string) * 1024;
157
158                 if (!(log = popen("logread", "r")))
159                         goto fail;
160         }
161
162         blob_buf_init(&buf, 0);
163
164         log_read(log, logsize);
165         fclose(log);
166
167         uci_unload(cursor, p);
168         ubus_send_reply(ctx, req, buf.head);
169         return 0;
170
171 fail:
172         uci_unload(cursor, p);
173         return rpc_errno_status();
174 }
175
176 static int
177 rpc_luci2_system_dmesg(struct ubus_context *ctx, struct ubus_object *obj,
178                        struct ubus_request_data *req, const char *method,
179                        struct blob_attr *msg)
180 {
181         FILE *log;
182
183         if (!(log = popen("dmesg", "r")))
184                 return rpc_errno_status();
185
186         blob_buf_init(&buf, 0);
187
188         log_read(log, RPC_LUCI2_MAX_LOGSIZE);
189         fclose(log);
190
191         ubus_send_reply(ctx, req, buf.head);
192         return 0;
193 }
194
195 static int
196 rpc_luci2_process_list(struct ubus_context *ctx, struct ubus_object *obj,
197                        struct ubus_request_data *req, const char *method,
198                        struct blob_attr *msg)
199 {
200         FILE *top;
201         void *c, *d;
202         char line[1024];
203         char *pid, *ppid, *user, *stat, *vsz, *pvsz, *pcpu, *cmd;
204
205         if (!(top = popen("/bin/busybox top -bn1", "r")))
206                 return rpc_errno_status();
207
208         blob_buf_init(&buf, 0);
209         c = blobmsg_open_array(&buf, "processes");
210
211         while (fgets(line, sizeof(line) - 1, top))
212         {
213                 pid  = strtok(line, " ");
214
215                 if (*pid < '0' || *pid > '9')
216                         continue;
217
218                 ppid = strtok(NULL, " ");
219                 user = strtok(NULL, " ");
220                 stat = strtok(NULL, " ");
221
222                 if (!stat)
223                         continue;
224
225                 if (!*(stat + 1))
226                         *(stat + 1) = ' ';
227
228                 if (!*(stat + 2))
229                         *(stat + 2) = ' ';
230
231                 *(stat + 3) = 0;
232
233                 vsz  = strtok(stat + 4, " ");
234                 pvsz = strtok(NULL, " ");
235                 pcpu = strtok(NULL, " ");
236                 cmd  = strtok(NULL, "\n");
237
238                 if (!cmd)
239                         continue;
240
241                 d = blobmsg_open_table(&buf, NULL);
242
243                 blobmsg_add_u32(&buf, "pid", atoi(pid));
244                 blobmsg_add_u32(&buf, "ppid", atoi(ppid));
245                 blobmsg_add_string(&buf, "user", user);
246                 blobmsg_add_string(&buf, "stat", stat);
247                 blobmsg_add_u32(&buf, "vsize", atoi(vsz) * 1024);
248                 blobmsg_add_u32(&buf, "vsize_percent", atoi(pvsz));
249                 blobmsg_add_u32(&buf, "cpu_percent", atoi(pcpu));
250                 blobmsg_add_string(&buf, "command", cmd);
251
252                 blobmsg_close_table(&buf, d);
253         }
254
255         fclose(top);
256         blobmsg_close_array(&buf, c);
257
258         ubus_send_reply(ctx, req, buf.head);
259         return 0;
260 }
261
262 static int
263 rpc_luci2_process_signal(struct ubus_context *ctx, struct ubus_object *obj,
264                          struct ubus_request_data *req, const char *method,
265                          struct blob_attr *msg)
266 {
267         int pid, sig;
268         struct blob_attr *tb[__RPC_S_MAX];
269
270         blobmsg_parse(rpc_signal_policy, __RPC_S_MAX, tb,
271                       blob_data(msg), blob_len(msg));
272
273         if (!tb[RPC_S_SIGNAL] || !tb[RPC_S_PID])
274         {
275                 errno = EINVAL;
276                 return rpc_errno_status();
277         }
278
279         pid = blobmsg_get_u32(tb[RPC_S_PID]);
280         sig = blobmsg_get_u32(tb[RPC_S_SIGNAL]);
281
282         if (kill(pid, sig))
283                 return rpc_errno_status();
284
285         return 0;
286 }
287
288
289 static FILE *
290 dnsmasq_leasefile(void)
291 {
292         FILE *leases = NULL;
293         struct uci_package *p;
294         struct uci_element *e;
295         struct uci_section *s;
296         struct uci_ptr ptr = {
297                 .package = "dhcp",
298                 .section = NULL,
299                 .option  = "leasefile"
300         };
301
302         uci_load(cursor, ptr.package, &p);
303
304         if (!p)
305                 return NULL;
306
307         uci_foreach_element(&p->sections, e)
308         {
309                 s = uci_to_section(e);
310
311                 if (strcmp(s->type, "dnsmasq"))
312                         continue;
313
314                 ptr.section = e->name;
315                 uci_lookup_ptr(cursor, &ptr, NULL, true);
316                 break;
317         }
318
319         if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
320                 leases = fopen(ptr.o->v.string, "r");
321
322         uci_unload(cursor, p);
323
324         return leases;
325 }
326
327 static int
328 rpc_luci2_network_leases(struct ubus_context *ctx, struct ubus_object *obj,
329                          struct ubus_request_data *req, const char *method,
330                          struct blob_attr *msg)
331 {
332         FILE *leases;
333         void *c, *d;
334         char line[128];
335         char *ts, *mac, *addr, *name;
336         time_t now = time(NULL);
337
338         blob_buf_init(&buf, 0);
339         c = blobmsg_open_array(&buf, "leases");
340
341         leases = dnsmasq_leasefile();
342
343         if (!leases)
344                 goto out;
345
346         while (fgets(line, sizeof(line) - 1, leases))
347         {
348                 ts   = strtok(line, " \t");
349                 mac  = strtok(NULL, " \t");
350                 addr = strtok(NULL, " \t");
351                 name = strtok(NULL, " \t");
352
353                 if (!ts || !mac || !addr || !name)
354                         continue;
355
356                 if (strchr(addr, ':'))
357                         continue;
358
359                 d = blobmsg_open_table(&buf, NULL);
360
361                 blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
362                 blobmsg_add_string(&buf, "macaddr", mac);
363                 blobmsg_add_string(&buf, "ipaddr", addr);
364
365                 if (strcmp(name, "*"))
366                         blobmsg_add_string(&buf, "hostname", name);
367
368                 blobmsg_close_table(&buf, d);
369         }
370
371         fclose(leases);
372
373 out:
374         blobmsg_close_array(&buf, c);
375         ubus_send_reply(ctx, req, buf.head);
376
377         return 0;
378 }
379
380 static int
381 rpc_luci2_network_leases6(struct ubus_context *ctx, struct ubus_object *obj,
382                           struct ubus_request_data *req, const char *method,
383                           struct blob_attr *msg)
384 {
385         FILE *leases;
386         void *c, *d;
387         char line[128];
388         char *ts, *mac, *addr, *name, *duid;
389         time_t now = time(NULL);
390
391         blob_buf_init(&buf, 0);
392         c = blobmsg_open_array(&buf, "leases");
393
394         leases = fopen("/tmp/hosts/6relayd", "r");
395
396         if (leases)
397         {
398                 while (fgets(line, sizeof(line) - 1, leases))
399                 {
400                         if (strncmp(line, "# ", 2))
401                                 continue;
402
403                         strtok(line + 2, " \t"); /* iface */
404
405                         duid = strtok(NULL, " \t");
406
407                         strtok(NULL, " \t"); /* iaid */
408
409                         name = strtok(NULL, " \t");
410                         ts   = strtok(NULL, " \t");
411
412                         strtok(NULL, " \t"); /* id */
413                         strtok(NULL, " \t"); /* length */
414
415                         addr = strtok(NULL, " \t\n");
416
417                         if (!addr)
418                                 continue;
419
420                         d = blobmsg_open_table(&buf, NULL);
421
422                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
423                         blobmsg_add_string(&buf, "duid", duid);
424                         blobmsg_add_string(&buf, "ip6addr", addr);
425
426                         if (strcmp(name, "-"))
427                                 blobmsg_add_string(&buf, "hostname", name);
428
429                         blobmsg_close_array(&buf, d);
430                 }
431
432                 fclose(leases);
433         }
434         else
435         {
436                 leases = dnsmasq_leasefile();
437
438                 if (!leases)
439                         goto out;
440
441                 while (fgets(line, sizeof(line) - 1, leases))
442                 {
443                         ts   = strtok(line, " \t");
444                         mac  = strtok(NULL, " \t");
445                         addr = strtok(NULL, " \t");
446                         name = strtok(NULL, " \t");
447                         duid = strtok(NULL, " \t\n");
448
449                         if (!ts || !mac || !addr || !duid)
450                                 continue;
451
452                         if (!strchr(addr, ':'))
453                                 continue;
454
455                         d = blobmsg_open_table(&buf, NULL);
456
457                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
458                         blobmsg_add_string(&buf, "macaddr", mac);
459                         blobmsg_add_string(&buf, "ip6addr", addr);
460
461                         if (strcmp(name, "*"))
462                                 blobmsg_add_string(&buf, "hostname", name);
463
464                         if (strcmp(duid, "*"))
465                                 blobmsg_add_string(&buf, "duid", name);
466
467                         blobmsg_close_table(&buf, d);
468                 }
469
470                 fclose(leases);
471         }
472
473 out:
474         blobmsg_close_array(&buf, c);
475         ubus_send_reply(ctx, req, buf.head);
476
477         return 0;
478 }
479
480 static int
481 rpc_luci2_network_ct_count(struct ubus_context *ctx, struct ubus_object *obj,
482                            struct ubus_request_data *req, const char *method,
483                            struct blob_attr *msg)
484 {
485         FILE *f;
486         char line[128];
487
488         blob_buf_init(&buf, 0);
489
490         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_count", "r")) != NULL)
491         {
492                 if (fgets(line, sizeof(line) - 1, f))
493                         blobmsg_add_u32(&buf, "count", atoi(line));
494
495                 fclose(f);
496         }
497
498         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_max", "r")) != NULL)
499         {
500                 if (fgets(line, sizeof(line) - 1, f))
501                         blobmsg_add_u32(&buf, "limit", atoi(line));
502
503                 fclose(f);
504         }
505
506         ubus_send_reply(ctx, req, buf.head);
507
508         return 0;
509 }
510
511 static int
512 rpc_luci2_network_ct_table(struct ubus_context *ctx, struct ubus_object *obj,
513                            struct ubus_request_data *req, const char *method,
514                            struct blob_attr *msg)
515 {
516         FILE *f;
517         int i;
518         void *c, *d;
519         char *p, line[512];
520         bool seen[6];
521
522         blob_buf_init(&buf, 0);
523         c = blobmsg_open_array(&buf, "entries");
524
525         if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL)
526         {
527                 while (fgets(line, sizeof(line) - 1, f))
528                 {
529                         d = blobmsg_open_table(&buf, NULL);
530                         memset(seen, 0, sizeof(seen));
531
532                         for (i = 0, p = strtok(line, " "); p; i++, p = strtok(NULL, " "))
533                         {
534                                 if (i == 0)
535                                         blobmsg_add_u8(&buf, "ipv6", !strcmp(p, "ipv6"));
536                                 else if (i == 3)
537                                         blobmsg_add_u32(&buf, "protocol", atoi(p));
538                                 else if (i == 4)
539                                         blobmsg_add_u32(&buf, "expires", atoi(p));
540                                 else if (i >= 5)
541                                 {
542                                         if (*p == '[')
543                                                 continue;
544
545                                         if (!seen[0] && !strncmp(p, "src=", 4))
546                                         {
547                                                 blobmsg_add_string(&buf, "src", p + 4);
548                                                 seen[0] = true;
549                                         }
550                                         else if (!seen[1] && !strncmp(p, "dst=", 4))
551                                         {
552                                                 blobmsg_add_string(&buf, "dest", p + 4);
553                                                 seen[1] = true;
554                                         }
555                                         else if (!seen[2] && !strncmp(p, "sport=", 6))
556                                         {
557                                                 blobmsg_add_u32(&buf, "sport", atoi(p + 6));
558                                                 seen[2] = true;
559                                         }
560                                         else if (!seen[3] && !strncmp(p, "dport=", 6))
561                                         {
562                                                 blobmsg_add_u32(&buf, "dport", atoi(p + 6));
563                                                 seen[3] = true;
564                                         }
565                                         else if (!strncmp(p, "packets=", 8))
566                                         {
567                                                 blobmsg_add_u32(&buf,
568                                                                 seen[4] ? "tx_packets" : "rx_packets",
569                                                                 atoi(p + 8));
570                                                 seen[4] = true;
571                                         }
572                                         else if (!strncmp(p, "bytes=", 6))
573                                         {
574                                                 blobmsg_add_u32(&buf,
575                                                                                 seen[5] ? "tx_bytes" : "rx_bytes",
576                                                                 atoi(p + 6));
577                                                 seen[5] = true;
578                                         }
579                                 }
580                         }
581
582                         blobmsg_close_table(&buf, d);
583                 }
584
585                 fclose(f);
586         }
587
588         blobmsg_close_array(&buf, c);
589         ubus_send_reply(ctx, req, buf.head);
590
591         return 0;
592 }
593
594 static int
595 rpc_luci2_network_arp_table(struct ubus_context *ctx, struct ubus_object *obj,
596                             struct ubus_request_data *req, const char *method,
597                             struct blob_attr *msg)
598 {
599         FILE *f;
600         void *c, *d;
601         char *addr, *mac, *dev, line[128];
602
603         blob_buf_init(&buf, 0);
604         c = blobmsg_open_array(&buf, "entries");
605
606         if ((f = fopen("/proc/net/arp", "r")) != NULL)
607         {
608                 /* skip header line */
609                 fgets(line, sizeof(line) - 1, f);
610
611                 while (fgets(line, sizeof(line) - 1, f))
612                 {
613                         addr = strtok(line, " \t");
614
615                         strtok(NULL, " \t"); /* HW type */
616                         strtok(NULL, " \t"); /* Flags */
617
618                         mac = strtok(NULL, " \t");
619
620                         strtok(NULL, " \t"); /* Mask */
621
622                         dev = strtok(NULL, " \t\n");
623
624                         if (!dev)
625                                 continue;
626
627                         d = blobmsg_open_table(&buf, NULL);
628                         blobmsg_add_string(&buf, "ipaddr", addr);
629                         blobmsg_add_string(&buf, "macaddr", mac);
630                         blobmsg_add_string(&buf, "device", dev);
631                         blobmsg_close_table(&buf, d);
632                 }
633
634                 fclose(f);
635         }
636
637         blobmsg_close_array(&buf, c);
638         ubus_send_reply(ctx, req, buf.head);
639
640         return 0;
641 }
642
643 static void
644 put_hexaddr(const char *name, const char *s, const char *m)
645 {
646         int bits;
647         struct in_addr a;
648         char as[sizeof("255.255.255.255/32\0")];
649
650         a.s_addr = strtoul(s, NULL, 16);
651         inet_ntop(AF_INET, &a, as, sizeof(as));
652
653         if (m)
654         {
655                 for (a.s_addr = ntohl(strtoul(m, NULL, 16)), bits = 0;
656                      a.s_addr & 0x80000000;
657                      a.s_addr <<= 1)
658                         bits++;
659
660                 sprintf(as + strlen(as), "/%u", bits);
661         }
662
663         blobmsg_add_string(&buf, name, as);
664 }
665
666 static int
667 rpc_luci2_network_routes(struct ubus_context *ctx, struct ubus_object *obj,
668                          struct ubus_request_data *req, const char *method,
669                          struct blob_attr *msg)
670 {
671         FILE *routes;
672         void *c, *d;
673         char *dst, *dmask, *next, *metric, *device;
674         char line[256];
675         unsigned int n;
676
677         if (!(routes = fopen("/proc/net/route", "r")))
678                 return rpc_errno_status();
679
680         blob_buf_init(&buf, 0);
681         c = blobmsg_open_array(&buf, "routes");
682
683         /* skip header line */
684         fgets(line, sizeof(line) - 1, routes);
685
686         while (fgets(line, sizeof(line) - 1, routes))
687         {
688                 device = strtok(line, "\t ");
689                 dst    = strtok(NULL, "\t ");
690                 next   = strtok(NULL, "\t ");
691
692                 strtok(NULL, "\t "); /* flags */
693                 strtok(NULL, "\t "); /* refcount */
694                 strtok(NULL, "\t "); /* usecount */
695
696                 metric = strtok(NULL, "\t ");
697                 dmask  = strtok(NULL, "\t ");
698
699                 if (!dmask)
700                         continue;
701
702                 d = blobmsg_open_table(&buf, NULL);
703
704                 put_hexaddr("target", dst, dmask);
705                 put_hexaddr("nexthop", next, NULL);
706
707                 n = strtoul(metric, NULL, 10);
708                 blobmsg_add_u32(&buf, "metric", n);
709
710                 blobmsg_add_string(&buf, "device", device);
711
712                 blobmsg_close_table(&buf, d);
713         }
714
715         blobmsg_close_array(&buf, c);
716         fclose(routes);
717
718         ubus_send_reply(ctx, req, buf.head);
719         return 0;
720 }
721
722 static void
723 put_hex6addr(const char *name, const char *s, const char *m)
724 {
725         int i;
726         struct in6_addr a;
727         char as[INET6_ADDRSTRLEN + sizeof("/128")];
728
729 #define hex(x) \
730         (((x) <= '9') ? ((x) - '0') : \
731                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
732                         ((x) - 'a' + 10)))
733
734         for (i = 0; i < 16; i++, s += 2)
735                 a.s6_addr[i] = (16 * hex(*s)) + hex(*(s+1));
736
737         inet_ntop(AF_INET6, &a, as, sizeof(as));
738
739         if (m)
740                 sprintf(as + strlen(as), "/%lu", strtoul(m, NULL, 16));
741
742         blobmsg_add_string(&buf, name, as);
743 }
744
745 static int
746 rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj,
747                           struct ubus_request_data *req, const char *method,
748                           struct blob_attr *msg)
749 {
750         FILE *routes;
751         void *c, *d;
752         char *src, *smask, *dst, *dmask, *next, *metric, *flags, *device;
753         char line[256];
754         unsigned int n;
755
756         if (!(routes = fopen("/proc/net/ipv6_route", "r")))
757                 return rpc_errno_status();
758
759         blob_buf_init(&buf, 0);
760         c = blobmsg_open_array(&buf, "routes");
761
762         while (fgets(line, sizeof(line) - 1, routes))
763         {
764                 dst    = strtok(line, " ");
765                 dmask  = strtok(NULL, " ");
766                 src    = strtok(NULL, " ");
767                 smask  = strtok(NULL, " ");
768                 next   = strtok(NULL, " ");
769                 metric = strtok(NULL, " ");
770
771                 strtok(NULL, " "); /* refcount */
772                 strtok(NULL, " "); /* usecount */
773
774                 flags  = strtok(NULL, " ");
775                 device = strtok(NULL, " \n");
776
777                 if (!device)
778                         continue;
779
780                 n = strtoul(flags, NULL, 16);
781
782                 if (!(n & 1))
783                         continue;
784
785                 d = blobmsg_open_table(&buf, NULL);
786
787                 put_hex6addr("target", dst, dmask);
788                 put_hex6addr("source", src, smask);
789                 put_hex6addr("nexthop", next, NULL);
790
791                 n = strtoul(metric, NULL, 16);
792                 blobmsg_add_u32(&buf, "metric", n);
793
794                 blobmsg_add_string(&buf, "device", device);
795
796                 blobmsg_close_table(&buf, d);
797         }
798
799         blobmsg_close_array(&buf, c);
800         fclose(routes);
801
802         ubus_send_reply(ctx, req, buf.head);
803         return 0;
804 }
805
806
807 int rpc_luci2_api_init(struct ubus_context *ctx)
808 {
809         int rv = 0;
810
811         static const struct ubus_method luci2_system_methods[] = {
812                 UBUS_METHOD_NOARG("syslog",       rpc_luci2_system_log),
813                 UBUS_METHOD_NOARG("dmesg",        rpc_luci2_system_dmesg),
814                 UBUS_METHOD_NOARG("process_list", rpc_luci2_process_list),
815                 UBUS_METHOD("process_signal",     rpc_luci2_process_signal,
816                                                   rpc_signal_policy),
817         };
818
819         static struct ubus_object_type luci2_system_type =
820                 UBUS_OBJECT_TYPE("luci-rpc-luci2-system", luci2_system_methods);
821
822         static struct ubus_object system_obj = {
823                 .name = "luci2.system",
824                 .type = &luci2_system_type,
825                 .methods = luci2_system_methods,
826                 .n_methods = ARRAY_SIZE(luci2_system_methods),
827         };
828
829
830         static const struct ubus_method luci2_network_methods[] = {
831                 UBUS_METHOD_NOARG("conntrack_count", rpc_luci2_network_ct_count),
832                 UBUS_METHOD_NOARG("conntrack_table", rpc_luci2_network_ct_table),
833                 UBUS_METHOD_NOARG("arp_table",       rpc_luci2_network_arp_table),
834                 UBUS_METHOD_NOARG("dhcp_leases",     rpc_luci2_network_leases),
835                 UBUS_METHOD_NOARG("dhcp6_leases",    rpc_luci2_network_leases6),
836                 UBUS_METHOD_NOARG("routes",          rpc_luci2_network_routes),
837                 UBUS_METHOD_NOARG("routes6",         rpc_luci2_network_routes6),
838         };
839
840         static struct ubus_object_type luci2_network_type =
841                 UBUS_OBJECT_TYPE("luci-rpc-luci2-network", luci2_network_methods);
842
843         static struct ubus_object network_obj = {
844                 .name = "luci2.network",
845                 .type = &luci2_network_type,
846                 .methods = luci2_network_methods,
847                 .n_methods = ARRAY_SIZE(luci2_network_methods),
848         };
849
850         cursor = uci_alloc_context();
851
852         if (!cursor)
853                 return UBUS_STATUS_UNKNOWN_ERROR;
854
855         rv |= ubus_add_object(ctx, &system_obj);
856         rv |= ubus_add_object(ctx, &network_obj);
857
858         return rv;
859 }