file: convert to plugin library
[project/rpcd.git] / file.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 "file.h"
20 #include "plugin.h"
21
22 static struct blob_buf buf;
23
24 enum {
25         RPC_F_PATH,
26         RPC_F_DATA,
27         __RPC_F_MAX,
28 };
29
30 static const struct blobmsg_policy rpc_file_policy[__RPC_F_MAX] = {
31         [RPC_F_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
32         [RPC_F_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
33 };
34
35 enum {
36         RPC_E_CMD,
37         RPC_E_PARM,
38         RPC_E_ENV,
39         __RPC_E_MAX,
40 };
41
42 static const struct blobmsg_policy rpc_exec_policy[__RPC_E_MAX] = {
43         [RPC_E_CMD] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
44         [RPC_E_PARM] = { .name = "params",  .type = BLOBMSG_TYPE_ARRAY  },
45         [RPC_E_ENV]  = { .name = "env",     .type = BLOBMSG_TYPE_TABLE  },
46 };
47
48 static const char *d_types[] = {
49         [DT_BLK]     = "block",
50         [DT_CHR]     = "char",
51         [DT_DIR]     = "directory",
52         [DT_FIFO]    = "fifo",
53         [DT_LNK]     = "symlink",
54         [DT_REG]     = "file",
55         [DT_SOCK]    = "socket",
56         [DT_UNKNOWN] = "unknown",
57 };
58
59
60 static int
61 rpc_errno_status(void)
62 {
63         switch (errno)
64         {
65         case EACCES:
66                 return UBUS_STATUS_PERMISSION_DENIED;
67
68         case ENOTDIR:
69                 return UBUS_STATUS_INVALID_ARGUMENT;
70
71         case ENOENT:
72                 return UBUS_STATUS_NOT_FOUND;
73
74         case EINVAL:
75                 return UBUS_STATUS_INVALID_ARGUMENT;
76
77         default:
78                 return UBUS_STATUS_UNKNOWN_ERROR;
79         }
80 }
81
82 static struct blob_attr **
83 rpc_check_path(struct blob_attr *msg, char **path, struct stat *s)
84 {
85         static struct blob_attr *tb[__RPC_F_MAX];
86
87         blobmsg_parse(rpc_file_policy, __RPC_F_MAX, tb, blob_data(msg), blob_len(msg));
88
89         if (!tb[RPC_F_PATH])
90         {
91                 errno = EINVAL;
92                 return NULL;
93         }
94
95         *path = blobmsg_data(tb[RPC_F_PATH]);
96
97         if (stat(*path, s))
98                 return NULL;
99
100         return tb;
101 }
102
103 static int
104 rpc_file_read(struct ubus_context *ctx, struct ubus_object *obj,
105               struct ubus_request_data *req, const char *method,
106               struct blob_attr *msg)
107 {
108         int fd, rv, len;
109         char *path;
110         struct stat s;
111         char *wbuf;
112
113         if (!rpc_check_path(msg, &path, &s))
114                 return rpc_errno_status();
115
116         if (s.st_size >= RPC_FILE_MAX_SIZE)
117                 return UBUS_STATUS_NOT_SUPPORTED;
118
119         if ((fd = open(path, O_RDONLY)) < 0)
120                 return rpc_errno_status();
121
122         /* some sysfs files do not report a length */
123         if (s.st_size == 0)
124                 s.st_size = RPC_FILE_MIN_SIZE;
125
126         blob_buf_init(&buf, 0);
127
128         wbuf = blobmsg_alloc_string_buffer(&buf, "data", s.st_size + 1);
129
130         if (!wbuf)
131         {
132                 rv = UBUS_STATUS_UNKNOWN_ERROR;
133                 goto out;
134         }
135
136         if ((len = read(fd, wbuf, s.st_size)) <= 0)
137         {
138                 rv = UBUS_STATUS_NO_DATA;
139                 goto out;
140         }
141
142         *(wbuf + len) = 0;
143         blobmsg_add_string_buffer(&buf);
144
145         ubus_send_reply(ctx, req, buf.head);
146         rv = UBUS_STATUS_OK;
147
148 out:
149         close(fd);
150         return rv;
151 }
152
153 static int
154 rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj,
155                struct ubus_request_data *req, const char *method,
156                struct blob_attr *msg)
157 {
158         int fd;
159         char *path;
160         struct stat s;
161         struct blob_attr **tb;
162
163         if (!(tb = rpc_check_path(msg, &path, &s)))
164                 return rpc_errno_status();
165
166         if (!tb[RPC_F_DATA])
167                 return UBUS_STATUS_INVALID_ARGUMENT;
168
169         if ((fd = open(path, O_WRONLY)) < 0)
170                 return rpc_errno_status();
171
172         write(fd, blobmsg_data(tb[RPC_F_DATA]), blobmsg_data_len(tb[RPC_F_DATA]));
173         close(fd);
174
175         return 0;
176 }
177
178 static int
179 rpc_file_list(struct ubus_context *ctx, struct ubus_object *obj,
180               struct ubus_request_data *req, const char *method,
181               struct blob_attr *msg)
182 {
183         DIR *fd;
184         void *c, *d;
185         char *path;
186         struct stat s;
187         struct dirent *e;
188
189         if (!rpc_check_path(msg, &path, &s))
190                 return rpc_errno_status();
191
192         if ((fd = opendir(path)) == NULL)
193                 return rpc_errno_status();
194
195         blob_buf_init(&buf, 0);
196         c = blobmsg_open_array(&buf, "entries");
197
198         while ((e = readdir(fd)) != NULL)
199         {
200                 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
201                         continue;
202
203                 d = blobmsg_open_table(&buf, NULL);
204                 blobmsg_add_string(&buf, "name", e->d_name);
205                 blobmsg_add_string(&buf, "type", d_types[e->d_type]);
206                 blobmsg_close_table(&buf, d);
207         }
208
209         blobmsg_close_array(&buf, c);
210         ubus_send_reply(ctx, req, buf.head);
211
212         return 0;
213 }
214
215 static int
216 rpc_file_stat(struct ubus_context *ctx, struct ubus_object *obj,
217               struct ubus_request_data *req, const char *method,
218               struct blob_attr *msg)
219 {
220         int type;
221         char *path;
222         struct stat s;
223
224         if (!rpc_check_path(msg, &path, &s))
225                 return rpc_errno_status();
226
227         blob_buf_init(&buf, 0);
228
229         type = S_ISREG(s.st_mode) ? DT_REG :
230                 S_ISDIR(s.st_mode) ? DT_DIR :
231                  S_ISCHR(s.st_mode) ? DT_CHR :
232                   S_ISBLK(s.st_mode) ? DT_BLK :
233                    S_ISFIFO(s.st_mode) ? DT_FIFO :
234                     S_ISLNK(s.st_mode) ? DT_LNK :
235                      S_ISSOCK(s.st_mode) ? DT_SOCK :
236                       DT_UNKNOWN;
237
238         blobmsg_add_string(&buf, "path", path);
239         blobmsg_add_string(&buf, "type", d_types[type]);
240         blobmsg_add_u32(&buf, "size",  s.st_size);
241         blobmsg_add_u32(&buf, "mode",  s.st_mode);
242         blobmsg_add_u32(&buf, "atime", s.st_atime);
243         blobmsg_add_u32(&buf, "mtime", s.st_mtime);
244         blobmsg_add_u32(&buf, "ctime", s.st_ctime);
245         blobmsg_add_u32(&buf, "inode", s.st_ino);
246         blobmsg_add_u32(&buf, "uid",   s.st_uid);
247         blobmsg_add_u32(&buf, "gid",   s.st_gid);
248
249         ubus_send_reply(ctx, req, buf.head);
250
251         return 0;
252 }
253
254 static const char *
255 rpc_file_exec_lookup(const char *cmd)
256 {
257         struct stat s;
258         int plen = 0, clen = strlen(cmd) + 1;
259         char *search, *p;
260         static char path[PATH_MAX];
261
262         if (!stat(cmd, &s) && S_ISREG(s.st_mode))
263                 return cmd;
264
265         search = getenv("PATH");
266
267         if (!search)
268                 search = "/bin:/usr/bin:/sbin:/usr/sbin";
269
270         p = search;
271
272         do
273         {
274                 if (*p != ':' && *p != '\0')
275                         continue;
276
277                 plen = p - search;
278
279                 if ((plen + clen) >= sizeof(path))
280                         continue;
281
282                 strncpy(path, search, plen);
283                 sprintf(path + plen, "/%s", cmd);
284
285                 if (!stat(path, &s) && S_ISREG(s.st_mode))
286                         return path;
287
288                 search = p + 1;
289         }
290         while (*p++);
291
292         return NULL;
293 }
294
295
296 static void
297 rpc_ustream_to_blobmsg(struct ustream *s, const char *name)
298 {
299         int len;
300         char *rbuf, *wbuf;
301
302         if ((len = ustream_pending_data(s, false)) > 0)
303         {
304                 wbuf = blobmsg_alloc_string_buffer(&buf, name, len + 1);
305
306                 if (!wbuf)
307                         return;
308
309                 ustream_for_each_read_buffer(s, rbuf, len)
310                 {
311                         memcpy(wbuf, rbuf, len);
312                         wbuf += len;
313                 }
314
315                 *wbuf = 0;
316                 blobmsg_add_string_buffer(&buf);
317         }
318 }
319
320 static void
321 rpc_file_exec_reply(struct rpc_file_exec_context *c, int rv)
322 {
323         uloop_timeout_cancel(&c->timeout);
324         uloop_process_delete(&c->process);
325
326         if (rv == UBUS_STATUS_OK)
327         {
328                 blob_buf_init(&buf, 0);
329
330                 blobmsg_add_u32(&buf, "code", WEXITSTATUS(c->stat));
331
332                 rpc_ustream_to_blobmsg(&c->opipe.stream, "stdout");
333                 rpc_ustream_to_blobmsg(&c->epipe.stream, "stderr");
334
335                 ubus_send_reply(c->context, &c->request, buf.head);
336         }
337
338         ubus_complete_deferred_request(c->context, &c->request, rv);
339
340         ustream_free(&c->opipe.stream);
341         ustream_free(&c->epipe.stream);
342
343         close(c->opipe.fd.fd);
344         close(c->epipe.fd.fd);
345
346         free(c);
347 }
348
349 static void
350 rpc_file_exec_timeout_cb(struct uloop_timeout *t)
351 {
352         struct rpc_file_exec_context *c =
353                 container_of(t, struct rpc_file_exec_context, timeout);
354
355         kill(c->process.pid, SIGKILL);
356         rpc_file_exec_reply(c, UBUS_STATUS_TIMEOUT);
357 }
358
359 static void
360 rpc_file_exec_process_cb(struct uloop_process *p, int stat)
361 {
362         struct rpc_file_exec_context *c =
363                 container_of(p, struct rpc_file_exec_context, process);
364
365         c->stat = stat;
366
367         ustream_poll(&c->opipe.stream);
368         ustream_poll(&c->epipe.stream);
369 }
370
371 static void
372 rpc_file_exec_opipe_read_cb(struct ustream *s, int bytes)
373 {
374         struct rpc_file_exec_context *c =
375                 container_of(s, struct rpc_file_exec_context, opipe.stream);
376
377         if (ustream_read_buf_full(s))
378                 rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
379 }
380
381 static void
382 rpc_file_exec_epipe_read_cb(struct ustream *s, int bytes)
383 {
384         struct rpc_file_exec_context *c =
385                 container_of(s, struct rpc_file_exec_context, epipe.stream);
386
387         if (ustream_read_buf_full(s))
388                 rpc_file_exec_reply(c, UBUS_STATUS_NOT_SUPPORTED);
389 }
390
391 static void
392 rpc_file_exec_opipe_state_cb(struct ustream *s)
393 {
394         struct rpc_file_exec_context *c =
395                 container_of(s, struct rpc_file_exec_context, opipe.stream);
396
397         if (c->opipe.stream.eof && c->epipe.stream.eof)
398                 rpc_file_exec_reply(c, UBUS_STATUS_OK);
399 }
400
401 static void
402 rpc_file_exec_epipe_state_cb(struct ustream *s)
403 {
404         struct rpc_file_exec_context *c =
405                 container_of(s, struct rpc_file_exec_context, epipe.stream);
406
407         if (c->opipe.stream.eof && c->epipe.stream.eof)
408                 rpc_file_exec_reply(c, UBUS_STATUS_OK);
409 }
410
411 static int
412 rpc_file_exec_run(const char *cmd,
413                               const struct blob_attr *arg, const struct blob_attr *env,
414                   struct ubus_context *ctx, struct ubus_request_data *req)
415 {
416         pid_t pid;
417
418         int opipe[2];
419         int epipe[2];
420
421         int rem;
422         struct blob_attr *cur;
423
424         char arglen;
425         char **args;
426
427         struct rpc_file_exec_context *c;
428
429         cmd = rpc_file_exec_lookup(cmd);
430
431         if (!cmd)
432                 return UBUS_STATUS_NOT_FOUND;
433
434         c = malloc(sizeof(*c));
435
436         if (!c)
437                 return UBUS_STATUS_UNKNOWN_ERROR;
438
439         if (pipe(opipe) || pipe(epipe))
440                 return rpc_errno_status();
441
442         switch ((pid = fork()))
443         {
444         case -1:
445                 return rpc_errno_status();
446
447         case 0:
448                 uloop_done();
449
450                 dup2(opipe[1], 1);
451                 dup2(epipe[1], 2);
452
453                 close(0);
454                 close(opipe[0]);
455                 close(opipe[1]);
456                 close(epipe[0]);
457                 close(epipe[1]);
458
459                 arglen = 2;
460                 args = malloc(sizeof(char *) * arglen);
461
462                 if (!args)
463                         return UBUS_STATUS_UNKNOWN_ERROR;
464
465                 args[0] = (char *)cmd;
466                 args[1] = NULL;
467
468                 if (arg)
469                 {
470                         blobmsg_for_each_attr(cur, arg, rem)
471                         {
472                                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
473                                         continue;
474
475                                 arglen++;
476
477                                 if (!(args = realloc(args, sizeof(char *) * arglen)))
478                                         return UBUS_STATUS_UNKNOWN_ERROR;
479
480                                 args[arglen-2] = blobmsg_data(cur);
481                                 args[arglen-1] = NULL;
482                         }
483                 }
484
485                 if (env)
486                 {
487                         blobmsg_for_each_attr(cur, env, rem)
488                         {
489                                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
490                                         continue;
491
492                                 setenv(blobmsg_name(cur), blobmsg_data(cur), 1);
493                         }
494                 }
495
496                 if (execv(cmd, args))
497                         return rpc_errno_status();
498
499         default:
500                 memset(c, 0, sizeof(*c));
501
502                 ustream_declare(c->opipe, opipe[0], exec_opipe);
503                 ustream_declare(c->epipe, epipe[0], exec_epipe);
504
505                 c->process.pid = pid;
506                 c->process.cb = rpc_file_exec_process_cb;
507                 uloop_process_add(&c->process);
508
509                 c->timeout.cb = rpc_file_exec_timeout_cb;
510                 uloop_timeout_set(&c->timeout, RPC_FILE_MAX_RUNTIME);
511
512                 close(opipe[1]);
513                 close(epipe[1]);
514
515                 c->context = ctx;
516                 ubus_defer_request(ctx, req, &c->request);
517         }
518
519         return UBUS_STATUS_OK;
520 }
521
522 static int
523 rpc_file_exec(struct ubus_context *ctx, struct ubus_object *obj,
524               struct ubus_request_data *req, const char *method,
525               struct blob_attr *msg)
526 {
527         struct blob_attr *tb[__RPC_E_MAX];
528
529         blobmsg_parse(rpc_exec_policy, __RPC_E_MAX, tb,
530                       blob_data(msg), blob_len(msg));
531
532         if (!tb[RPC_E_CMD])
533                 return UBUS_STATUS_INVALID_ARGUMENT;
534
535         return rpc_file_exec_run(blobmsg_data(tb[RPC_E_CMD]),
536                                                  tb[RPC_E_PARM], tb[RPC_E_ENV], ctx, req);
537 }
538
539
540 static int
541 rpc_file_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx)
542 {
543         static const struct ubus_method file_methods[] = {
544                 UBUS_METHOD("read",    rpc_file_read,  rpc_file_policy),
545                 UBUS_METHOD("write",   rpc_file_write, rpc_file_policy),
546                 UBUS_METHOD("list",    rpc_file_list,  rpc_file_policy),
547                 UBUS_METHOD("stat",    rpc_file_stat,  rpc_file_policy),
548                 UBUS_METHOD("exec",    rpc_file_exec,  rpc_exec_policy),
549         };
550
551         static struct ubus_object_type file_type =
552                 UBUS_OBJECT_TYPE("luci-rpc-file", file_methods);
553
554         static struct ubus_object obj = {
555                 .name = "file",
556                 .type = &file_type,
557                 .methods = file_methods,
558                 .n_methods = ARRAY_SIZE(file_methods),
559         };
560
561         return ubus_add_object(ctx, &obj);
562 }
563
564 const struct rpc_plugin rpc_plugin = {
565         .init = rpc_file_api_init
566 };