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 }