Link json-c
[project/rpcd.git] / plugin.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 "plugin.h"
20 #include "exec.h"
21
22 static struct blob_buf buf;
23
24 struct rpc_plugin_lookup_context {
25 uint32_t id;
26 char *name;
27 bool found;
28 };
29
30 static void
31 rpc_plugin_lookup_plugin_cb(struct ubus_context *ctx,
32 struct ubus_object_data *obj, void *priv)
33 {
34 struct rpc_plugin_lookup_context *c = priv;
35
36 if (c->id == obj->id)
37 {
38 c->found = true;
39 sprintf(c->name, "%s", obj->path);
40 }
41 }
42
43 static bool
44 rpc_plugin_lookup_plugin(struct ubus_context *ctx, struct ubus_object *obj,
45 char *strptr)
46 {
47 struct rpc_plugin_lookup_context c = { .id = obj->id, .name = strptr };
48
49 if (ubus_lookup(ctx, NULL, rpc_plugin_lookup_plugin_cb, &c))
50 return false;
51
52 return c.found;
53 }
54
55 struct call_context {
56 char path[PATH_MAX];
57 const char *argv[4];
58 char *method;
59 char *input;
60 json_tokener *tok;
61 json_object *obj;
62 bool input_done;
63 bool output_done;
64 };
65
66 static int
67 rpc_plugin_call_stdin_cb(struct ustream *s, void *priv)
68 {
69 struct call_context *c = priv;
70
71 if (!c->input_done)
72 {
73 ustream_write(s, c->input, strlen(c->input), false);
74 c->input_done = true;
75 }
76
77 return 0;
78 }
79
80 static int
81 rpc_plugin_call_stdout_cb(struct blob_buf *blob, char *buf, int len, void *priv)
82 {
83 struct call_context *c = priv;
84
85 if (!c->output_done)
86 {
87 c->obj = json_tokener_parse_ex(c->tok, buf, len);
88
89 if (json_tokener_get_error(c->tok) != json_tokener_continue)
90 c->output_done = true;
91 }
92
93 return len;
94 }
95
96 static int
97 rpc_plugin_call_finish_cb(struct blob_buf *blob, int stat, void *priv)
98 {
99 struct call_context *c = priv;
100 int rv = UBUS_STATUS_INVALID_ARGUMENT;
101
102 if (json_tokener_get_error(c->tok) == json_tokener_success)
103 {
104 if (c->obj)
105 {
106 if (json_object_get_type(c->obj) == json_type_object ||
107 json_object_get_type(c->obj) == json_type_array)
108 {
109 blobmsg_add_json_element(blob, NULL, c->obj);
110 rv = UBUS_STATUS_OK;
111 }
112
113 json_object_put(c->obj);
114 }
115 else
116 {
117 rv = UBUS_STATUS_NO_DATA;
118 }
119 }
120
121 json_tokener_free(c->tok);
122
123 free(c->input);
124 free(c->method);
125
126 return rv;
127 }
128
129 static int
130 rpc_plugin_call(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 rv = UBUS_STATUS_UNKNOWN_ERROR;
135 struct call_context *c;
136 char *plugin;
137
138 c = calloc(1, sizeof(*c));
139
140 if (!c)
141 goto fail;
142
143 c->method = strdup(method);
144 c->input = blobmsg_format_json(msg, true);
145 c->tok = json_tokener_new();
146
147 if (!c->method || !c->input || !c->tok)
148 goto fail;
149
150 plugin = c->path + sprintf(c->path, "%s/", RPC_PLUGIN_DIRECTORY);
151
152 if (!rpc_plugin_lookup_plugin(ctx, obj, plugin))
153 {
154 rv = UBUS_STATUS_NOT_FOUND;
155 goto fail;
156 }
157
158 c->argv[0] = c->path;
159 c->argv[1] = "call";
160 c->argv[2] = c->method;
161
162 return rpc_exec(c->argv, rpc_plugin_call_stdin_cb,
163 rpc_plugin_call_stdout_cb, NULL, rpc_plugin_call_finish_cb,
164 c, ctx, req);
165
166 fail:
167 if (c)
168 {
169 if (c->method)
170 free(c->method);
171
172 if (c->input)
173 free(c->input);
174
175 if (c->tok)
176 json_tokener_free(c->tok);
177
178 free(c);
179 }
180
181 return rv;
182 }
183
184 static bool
185 rpc_plugin_parse_signature(struct blob_attr *sig, struct ubus_method *method)
186 {
187 int rem, n_attr;
188 enum blobmsg_type type;
189 struct blob_attr *attr;
190 struct blobmsg_policy *policy = NULL;
191
192 if (!sig || blob_id(sig) != BLOBMSG_TYPE_TABLE)
193 return false;
194
195 n_attr = 0;
196
197 blobmsg_for_each_attr(attr, sig, rem)
198 n_attr++;
199
200 if (n_attr)
201 {
202 policy = calloc(n_attr, sizeof(*policy));
203
204 if (!policy)
205 return false;
206
207 n_attr = 0;
208
209 blobmsg_for_each_attr(attr, sig, rem)
210 {
211 type = blob_id(attr);
212
213 if (type == BLOBMSG_TYPE_INT32)
214 {
215 switch (blobmsg_get_u32(attr))
216 {
217 case 8:
218 type = BLOBMSG_TYPE_INT8;
219 break;
220
221 case 16:
222 type = BLOBMSG_TYPE_INT16;
223 break;
224
225 case 64:
226 type = BLOBMSG_TYPE_INT64;
227 break;
228
229 default:
230 type = BLOBMSG_TYPE_INT32;
231 break;
232 }
233 }
234
235 policy[n_attr].name = strdup(blobmsg_name(attr));
236 policy[n_attr].type = type;
237
238 n_attr++;
239 }
240 }
241
242 method->name = strdup(blobmsg_name(sig));
243 method->handler = rpc_plugin_call;
244 method->policy = policy;
245 method->n_policy = n_attr;
246
247 return true;
248 }
249
250 static struct ubus_object *
251 rpc_plugin_parse_plugin(const char *name, int fd)
252 {
253 int len, rem, n_method;
254 struct blob_attr *cur;
255 struct ubus_method *methods;
256 struct ubus_object_type *obj_type;
257 struct ubus_object *obj;
258 char outbuf[1024];
259
260 json_tokener *tok;
261 json_object *jsobj;
262
263 blob_buf_init(&buf, 0);
264
265 tok = json_tokener_new();
266
267 if (!tok)
268 return NULL;
269
270 while ((len = read(fd, outbuf, sizeof(outbuf))) > 0)
271 {
272 jsobj = json_tokener_parse_ex(tok, outbuf, len);
273
274 if (json_tokener_get_error(tok) == json_tokener_continue)
275 continue;
276
277 if (json_tokener_get_error(tok) != json_tokener_success)
278 break;
279
280 if (jsobj)
281 {
282 if (json_object_get_type(jsobj) == json_type_object)
283 blobmsg_add_object(&buf, jsobj);
284
285 json_object_put(jsobj);
286 break;
287 }
288 }
289
290 json_tokener_free(tok);
291
292 n_method = 0;
293
294 blob_for_each_attr(cur, buf.head, rem)
295 n_method++;
296
297 if (!n_method)
298 return NULL;
299
300 methods = calloc(n_method, sizeof(*methods));
301
302 if (!methods)
303 return NULL;
304
305 n_method = 0;
306
307 blob_for_each_attr(cur, buf.head, rem)
308 {
309 if (!rpc_plugin_parse_signature(cur, &methods[n_method]))
310 continue;
311
312 n_method++;
313 }
314
315 obj = calloc(1, sizeof(*obj));
316
317 if (!obj)
318 return NULL;
319
320 obj_type = calloc(1, sizeof(*obj_type));
321
322 if (!obj_type)
323 return NULL;
324
325 asprintf((char **)&obj_type->name, "luci-rpc-plugin-%s", name);
326 obj_type->methods = methods;
327 obj_type->n_methods = n_method;
328
329 obj->name = strdup(name);
330 obj->type = obj_type;
331 obj->methods = methods;
332 obj->n_methods = n_method;
333
334 return obj;
335 }
336
337 static int
338 rpc_plugin_register(struct ubus_context *ctx, const char *path)
339 {
340 pid_t pid;
341 int rv = UBUS_STATUS_NO_DATA, fd, fds[2];
342 const char *name;
343 struct ubus_object *plugin;
344
345 name = strrchr(path, '/');
346
347 if (!name)
348 return UBUS_STATUS_INVALID_ARGUMENT;
349
350 if (pipe(fds))
351 return UBUS_STATUS_UNKNOWN_ERROR;
352
353 switch ((pid = fork()))
354 {
355 case -1:
356 return UBUS_STATUS_UNKNOWN_ERROR;
357
358 case 0:
359 fd = open("/dev/null", O_RDWR);
360
361 if (fd > -1)
362 {
363 dup2(fd, 0);
364 dup2(fd, 2);
365
366 if (fd > 2)
367 close(fd);
368 }
369
370 dup2(fds[1], 1);
371
372 close(fds[0]);
373 close(fds[1]);
374
375 if (execl(path, path, "list", NULL))
376 return UBUS_STATUS_UNKNOWN_ERROR;
377
378 default:
379 plugin = rpc_plugin_parse_plugin(name + 1, fds[0]);
380
381 if (!plugin)
382 goto out;
383
384 rv = ubus_add_object(ctx, plugin);
385
386 out:
387 close(fds[0]);
388 close(fds[1]);
389 waitpid(pid, NULL, 0);
390
391 return rv;
392 }
393 }
394
395 int rpc_plugin_api_init(struct ubus_context *ctx)
396 {
397 DIR *d;
398 int rv = 0;
399 struct stat s;
400 struct dirent *e;
401 char path[PATH_MAX];
402
403 d = opendir(RPC_PLUGIN_DIRECTORY);
404
405 if (!d)
406 return UBUS_STATUS_NOT_FOUND;
407
408 while ((e = readdir(d)) != NULL)
409 {
410 snprintf(path, sizeof(path) - 1, RPC_PLUGIN_DIRECTORY "/%s", e->d_name);
411
412 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
413 continue;
414
415 rv |= rpc_plugin_register(ctx, path);
416 }
417
418 closedir(d);
419
420 return rv;
421 }