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