initial commit
[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 <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <dirent.h>
23 #include <sys/stat.h>
24
25 #include "file.h"
26
27 static struct blob_buf buf;
28
29 enum {
30         RPC_F_PATH,
31         RPC_F_DATA,
32         __RPC_F_MAX,
33 };
34
35 static const struct blobmsg_policy file_policy[__RPC_F_MAX] = {
36         [RPC_F_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
37         [RPC_F_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
38 };
39
40 static const char *d_types[] = {
41         [DT_BLK]     = "block",
42         [DT_CHR]     = "char",
43         [DT_DIR]     = "directory",
44         [DT_FIFO]    = "fifo",
45         [DT_LNK]     = "symlink",
46         [DT_REG]     = "file",
47         [DT_SOCK]    = "socket",
48         [DT_UNKNOWN] = "unknown",
49 };
50
51
52 static int
53 rpc_errno_status(void)
54 {
55         switch (errno)
56         {
57         case EACCES:
58                 return UBUS_STATUS_PERMISSION_DENIED;
59
60         case ENOTDIR:
61                 return UBUS_STATUS_INVALID_ARGUMENT;
62
63         case ENOENT:
64                 return UBUS_STATUS_NOT_FOUND;
65
66         case EINVAL:
67                 return UBUS_STATUS_INVALID_ARGUMENT;
68
69         default:
70                 return UBUS_STATUS_UNKNOWN_ERROR;
71         }
72 }
73
74 static struct blob_attr **
75 rpc_check_path(struct blob_attr *msg, char **path, struct stat *s)
76 {
77         static struct blob_attr *tb[__RPC_F_MAX];
78
79         blobmsg_parse(file_policy, __RPC_F_MAX, tb, blob_data(msg), blob_len(msg));
80
81         if (!tb[RPC_F_PATH])
82         {
83                 errno = EINVAL;
84                 return NULL;
85         }
86
87         *path = blobmsg_data(tb[RPC_F_PATH]);
88
89         if (stat(*path, s))
90                 return NULL;
91
92         return tb;
93 }
94
95 static int
96 rpc_handle_read(struct ubus_context *ctx, struct ubus_object *obj,
97                 struct ubus_request_data *req, const char *method,
98                 struct blob_attr *msg)
99 {
100         int fd, rlen;
101         char *path;
102         char buffer[RPC_FILE_MAX_SIZE];
103         struct stat s;
104
105         if (!rpc_check_path(msg, &path, &s))
106                 return rpc_errno_status();
107
108         if (s.st_size >= RPC_FILE_MAX_SIZE)
109                 return UBUS_STATUS_NOT_SUPPORTED;
110
111         if ((fd = open(path, O_RDONLY)) < 0)
112                 return rpc_errno_status();
113
114         if ((rlen = read(fd, buffer, RPC_FILE_MAX_SIZE-1)) > 0)
115                 buffer[rlen] = 0;
116
117         close(fd);
118
119         if (rlen <= 0)
120                 return UBUS_STATUS_NO_DATA;
121
122         blob_buf_init(&buf, 0);
123         blobmsg_add_string(&buf, "data", buffer);
124         ubus_send_reply(ctx, req, buf.head);
125
126         return 0;
127 }
128
129 static int
130 rpc_handle_write(struct ubus_context *ctx, struct ubus_object *obj,
131                  struct ubus_request_data *req, const char *method,
132                  struct blob_attr *msg)
133 {
134         int fd, rv;
135         char *path;
136         struct stat s;
137         struct blob_attr **tb;
138
139         if (!(tb = rpc_check_path(msg, &path, &s)))
140                 return rpc_errno_status();
141
142         if (!tb[RPC_F_DATA])
143                 return UBUS_STATUS_INVALID_ARGUMENT;
144
145         if ((fd = open(path, O_WRONLY)) < 0)
146                 return rpc_errno_status();
147
148         rv = write(fd, blobmsg_data(tb[RPC_F_DATA]), blobmsg_data_len(tb[RPC_F_DATA]));
149
150         close(fd);
151
152         if (rv <= 0)
153                 return UBUS_STATUS_NO_DATA;
154
155         return 0;
156 }
157
158 static int
159 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
160                 struct ubus_request_data *req, const char *method,
161                 struct blob_attr *msg)
162 {
163         DIR *fd;
164         void *c, *d;
165         char *path;
166         struct stat s;
167         struct dirent *e;
168
169         if (!rpc_check_path(msg, &path, &s))
170                 return rpc_errno_status();
171
172         if ((fd = opendir(path)) == NULL)
173                 return rpc_errno_status();
174
175         blob_buf_init(&buf, 0);
176         c = blobmsg_open_array(&buf, "entries");
177
178         while ((e = readdir(fd)) != NULL)
179         {
180                 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, ".."))
181                         continue;
182
183                 d = blobmsg_open_table(&buf, NULL);
184                 blobmsg_add_string(&buf, "name", e->d_name);
185                 blobmsg_add_string(&buf, "type", d_types[e->d_type]);
186                 blobmsg_close_table(&buf, d);
187         }
188
189         blobmsg_close_array(&buf, c);
190         ubus_send_reply(ctx, req, buf.head);
191
192         return 0;
193 }
194
195 static int
196 rpc_handle_stat(struct ubus_context *ctx, struct ubus_object *obj,
197                 struct ubus_request_data *req, const char *method,
198                 struct blob_attr *msg)
199 {
200         int type;
201         char *path;
202         struct stat s;
203
204         if (!rpc_check_path(msg, &path, &s))
205                 return rpc_errno_status();
206
207         blob_buf_init(&buf, 0);
208
209         type = S_ISREG(s.st_mode) ? DT_REG :
210                 S_ISDIR(s.st_mode) ? DT_DIR :
211                          S_ISCHR(s.st_mode) ? DT_CHR :
212                           S_ISBLK(s.st_mode) ? DT_BLK :
213                            S_ISFIFO(s.st_mode) ? DT_FIFO :
214                             S_ISLNK(s.st_mode) ? DT_LNK :
215                                  S_ISSOCK(s.st_mode) ? DT_SOCK :
216                                   DT_UNKNOWN;
217
218         blobmsg_add_string(&buf, "path", path);
219         blobmsg_add_string(&buf, "type", d_types[type]);
220         blobmsg_add_u32(&buf, "size",  s.st_size);
221         blobmsg_add_u32(&buf, "mode",  s.st_mode);
222         blobmsg_add_u32(&buf, "atime", s.st_atime);
223         blobmsg_add_u32(&buf, "mtime", s.st_mtime);
224         blobmsg_add_u32(&buf, "ctime", s.st_ctime);
225         blobmsg_add_u32(&buf, "inode", s.st_ino);
226         blobmsg_add_u32(&buf, "uid",   s.st_uid);
227         blobmsg_add_u32(&buf, "gid",   s.st_gid);
228
229         ubus_send_reply(ctx, req, buf.head);
230
231         return 0;
232 }
233
234
235 int rpc_file_api_init(struct ubus_context *ctx)
236 {
237         static const struct ubus_method file_methods[] = {
238                 UBUS_METHOD("read",    rpc_handle_read,  file_policy),
239                 UBUS_METHOD("write",   rpc_handle_write, file_policy),
240                 UBUS_METHOD("list",    rpc_handle_list,  file_policy),
241                 UBUS_METHOD("stat",    rpc_handle_stat,  file_policy),
242         };
243
244         static struct ubus_object_type file_type =
245                 UBUS_OBJECT_TYPE("luci-rpc-file", file_methods);
246
247         static struct ubus_object obj = {
248                 .name = "file",
249                 .type = &file_type,
250                 .methods = file_methods,
251                 .n_methods = ARRAY_SIZE(file_methods),
252         };
253
254         return ubus_add_object(ctx, &obj);
255 }