Add luci2 namespace to offer various backend methods required by the ui
[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
27 #include "luci2.h"
28
29 static struct blob_buf buf;
30 static struct uci_context *cursor;
31
32
33 static int
34 rpc_errno_status(void)
35 {
36         switch (errno)
37         {
38         case EACCES:
39                 return UBUS_STATUS_PERMISSION_DENIED;
40
41         case ENOTDIR:
42                 return UBUS_STATUS_INVALID_ARGUMENT;
43
44         case ENOENT:
45                 return UBUS_STATUS_NOT_FOUND;
46
47         case EINVAL:
48                 return UBUS_STATUS_INVALID_ARGUMENT;
49
50         default:
51                 return UBUS_STATUS_UNKNOWN_ERROR;
52         }
53 }
54
55 static void
56 log_read(FILE *log, int logsize)
57 {
58         int len;
59         char *logbuf;
60
61         if (logsize == 0)
62                 logsize = RPC_LUCI2_DEF_LOGSIZE;
63
64         len = (logsize > RPC_LUCI2_MAX_LOGSIZE) ? RPC_LUCI2_MAX_LOGSIZE : logsize;
65         logbuf = blobmsg_alloc_string_buffer(&buf, "log", len + 1);
66
67         if (!logbuf)
68                 return;
69
70         while (logsize > RPC_LUCI2_MAX_LOGSIZE)
71         {
72                 len = logsize % RPC_LUCI2_MAX_LOGSIZE;
73
74                 if (len == 0)
75                         len = RPC_LUCI2_MAX_LOGSIZE;
76
77                 fread(logbuf, 1, len, log);
78                 logsize -= len;
79         }
80
81         len = fread(logbuf, 1, logsize, log);
82         *(logbuf + len) = 0;
83
84         blobmsg_add_string_buffer(&buf);
85 }
86
87 static int
88 rpc_luci2_system_log(struct ubus_context *ctx, struct ubus_object *obj,
89                      struct ubus_request_data *req, const char *method,
90                      struct blob_attr *msg)
91 {
92         FILE *log;
93         int logsize = 0;
94         const char *logfile = NULL;
95         struct stat st;
96         struct uci_package *p;
97         struct uci_element *e;
98         struct uci_section *s;
99         struct uci_ptr ptr = { .package = "system" };
100
101         uci_load(cursor, ptr.package, &p);
102
103         if (!p)
104                 return UBUS_STATUS_NOT_FOUND;
105
106         uci_foreach_element(&p->sections, e)
107         {
108                 s = uci_to_section(e);
109
110                 if (strcmp(s->type, "system"))
111                         continue;
112
113                 ptr.o = NULL;
114                 ptr.option = "log_type";
115                 ptr.section = e->name;
116                 uci_lookup_ptr(cursor, &ptr, NULL, true);
117                 break;
118         }
119
120         if (ptr.o && ptr.o->type == UCI_TYPE_STRING &&
121             !strcmp(ptr.o->v.string, "file"))
122         {
123                 ptr.o = NULL;
124                 ptr.option = "log_file";
125                 uci_lookup_ptr(cursor, &ptr, NULL, true);
126
127                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
128                         logfile = ptr.o->v.string;
129                 else
130                         logfile = "/var/log/messages";
131
132                 if (stat(logfile, &st) || !(log = fopen(logfile, "r")))
133                         goto fail;
134
135                 logsize = st.st_size;
136         }
137         else
138         {
139                 ptr.o = NULL;
140                 ptr.option = "log_size";
141                 uci_lookup_ptr(cursor, &ptr, NULL, true);
142
143                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
144                         logsize = atoi(ptr.o->v.string) * 1024;
145
146                 if (!(log = popen("logread", "r")))
147                         goto fail;
148         }
149
150         blob_buf_init(&buf, 0);
151
152         log_read(log, logsize);
153         fclose(log);
154
155         uci_unload(cursor, p);
156         ubus_send_reply(ctx, req, buf.head);
157         return 0;
158
159 fail:
160         uci_unload(cursor, p);
161         return rpc_errno_status();
162 }
163
164 static int
165 rpc_luci2_system_dmesg(struct ubus_context *ctx, struct ubus_object *obj,
166                        struct ubus_request_data *req, const char *method,
167                        struct blob_attr *msg)
168 {
169         FILE *log;
170
171         if (!(log = popen("dmesg", "r")))
172                 return rpc_errno_status();
173
174         blob_buf_init(&buf, 0);
175
176         log_read(log, RPC_LUCI2_MAX_LOGSIZE);
177         fclose(log);
178
179         ubus_send_reply(ctx, req, buf.head);
180         return 0;
181 }
182
183
184 static FILE *
185 dnsmasq_leasefile(void)
186 {
187         FILE *leases = NULL;
188         struct uci_package *p;
189         struct uci_element *e;
190         struct uci_section *s;
191         struct uci_ptr ptr = {
192                 .package = "dhcp",
193                 .section = NULL,
194                 .option  = "leasefile"
195         };
196
197         uci_load(cursor, ptr.package, &p);
198
199         if (!p)
200                 return NULL;
201
202         uci_foreach_element(&p->sections, e)
203         {
204                 s = uci_to_section(e);
205
206                 if (strcmp(s->type, "dnsmasq"))
207                         continue;
208
209                 ptr.section = e->name;
210                 uci_lookup_ptr(cursor, &ptr, NULL, true);
211                 break;
212         }
213
214         if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
215                 leases = fopen(ptr.o->v.string, "r");
216
217         uci_unload(cursor, p);
218
219         return leases;
220 }
221
222 static int
223 rpc_luci2_network_leases(struct ubus_context *ctx, struct ubus_object *obj,
224                          struct ubus_request_data *req, const char *method,
225                          struct blob_attr *msg)
226 {
227         FILE *leases;
228         void *c, *d;
229         char line[128];
230         char *ts, *mac, *addr, *name;
231         time_t now = time(NULL);
232
233         blob_buf_init(&buf, 0);
234         c = blobmsg_open_array(&buf, "leases");
235
236         leases = dnsmasq_leasefile();
237
238         if (!leases)
239                 goto out;
240
241         while (fgets(line, sizeof(line) - 1, leases))
242         {
243                 ts   = strtok(line, " \t");
244                 mac  = strtok(NULL, " \t");
245                 addr = strtok(NULL, " \t");
246                 name = strtok(NULL, " \t");
247
248                 if (!ts || !mac || !addr || !name)
249                         continue;
250
251                 if (strchr(addr, ':'))
252                         continue;
253
254                 d = blobmsg_open_table(&buf, NULL);
255
256                 blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
257                 blobmsg_add_string(&buf, "macaddr", mac);
258                 blobmsg_add_string(&buf, "ipaddr", addr);
259
260                 if (strcmp(name, "*"))
261                         blobmsg_add_string(&buf, "hostname", name);
262
263                 blobmsg_close_table(&buf, d);
264         }
265
266         fclose(leases);
267
268 out:
269         blobmsg_close_array(&buf, c);
270         ubus_send_reply(ctx, req, buf.head);
271
272         return 0;
273 }
274
275 static int
276 rpc_luci2_network_leases6(struct ubus_context *ctx, struct ubus_object *obj,
277                           struct ubus_request_data *req, const char *method,
278                           struct blob_attr *msg)
279 {
280         FILE *leases;
281         void *c, *d;
282         char line[128];
283         char *ts, *mac, *addr, *name, *duid;
284         time_t now = time(NULL);
285
286         blob_buf_init(&buf, 0);
287         c = blobmsg_open_array(&buf, "leases");
288
289         leases = fopen("/tmp/hosts/6relayd", "r");
290
291         if (leases)
292         {
293                 while (fgets(line, sizeof(line) - 1, leases))
294                 {
295                         if (strncmp(line, "# ", 2))
296                                 continue;
297
298                         strtok(line + 2, " \t"); /* iface */
299
300                         duid = strtok(NULL, " \t");
301
302                         strtok(NULL, " \t"); /* iaid */
303
304                         name = strtok(NULL, " \t");
305                         ts   = strtok(NULL, " \t");
306
307                         strtok(NULL, " \t"); /* id */
308                         strtok(NULL, " \t"); /* length */
309
310                         addr = strtok(NULL, " \t\n");
311
312                         if (!addr)
313                                 continue;
314
315                         d = blobmsg_open_table(&buf, NULL);
316
317                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
318                         blobmsg_add_string(&buf, "duid", duid);
319                         blobmsg_add_string(&buf, "ip6addr", addr);
320
321                         if (strcmp(name, "-"))
322                                 blobmsg_add_string(&buf, "hostname", name);
323
324                         blobmsg_close_array(&buf, d);
325                 }
326
327                 fclose(leases);
328         }
329         else
330         {
331                 leases = dnsmasq_leasefile();
332
333                 if (!leases)
334                         goto out;
335
336                 while (fgets(line, sizeof(line) - 1, leases))
337                 {
338                         ts   = strtok(line, " \t");
339                         mac  = strtok(NULL, " \t");
340                         addr = strtok(NULL, " \t");
341                         name = strtok(NULL, " \t");
342                         duid = strtok(NULL, " \t\n");
343
344                         if (!ts || !mac || !addr || !duid)
345                                 continue;
346
347                         if (!strchr(addr, ':'))
348                                 continue;
349
350                         d = blobmsg_open_table(&buf, NULL);
351
352                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
353                         blobmsg_add_string(&buf, "macaddr", mac);
354                         blobmsg_add_string(&buf, "ip6addr", addr);
355
356                         if (strcmp(name, "*"))
357                                 blobmsg_add_string(&buf, "hostname", name);
358
359                         if (strcmp(duid, "*"))
360                                 blobmsg_add_string(&buf, "duid", name);
361
362                         blobmsg_close_table(&buf, d);
363                 }
364
365                 fclose(leases);
366         }
367
368 out:
369         blobmsg_close_array(&buf, c);
370         ubus_send_reply(ctx, req, buf.head);
371
372         return 0;
373 }
374
375 static int
376 rpc_luci2_network_ct_count(struct ubus_context *ctx, struct ubus_object *obj,
377                            struct ubus_request_data *req, const char *method,
378                            struct blob_attr *msg)
379 {
380         FILE *f;
381         char line[128];
382
383         blob_buf_init(&buf, 0);
384
385         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_count", "r")) != NULL)
386         {
387                 if (fgets(line, sizeof(line) - 1, f))
388                         blobmsg_add_u32(&buf, "count", atoi(line));
389
390                 fclose(f);
391         }
392
393         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_max", "r")) != NULL)
394         {
395                 if (fgets(line, sizeof(line) - 1, f))
396                         blobmsg_add_u32(&buf, "limit", atoi(line));
397
398                 fclose(f);
399         }
400
401         ubus_send_reply(ctx, req, buf.head);
402
403         return 0;
404 }
405
406 static int
407 rpc_luci2_network_ct_table(struct ubus_context *ctx, struct ubus_object *obj,
408                            struct ubus_request_data *req, const char *method,
409                            struct blob_attr *msg)
410 {
411         FILE *f;
412         int i;
413         void *c, *d;
414         char *p, line[512];
415         bool seen[6];
416
417         blob_buf_init(&buf, 0);
418         c = blobmsg_open_array(&buf, "entries");
419
420         if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL)
421         {
422                 while (fgets(line, sizeof(line) - 1, f))
423                 {
424                         d = blobmsg_open_table(&buf, NULL);
425                         memset(seen, 0, sizeof(seen));
426
427                         for (i = 0, p = strtok(line, " "); p; i++, p = strtok(NULL, " "))
428                         {
429                                 if (i == 0)
430                                         blobmsg_add_u8(&buf, "ipv6", !strcmp(p, "ipv6"));
431                                 else if (i == 3)
432                                         blobmsg_add_u32(&buf, "protocol", atoi(p));
433                                 else if (i == 4)
434                                         blobmsg_add_u32(&buf, "expires", atoi(p));
435                                 else if (i >= 5)
436                                 {
437                                         if (*p == '[')
438                                                 continue;
439
440                                         if (!seen[0] && !strncmp(p, "src=", 4))
441                                         {
442                                                 blobmsg_add_string(&buf, "src", p + 4);
443                                                 seen[0] = true;
444                                         }
445                                         else if (!seen[1] && !strncmp(p, "dst=", 4))
446                                         {
447                                                 blobmsg_add_string(&buf, "dest", p + 4);
448                                                 seen[1] = true;
449                                         }
450                                         else if (!seen[2] && !strncmp(p, "sport=", 6))
451                                         {
452                                                 blobmsg_add_u32(&buf, "sport", atoi(p + 6));
453                                                 seen[2] = true;
454                                         }
455                                         else if (!seen[3] && !strncmp(p, "dport=", 6))
456                                         {
457                                                 blobmsg_add_u32(&buf, "dport", atoi(p + 6));
458                                                 seen[3] = true;
459                                         }
460                                         else if (!strncmp(p, "packets=", 8))
461                                         {
462                                                 blobmsg_add_u32(&buf,
463                                                                 seen[4] ? "tx_packets" : "rx_packets",
464                                                                 atoi(p + 8));
465                                                 seen[4] = true;
466                                         }
467                                         else if (!strncmp(p, "bytes=", 6))
468                                         {
469                                                 blobmsg_add_u32(&buf,
470                                                                                 seen[5] ? "tx_bytes" : "rx_bytes",
471                                                                 atoi(p + 6));
472                                                 seen[5] = true;
473                                         }
474                                 }
475                         }
476
477                         blobmsg_close_table(&buf, d);
478                 }
479
480                 fclose(f);
481         }
482
483         blobmsg_close_array(&buf, c);
484         ubus_send_reply(ctx, req, buf.head);
485
486         return 0;
487 }
488
489 static int
490 rpc_luci2_network_arp_table(struct ubus_context *ctx, struct ubus_object *obj,
491                             struct ubus_request_data *req, const char *method,
492                             struct blob_attr *msg)
493 {
494         FILE *f;
495         void *c, *d;
496         char *addr, *mac, *dev, line[128];
497
498         blob_buf_init(&buf, 0);
499         c = blobmsg_open_array(&buf, "entries");
500
501         if ((f = fopen("/proc/net/arp", "r")) != NULL)
502         {
503                 /* skip header line */
504                 fgets(line, sizeof(line) - 1, f);
505
506                 while (fgets(line, sizeof(line) - 1, f))
507                 {
508                         addr = strtok(line, " \t");
509
510                         strtok(NULL, " \t"); /* HW type */
511                         strtok(NULL, " \t"); /* Flags */
512
513                         mac = strtok(NULL, " \t");
514
515                         strtok(NULL, " \t"); /* Mask */
516
517                         dev = strtok(NULL, " \t\n");
518
519                         if (!dev)
520                                 continue;
521
522                         d = blobmsg_open_table(&buf, NULL);
523                         blobmsg_add_string(&buf, "ipaddr", addr);
524                         blobmsg_add_string(&buf, "macaddr", mac);
525                         blobmsg_add_string(&buf, "device", dev);
526                         blobmsg_close_table(&buf, d);
527                 }
528
529                 fclose(f);
530         }
531
532         blobmsg_close_array(&buf, c);
533         ubus_send_reply(ctx, req, buf.head);
534
535         return 0;
536 }
537
538 static void
539 put_hexaddr(const char *name, const char *s, const char *m)
540 {
541         int bits;
542         struct in_addr a;
543         char as[sizeof("255.255.255.255/32\0")];
544
545         a.s_addr = strtoul(s, NULL, 16);
546         inet_ntop(AF_INET, &a, as, sizeof(as));
547
548         if (m)
549         {
550                 for (a.s_addr = ntohl(strtoul(m, NULL, 16)), bits = 0;
551                      a.s_addr & 0x80000000;
552                      a.s_addr <<= 1)
553                         bits++;
554
555                 sprintf(as + strlen(as), "/%u", bits);
556         }
557
558         blobmsg_add_string(&buf, name, as);
559 }
560
561 static int
562 rpc_luci2_network_routes(struct ubus_context *ctx, struct ubus_object *obj,
563                          struct ubus_request_data *req, const char *method,
564                          struct blob_attr *msg)
565 {
566         FILE *routes;
567         void *c, *d;
568         char *dst, *dmask, *next, *metric, *device;
569         char line[256];
570         unsigned int n;
571
572         if (!(routes = fopen("/proc/net/route", "r")))
573                 return rpc_errno_status();
574
575         blob_buf_init(&buf, 0);
576         c = blobmsg_open_array(&buf, "routes");
577
578         /* skip header line */
579         fgets(line, sizeof(line) - 1, routes);
580
581         while (fgets(line, sizeof(line) - 1, routes))
582         {
583                 device = strtok(line, "\t ");
584                 dst    = strtok(NULL, "\t ");
585                 next   = strtok(NULL, "\t ");
586
587                 strtok(NULL, "\t "); /* flags */
588                 strtok(NULL, "\t "); /* refcount */
589                 strtok(NULL, "\t "); /* usecount */
590
591                 metric = strtok(NULL, "\t ");
592                 dmask  = strtok(NULL, "\t ");
593
594                 if (!dmask)
595                         continue;
596
597                 d = blobmsg_open_table(&buf, NULL);
598
599                 put_hexaddr("target", dst, dmask);
600                 put_hexaddr("nexthop", next, NULL);
601
602                 n = strtoul(metric, NULL, 10);
603                 blobmsg_add_u32(&buf, "metric", n);
604
605                 blobmsg_add_string(&buf, "device", device);
606
607                 blobmsg_close_table(&buf, d);
608         }
609
610         blobmsg_close_array(&buf, c);
611         fclose(routes);
612
613         ubus_send_reply(ctx, req, buf.head);
614         return 0;
615 }
616
617 static void
618 put_hex6addr(const char *name, const char *s, const char *m)
619 {
620         int i;
621         struct in6_addr a;
622         char as[INET6_ADDRSTRLEN + sizeof("/128")];
623
624 #define hex(x) \
625         (((x) <= '9') ? ((x) - '0') : \
626                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
627                         ((x) - 'a' + 10)))
628
629         for (i = 0; i < 16; i++, s += 2)
630                 a.s6_addr[i] = (16 * hex(*s)) + hex(*(s+1));
631
632         inet_ntop(AF_INET6, &a, as, sizeof(as));
633
634         if (m)
635                 sprintf(as + strlen(as), "/%lu", strtoul(m, NULL, 16));
636
637         blobmsg_add_string(&buf, name, as);
638 }
639
640 static int
641 rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj,
642                           struct ubus_request_data *req, const char *method,
643                           struct blob_attr *msg)
644 {
645         FILE *routes;
646         void *c, *d;
647         char *src, *smask, *dst, *dmask, *next, *metric, *flags, *device;
648         char line[256];
649         unsigned int n;
650
651         if (!(routes = fopen("/proc/net/ipv6_route", "r")))
652                 return rpc_errno_status();
653
654         blob_buf_init(&buf, 0);
655         c = blobmsg_open_array(&buf, "routes");
656
657         while (fgets(line, sizeof(line) - 1, routes))
658         {
659                 dst    = strtok(line, " ");
660                 dmask  = strtok(NULL, " ");
661                 src    = strtok(NULL, " ");
662                 smask  = strtok(NULL, " ");
663                 next   = strtok(NULL, " ");
664                 metric = strtok(NULL, " ");
665
666                 strtok(NULL, " "); /* refcount */
667                 strtok(NULL, " "); /* usecount */
668
669                 flags  = strtok(NULL, " ");
670                 device = strtok(NULL, " \n");
671
672                 if (!device)
673                         continue;
674
675                 n = strtoul(flags, NULL, 16);
676
677                 if (!(n & 1))
678                         continue;
679
680                 d = blobmsg_open_table(&buf, NULL);
681
682                 put_hex6addr("target", dst, dmask);
683                 put_hex6addr("source", src, smask);
684                 put_hex6addr("nexthop", next, NULL);
685
686                 n = strtoul(metric, NULL, 16);
687                 blobmsg_add_u32(&buf, "metric", n);
688
689                 blobmsg_add_string(&buf, "device", device);
690
691                 blobmsg_close_table(&buf, d);
692         }
693
694         blobmsg_close_array(&buf, c);
695         fclose(routes);
696
697         ubus_send_reply(ctx, req, buf.head);
698         return 0;
699 }
700
701
702 int rpc_luci2_api_init(struct ubus_context *ctx)
703 {
704         int rv = 0;
705
706         static const struct ubus_method luci2_system_methods[] = {
707                 UBUS_METHOD_NOARG("syslog", rpc_luci2_system_log),
708                 UBUS_METHOD_NOARG("dmesg",  rpc_luci2_system_dmesg),
709         };
710
711         static struct ubus_object_type luci2_system_type =
712                 UBUS_OBJECT_TYPE("luci-rpc-luci2-system", luci2_system_methods);
713
714         static struct ubus_object system_obj = {
715                 .name = "luci2.system",
716                 .type = &luci2_system_type,
717                 .methods = luci2_system_methods,
718                 .n_methods = ARRAY_SIZE(luci2_system_methods),
719         };
720
721
722         static const struct ubus_method luci2_network_methods[] = {
723                 UBUS_METHOD_NOARG("conntrack_count", rpc_luci2_network_ct_count),
724                 UBUS_METHOD_NOARG("conntrack_table", rpc_luci2_network_ct_table),
725                 UBUS_METHOD_NOARG("arp_table",       rpc_luci2_network_arp_table),
726                 UBUS_METHOD_NOARG("dhcp_leases",     rpc_luci2_network_leases),
727                 UBUS_METHOD_NOARG("dhcp6_leases",    rpc_luci2_network_leases6),
728                 UBUS_METHOD_NOARG("routes",          rpc_luci2_network_routes),
729                 UBUS_METHOD_NOARG("routes6",         rpc_luci2_network_routes6),
730         };
731
732         static struct ubus_object_type luci2_network_type =
733                 UBUS_OBJECT_TYPE("luci-rpc-luci2-network", luci2_network_methods);
734
735         static struct ubus_object network_obj = {
736                 .name = "luci2.network",
737                 .type = &luci2_network_type,
738                 .methods = luci2_network_methods,
739                 .n_methods = ARRAY_SIZE(luci2_network_methods),
740         };
741
742         cursor = uci_alloc_context();
743
744         if (!cursor)
745                 return UBUS_STATUS_UNKNOWN_ERROR;
746
747         rv |= ubus_add_object(ctx, &system_obj);
748         rv |= ubus_add_object(ctx, &network_obj);
749
750         return rv;
751 }