file: use global exec timeout instead of own hardcoded limit
[project/rpcd.git] / file.c
1 /*
2 * rpcd - UBUS RPC server
3 *
4 * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
5 * Copyright (C) 2016 Luka Perkov <luka@openwrt.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <fcntl.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <limits.h>
26 #include <dirent.h>
27 #include <sys/stat.h>
28 #include <sys/wait.h>
29 #include <libubus.h>
30 #include <libubox/blobmsg.h>
31 #include <libubox/md5.h>
32 #include <libubox/ustream.h>
33 #include <libubox/utils.h>
34
35 #include <rpcd/plugin.h>
36
37 /* limit of sys & proc files */
38 #define RPC_FILE_MIN_SIZE (128)
39
40 /* limit of regular files and command output data */
41 #define RPC_FILE_MAX_SIZE (4096 * 64)
42
43 #define ustream_for_each_read_buffer(stream, ptr, len) \
44 for (ptr = ustream_get_read_buf(stream, &len); \
45 ptr != NULL && len > 0; \
46 ustream_consume(stream, len), ptr = ustream_get_read_buf(stream, &len))
47
48 #define ustream_declare(us, fd, name) \
49 us.stream.string_data = true; \
50 us.stream.r.buffer_len = 4096; \
51 us.stream.r.max_buffers = RPC_FILE_MAX_SIZE / 4096; \
52 us.stream.notify_read = rpc_file_##name##_read_cb; \
53 us.stream.notify_state = rpc_file_##name##_state_cb; \
54 ustream_fd_init(&us, fd);
55
56 struct rpc_file_exec_context {
57 struct ubus_context *context;
58 struct ubus_request_data request;
59 struct uloop_timeout timeout;
60 struct uloop_process process;
61 struct ustream_fd opipe;
62 struct ustream_fd epipe;
63 int outlen;
64 char *out;
65 int errlen;
66 char *err;
67 int stat;
68 };
69
70
71 static struct blob_buf buf;
72
73 enum {
74 RPC_F_R_PATH,
75 __RPC_F_R_MAX,
76 };
77
78 static const struct blobmsg_policy rpc_file_r_policy[__RPC_F_R_MAX] = {
79 [RPC_F_R_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
80 };
81
82 enum {
83 RPC_F_RB_PATH,
84 RPC_F_RB_BASE64,
85 __RPC_F_RB_MAX,
86 };
87
88 static const struct blobmsg_policy rpc_file_rb_policy[__RPC_F_RB_MAX] = {
89 [RPC_F_RB_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
90 [RPC_F_RB_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
91 };
92
93 enum {
94 RPC_F_RW_PATH,
95 RPC_F_RW_DATA,
96 RPC_F_RW_APPEND,
97 RPC_F_RW_MODE,
98 RPC_F_RW_BASE64,
99 __RPC_F_RW_MAX,
100 };
101
102 static const struct blobmsg_policy rpc_file_rw_policy[__RPC_F_RW_MAX] = {
103 [RPC_F_RW_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
104 [RPC_F_RW_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
105 [RPC_F_RW_APPEND] = { .name = "append", .type = BLOBMSG_TYPE_BOOL },
106 [RPC_F_RW_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_INT32 },
107 [RPC_F_RW_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
108 };
109
110 enum {
111 RPC_E_CMD,
112 RPC_E_PARM,
113 RPC_E_ENV,
114 __RPC_E_MAX,
115 };
116
117 static const struct blobmsg_policy rpc_exec_policy[__RPC_E_MAX] = {
118 [RPC_E_CMD] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
119 [RPC_E_PARM] = { .name = "params", .type = BLOBMSG_TYPE_ARRAY },
120 [RPC_E_ENV] = { .name = "env", .type = BLOBMSG_TYPE_TABLE },
121 };
122
123 static const char *d_types[] = {
124 [DT_BLK] = "block",
125 [DT_CHR] = "char",
126 [DT_DIR] = "directory",
127 [DT_FIFO] = "fifo",
128 [DT_LNK] = "symlink",
129 [DT_REG] = "file",
130 [DT_SOCK] = "socket",
131 [DT_UNKNOWN] = "unknown",
132 };
133
134
135 static int
136 rpc_errno_status(void)
137 {
138 switch (errno)
139 {
140 case EACCES:
141 return UBUS_STATUS_PERMISSION_DENIED;
142
143 case ENOTDIR:
144 return UBUS_STATUS_INVALID_ARGUMENT;
145
146 case ENOENT:
147 return UBUS_STATUS_NOT_FOUND;
148
149 case EINVAL:
150 return UBUS_STATUS_INVALID_ARGUMENT;
151
152 default:
153 return UBUS_STATUS_UNKNOWN_ERROR;
154 }
155 }
156
157 static struct blob_attr **
158 rpc_check_path(struct blob_attr *msg, char **path, struct stat *s)
159 {
160 static struct blob_attr *tb[__RPC_F_R_MAX];
161
162 blobmsg_parse(rpc_file_r_policy, __RPC_F_R_MAX, tb, blob_data(msg), blob_len(msg));
163
164 if (!tb[RPC_F_R_PATH])
165 {
166 errno = EINVAL;
167 return NULL;
168 }
169
170 *path = blobmsg_data(tb[RPC_F_R_PATH]);
171
172 if (stat(*path, s))
173 return NULL;
174
175 return tb;
176 }
177
178 static int
179 rpc_file_read(struct ubus_context *ctx, struct ubus_object *obj,
180 struct ubus_request_data *req, const char *method,
181 struct blob_attr *msg)
182 {
183 static struct blob_attr *tb[__RPC_F_RB_MAX];
184 bool base64 = false;
185 int fd, rv;
186 ssize_t len;
187 char *path;
188 struct stat s;
189 char *wbuf;
190
191 blobmsg_parse(rpc_file_rb_policy, __RPC_F_RB_MAX, tb, blob_data(msg), blob_len(msg));
192
193 if (!tb[RPC_F_RB_PATH])
194 return rpc_errno_status();
195
196 path = blobmsg_data(tb[RPC_F_RB_PATH]);
197
198 if (stat(path, &s))
199 return rpc_errno_status();
200
201 if (s.st_size >= RPC_FILE_MAX_SIZE)
202 return UBUS_STATUS_NOT_SUPPORTED;
203
204 if ((fd = open(path, O_RDONLY)) < 0)
205 return rpc_errno_status();
206
207 /* some sysfs files do not report a length */
208 if (s.st_size == 0)
209 s.st_size = RPC_FILE_MIN_SIZE;
210
211 blob_buf_init(&buf, 0);
212
213 if (tb[RPC_F_RB_BASE64])
214 base64 = blobmsg_get_bool(tb[RPC_F_RB_BASE64]);
215
216 len = s.st_size + 1;
217 if (base64)
218 len = B64_ENCODE_LEN(s.st_size);
219 wbuf = blobmsg_alloc_string_buffer(&buf, "data", len);
220
221 if (!wbuf)
222 {
223 rv = UBUS_STATUS_UNKNOWN_ERROR;
224 goto out;
225 }
226
227 if ((len = read(fd, wbuf, s.st_size)) <= 0)
228 {
229 rv = UBUS_STATUS_NO_DATA;
230 goto out;
231 }
232
233 if (base64)
234 {
235 uint8_t *data = calloc(len, sizeof(uint8_t));
236 if (!data)
237 {
238 rv = UBUS_STATUS_UNKNOWN_ERROR;
239 goto out;
240 }
241 memcpy(data, wbuf, len);
242
243 len = b64_encode(data, len, wbuf, B64_ENCODE_LEN(len));
244 free(data);
245 if (len < 0)
246 {
247 rv = UBUS_STATUS_UNKNOWN_ERROR;
248 goto out;
249 }
250 }
251
252 *(wbuf + len) = '\0';
253 blobmsg_add_string_buffer(&buf);
254
255 ubus_send_reply(ctx, req, buf.head);
256 rv = UBUS_STATUS_OK;
257
258 out:
259 blob_buf_free(&buf);
260 close(fd);
261 return rv;
262 }
263
264 static int
265 rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj,
266 struct ubus_request_data *req, const char *method,
267 struct blob_attr *msg)
268 {
269 struct blob_attr *tb[__RPC_F_RW_MAX];
270 int append = O_TRUNC;
271 mode_t prev_mode, mode = 0666;
272 int fd, rv = 0;
273 void *data = NULL;
274 ssize_t data_len = 0;
275
276 blobmsg_parse(rpc_file_rw_policy, __RPC_F_RW_MAX, tb,
277 blob_data(msg), blob_len(msg));
278
279 if (!tb[RPC_F_RW_PATH] || !tb[RPC_F_RW_DATA])
280 return UBUS_STATUS_INVALID_ARGUMENT;
281
282 data = blobmsg_data(tb[RPC_F_RW_DATA]);
283 data_len = blobmsg_data_len(tb[RPC_F_RW_DATA]) - 1;
284
285 if (tb[RPC_F_RW_APPEND] && blobmsg_get_bool(tb[RPC_F_RW_APPEND]))
286 append = O_APPEND;
287
288 if (tb[RPC_F_RW_MODE])
289 mode = blobmsg_get_u32(tb[RPC_F_RW_MODE]);
290
291 prev_mode = umask(0);
292 fd = open(blobmsg_data(tb[RPC_F_RW_PATH]), O_CREAT | O_WRONLY | append, mode);
293 umask(prev_mode);
294 if (fd < 0)
295 return rpc_errno_status();
296
297 if (tb[RPC_F_RW_BASE64] && blobmsg_get_bool(tb[RPC_F_RW_BASE64]))
298 {
299 data_len = b64_decode(data, data, data_len);
300 if (data_len < 0)
301 {
302 rv = UBUS_STATUS_UNKNOWN_ERROR;
303 goto out;
304 }
305 }
306
307 if (write(fd, data, data_len) < 0)
308 rv = -1;
309
310 out:
311 if (fsync(fd) < 0)
312 rv = -1;
313
314 close(fd);
315 sync();
316
317 if (rv)
318 return rpc_errno_status();
319
320 return 0;
321 }
322
323 static int
324 rpc_file_md5(struct ubus_context *ctx, struct ubus_object *obj,
325 struct ubus_request_data *req, const char *method,
326 struct blob_attr *msg)
327 {
328 int rv, i;
329 char *path;
330 struct stat s;
331 uint8_t md5[16];
332 char *wbuf;
333
334 if (!rpc_check_path(msg, &path, &s))
335 return rpc_errno_status();
336
337 if (!S_ISREG(s.st_mode))
338 return UBUS_STATUS_NOT_SUPPORTED;
339
340 if ((rv = md5sum(path, md5)) <= 0)
341 return rpc_errno_status();
342
343 blob_buf_init(&buf, 0);
344 wbuf = blobmsg_alloc_string_buffer(&buf, "md5", 33);
345
346 for (i = 0; i < 16; i++)
347 sprintf(wbuf + (i * 2), "%02x", (uint8_t) md5[i]);
348
349 blobmsg_add_string_buffer(&buf);
350 ubus_send_reply(ctx, req, buf.head);
351 blob_buf_free(&buf);
352
353 return UBUS_STATUS_OK;
354 }
355
356 static int
357 rpc_file_list(struct ubus_context *ctx, struct ubus_object *obj,
358 struct ubus_request_data *req, const char *method,
359 struct blob_attr *msg)
360 {
361 DIR *fd;
362 void *c, *d;
363 char *path;
364 struct stat s;
365 struct dirent *e;
366
367 if (!rpc_check_path(msg, &path, &s))
368 return rpc_errno_status();
369
370 if ((fd = opendir(path)) == NULL)
371 return rpc_errno_status();
372
373 blob_buf_init(&buf, 0);
374 c = blobmsg_open_array(&buf, "entries");
375
376 while ((e = readdir(fd)) != NULL)
377 {
378 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
379 continue;
380
381 d = blobmsg_open_table(&buf, NULL);
382 blobmsg_add_string(&buf, "name", e->d_name);
383 blobmsg_add_string(&buf, "type", d_types[e->d_type]);
384 blobmsg_close_table(&buf, d);
385 }
386
387 closedir(fd);
388
389 blobmsg_close_array(&buf, c);
390 ubus_send_reply(ctx, req, buf.head);
391 blob_buf_free(&buf);
392
393 return 0;
394 }
395
396 static int
397 rpc_file_stat(struct ubus_context *ctx, struct ubus_object *obj,
398 struct ubus_request_data *req, const char *method,
399 struct blob_attr *msg)
400 {
401 int type;
402 char *path;
403 struct stat s;
404
405 if (!rpc_check_path(msg, &path, &s))
406 return rpc_errno_status();
407
408 blob_buf_init(&buf, 0);
409
410 type = S_ISREG(s.st_mode) ? DT_REG :
411 S_ISDIR(s.st_mode) ? DT_DIR :
412 S_ISCHR(s.st_mode) ? DT_CHR :
413 S_ISBLK(s.st_mode) ? DT_BLK :
414 S_ISFIFO(s.st_mode) ? DT_FIFO :
415 S_ISLNK(s.st_mode) ? DT_LNK :
416 S_ISSOCK(s.st_mode) ? DT_SOCK :
417 DT_UNKNOWN;
418
419 blobmsg_add_string(&buf, "path", path);
420 blobmsg_add_string(&buf, "type", d_types[type]);
421 blobmsg_add_u32(&buf, "size", s.st_size);
422 blobmsg_add_u32(&buf, "mode", s.st_mode);
423 blobmsg_add_u32(&buf, "atime", s.st_atime);
424 blobmsg_add_u32(&buf, "mtime", s.st_mtime);
425 blobmsg_add_u32(&buf, "ctime", s.st_ctime);
426 blobmsg_add_u32(&buf, "inode", s.st_ino);
427 blobmsg_add_u32(&buf, "uid", s.st_uid);
428 blobmsg_add_u32(&buf, "gid", s.st_gid);
429
430 ubus_send_reply(ctx, req, buf.head);
431 blob_buf_free(&buf);
432
433 return 0;
434 }
435
436 static const char *
437 rpc_file_exec_lookup(const char *cmd)
438 {
439 struct stat s;
440 int plen = 0, clen = strlen(cmd) + 1;
441 char *search, *p;
442 static char path[PATH_MAX];
443
444 if (!stat(cmd, &s) && S_ISREG(s.st_mode))
445 return cmd;
446
447 search = getenv("PATH");
448
449 if (!search)
450 search = "/bin:/usr/bin:/sbin:/usr/sbin";
451
452 p = search;
453
454 do
455 {
456 if (*p != ':' && *p != '\0')
457 continue;
458
459 plen = p - search;
460
461 if ((plen + clen) >= sizeof(path))
462 continue;
463
464 strncpy(path, search, plen);
465 sprintf(path + plen, "/%s", cmd);
466
467 if (!stat(path, &s) && S_ISREG(s.st_mode))
468 return path;
469
470 search = p + 1;
471 }
472 while (*p++);
473
474 return NULL;
475 }
476
477
478 static void
479 rpc_ustream_to_blobmsg(struct ustream *s, const char *name)
480 {
481 int len;
482 char *rbuf, *wbuf;
483
484 if ((len = ustream_pending_data(s, false)) > 0)
485 {
486 wbuf = blobmsg_alloc_string_buffer(&buf, name, len + 1);
487
488 if (!wbuf)
489 return;
490
491 ustream_for_each_read_buffer(s, rbuf, len)
492 {
493 memcpy(wbuf, rbuf, len);
494 wbuf += len;
495 }
496
497 *wbuf = 0;
498 blobmsg_add_string_buffer(&buf);
499 }
500 }
501
502 static void
503 rpc_file_exec_reply(struct rpc_file_exec_context *c, int rv)
504 {
505 uloop_timeout_cancel(&c->timeout);
506 uloop_process_delete(&c->process);
507
508 if (rv == UBUS_STATUS_OK)
509 {
510 blob_buf_init(&buf, 0);
511
512 blobmsg_add_u32(&buf, "code", WEXITSTATUS(c->stat));
513
514 rpc_ustream_to_blobmsg(&c->opipe.stream, "stdout");
515 rpc_ustream_to_blobmsg(&c->epipe.stream, "stderr");
516
517 ubus_send_reply(c->context, &c->request, buf.head);
518 blob_buf_free(&buf);
519 }
520
521 ubus_complete_deferred_request(c->context, &c->request, rv);
522
523 ustream_free(&c->opipe.stream);
524 ustream_free(&c->epipe.stream);
525
526 close(c->opipe.fd.fd);
527 close(c->epipe.fd.fd);
528
529 free(c);
530 }
531
532 static void
533 rpc_file_exec_timeout_cb(struct uloop_timeout *t)
534 {
535 struct rpc_file_exec_context *c =
536 container_of(t, struct rpc_file_exec_context, timeout);
537
538 kill(c->process.pid, SIGKILL);
539 rpc_file_exec_reply(c, UBUS_STATUS_TIMEOUT);
540 }
541
542 static void
543 rpc_file_exec_process_cb(struct uloop_process *p, int stat)
544 {
545 struct rpc_file_exec_context *c =
546 container_of(p, struct rpc_file_exec_context, process);
547
548 c->stat = stat;
549
550 ustream_poll(&c->opipe.stream);
551 ustream_poll(&c->epipe.stream);
552 }
553
554 static void
555 rpc_file_exec_opipe_read_cb(struct ustream *s, int bytes)
556 {
557 struct rpc_file_exec_context *c =
558 container_of(s, struct rpc_file_exec_context, opipe.stream);
559
560 if (ustream_read_buf_full(s))
561 rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
562 }
563
564 static void
565 rpc_file_exec_epipe_read_cb(struct ustream *s, int bytes)
566 {
567 struct rpc_file_exec_context *c =
568 container_of(s, struct rpc_file_exec_context, epipe.stream);
569
570 if (ustream_read_buf_full(s))
571 rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
572 }
573
574 static void
575 rpc_file_exec_opipe_state_cb(struct ustream *s)
576 {
577 struct rpc_file_exec_context *c =
578 container_of(s, struct rpc_file_exec_context, opipe.stream);
579
580 if (c->opipe.stream.eof && c->epipe.stream.eof)
581 rpc_file_exec_reply(c, UBUS_STATUS_OK);
582 }
583
584 static void
585 rpc_file_exec_epipe_state_cb(struct ustream *s)
586 {
587 struct rpc_file_exec_context *c =
588 container_of(s, struct rpc_file_exec_context, epipe.stream);
589
590 if (c->opipe.stream.eof && c->epipe.stream.eof)
591 rpc_file_exec_reply(c, UBUS_STATUS_OK);
592 }
593
594 static int
595 rpc_file_exec_run(const char *cmd,
596 const struct blob_attr *arg, const struct blob_attr *env,
597 struct ubus_context *ctx, struct ubus_request_data *req)
598 {
599 pid_t pid;
600
601 int opipe[2];
602 int epipe[2];
603
604 int rem;
605 struct blob_attr *cur;
606
607 char arglen;
608 char **args;
609
610 struct rpc_file_exec_context *c;
611
612 cmd = rpc_file_exec_lookup(cmd);
613
614 if (!cmd)
615 return UBUS_STATUS_NOT_FOUND;
616
617 c = malloc(sizeof(*c));
618
619 if (!c)
620 return UBUS_STATUS_UNKNOWN_ERROR;
621
622 if (pipe(opipe) || pipe(epipe))
623 return rpc_errno_status();
624
625 switch ((pid = fork()))
626 {
627 case -1:
628 return rpc_errno_status();
629
630 case 0:
631 uloop_done();
632
633 dup2(opipe[1], 1);
634 dup2(epipe[1], 2);
635
636 close(0);
637 close(opipe[0]);
638 close(opipe[1]);
639 close(epipe[0]);
640 close(epipe[1]);
641
642 arglen = 2;
643 args = malloc(sizeof(char *) * arglen);
644
645 if (!args)
646 return UBUS_STATUS_UNKNOWN_ERROR;
647
648 args[0] = (char *)cmd;
649 args[1] = NULL;
650
651 if (arg)
652 {
653 blobmsg_for_each_attr(cur, arg, rem)
654 {
655 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
656 continue;
657
658 arglen++;
659
660 if (!(args = realloc(args, sizeof(char *) * arglen)))
661 return UBUS_STATUS_UNKNOWN_ERROR;
662
663 args[arglen-2] = blobmsg_data(cur);
664 args[arglen-1] = NULL;
665 }
666 }
667
668 if (env)
669 {
670 blobmsg_for_each_attr(cur, env, rem)
671 {
672 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
673 continue;
674
675 setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
676 }
677 }
678
679 if (execv(cmd, args))
680 return rpc_errno_status();
681
682 default:
683 memset(c, 0, sizeof(*c));
684
685 ustream_declare(c->opipe, opipe[0], exec_opipe);
686 ustream_declare(c->epipe, epipe[0], exec_epipe);
687
688 c->process.pid = pid;
689 c->process.cb = rpc_file_exec_process_cb;
690 uloop_process_add(&c->process);
691
692 c->timeout.cb = rpc_file_exec_timeout_cb;
693 uloop_timeout_set(&c->timeout, exec_timeout);
694
695 close(opipe[1]);
696 close(epipe[1]);
697
698 c->context = ctx;
699 ubus_defer_request(ctx, req, &c->request);
700 }
701
702 return UBUS_STATUS_OK;
703 }
704
705 static int
706 rpc_file_exec(struct ubus_context *ctx, struct ubus_object *obj,
707 struct ubus_request_data *req, const char *method,
708 struct blob_attr *msg)
709 {
710 struct blob_attr *tb[__RPC_E_MAX];
711
712 blobmsg_parse(rpc_exec_policy, __RPC_E_MAX, tb,
713 blob_data(msg), blob_len(msg));
714
715 if (!tb[RPC_E_CMD])
716 return UBUS_STATUS_INVALID_ARGUMENT;
717
718 return rpc_file_exec_run(blobmsg_data(tb[RPC_E_CMD]),
719 tb[RPC_E_PARM], tb[RPC_E_ENV], ctx, req);
720 }
721
722
723 static int
724 rpc_file_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
725 {
726 static const struct ubus_method file_methods[] = {
727 UBUS_METHOD("read", rpc_file_read, rpc_file_rb_policy),
728 UBUS_METHOD("write", rpc_file_write, rpc_file_rw_policy),
729 UBUS_METHOD("list", rpc_file_list, rpc_file_r_policy),
730 UBUS_METHOD("stat", rpc_file_stat, rpc_file_r_policy),
731 UBUS_METHOD("md5", rpc_file_md5, rpc_file_r_policy),
732 UBUS_METHOD("exec", rpc_file_exec, rpc_exec_policy),
733 };
734
735 static struct ubus_object_type file_type =
736 UBUS_OBJECT_TYPE("luci-rpc-file", file_methods);
737
738 static struct ubus_object obj = {
739 .name = "file",
740 .type = &file_type,
741 .methods = file_methods,
742 .n_methods = ARRAY_SIZE(file_methods),
743 };
744
745 return ubus_add_object(ctx, &obj);
746 }
747
748 struct rpc_plugin rpc_plugin = {
749 .init = rpc_file_api_init
750 };