ubus: split out session handling code into ubus-session.c
[project/uhttpd.git] / ubus.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2010-2012 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19
20
21 #include <libubox/blobmsg.h>
22 #include <libubox/blobmsg_json.h>
23 #include <libubox/avl.h>
24 #include <libubox/avl-cmp.h>
25 #include <stdio.h>
26 #include <poll.h>
27
28 #include "uhttpd.h"
29 #include "plugin.h"
30 #include "ubus-session.h"
31
32 static const struct uhttpd_ops *ops;
33 static struct config *_conf;
34 #define conf (*_conf)
35
36 static struct ubus_context *ctx;
37 static struct blob_buf buf;
38
39 #define UH_UBUS_MAX_POST_SIZE 4096
40
41 static char *split_str(char *str)
42 {
43 if (str)
44 str = strchr(str, '/');
45
46 while (str && *str == '/') {
47 *str = 0;
48 str++;
49 }
50 return str;
51 }
52
53 static bool
54 uh_ubus_request_parse_url(struct client *cl, char *url, char **sid, char **obj, char **fun)
55 {
56 url += strlen(conf.ubus_prefix);
57 while (url && *url == '/')
58 url++;
59
60 *sid = url;
61
62 url = split_str(url);
63 *obj = url;
64
65 url = split_str(url);
66 *fun = url;
67
68 return *sid && *obj && *fun;
69 }
70
71 static void
72 uh_ubus_request_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
73 {
74 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
75 struct client *cl = container_of(du, struct client, dispatch.ubus);
76 char *str;
77
78 if (!du->header_sent) {
79 ops->http_header(cl, 200, "OK");
80 ustream_printf(cl->us, "Content-Type: application/json\r\n\r\n");
81 du->header_sent = true;
82 }
83
84 str = blobmsg_format_json_indent(msg, true, 0);
85 ops->chunk_write(cl, str, strlen(str));
86 free(str);
87 }
88
89 static void
90 uh_ubus_request_cb(struct ubus_request *req, int ret)
91 {
92 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
93 struct client *cl = container_of(du, struct client, dispatch.ubus);
94
95 if (!du->header_sent)
96 return ops->client_error(cl, 204, "No content", "Function did not return data");
97
98 ops->request_done(cl);
99 }
100
101 static void uh_ubus_close_fds(struct client *cl)
102 {
103 if (ctx->sock.fd < 0)
104 return;
105
106 close(ctx->sock.fd);
107 ctx->sock.fd = -1;
108 }
109
110 static void uh_ubus_request_free(struct client *cl)
111 {
112 struct dispatch_ubus *du = &cl->dispatch.ubus;
113
114 if (du->jsobj)
115 json_object_put(du->jsobj);
116
117 if (du->jstok)
118 json_tokener_free(du->jstok);
119
120 if (du->req_pending)
121 ubus_abort_request(ctx, &du->req);
122 }
123
124 static void uh_ubus_json_error(struct client *cl)
125 {
126 ops->client_error(cl, 400, "Bad Request", "Invalid JSON data");
127 }
128
129 static void uh_ubus_send_request(struct client *cl, json_object *obj)
130 {
131 struct dispatch *d = &cl->dispatch;
132 struct dispatch_ubus *du = &d->ubus;
133 int ret;
134
135 blob_buf_init(&buf, 0);
136
137 if (obj && !blobmsg_add_object(&buf, obj))
138 return uh_ubus_json_error(cl);
139
140 ret = ubus_invoke_async(ctx, du->obj, du->func, buf.head, &du->req);
141 if (ret)
142 return ops->client_error(cl, 500, "Internal Error",
143 "Error sending ubus request: %s", ubus_strerror(ret));
144
145 du->req.data_cb = uh_ubus_request_data_cb;
146 du->req.complete_cb = uh_ubus_request_cb;
147 ubus_complete_request_async(ctx, &du->req);
148
149 du->req_pending = true;
150 }
151
152 static void uh_ubus_data_done(struct client *cl)
153 {
154 struct dispatch_ubus *du = &cl->dispatch.ubus;
155 struct json_object *obj = du->jsobj;
156
157 if (!obj || json_object_get_type(obj) != json_type_object)
158 return uh_ubus_json_error(cl);
159
160 uh_ubus_send_request(cl, obj);
161 }
162
163 static int uh_ubus_data_send(struct client *cl, const char *data, int len)
164 {
165 struct dispatch_ubus *du = &cl->dispatch.ubus;
166
167 if (du->jsobj) {
168 uh_ubus_json_error(cl);
169 return 0;
170 }
171
172 du->post_len += len;
173 if (du->post_len > UH_UBUS_MAX_POST_SIZE) {
174 ops->client_error(cl, 413, "Too Large", "Message too big");
175 return 0;
176 }
177
178 du->jsobj = json_tokener_parse_ex(du->jstok, data, len);
179 return len;
180 }
181
182 static void uh_ubus_defer_post(struct client *cl)
183 {
184 struct dispatch *d = &cl->dispatch;
185
186 d->ubus.jstok = json_tokener_new();
187 if (d->ubus.jstok)
188 return ops->client_error(cl, 500, "Internal Error", "Internal Error");
189
190 d->data_send = uh_ubus_data_send;
191 d->data_done = uh_ubus_data_done;
192 }
193
194 static void uh_ubus_handle_request(struct client *cl, char *url, struct path_info *pi)
195 {
196 struct uh_ubus_session *ses;
197 struct dispatch *d = &cl->dispatch;
198 char *sid, *obj, *fun;
199
200 blob_buf_init(&buf, 0);
201
202 if (!uh_ubus_request_parse_url(cl, url, &sid, &obj, &fun))
203 return ops->client_error(cl, 400, "Bad Request", "Invalid Request");
204
205 ses = uh_ubus_session_get(sid);
206 if (!ses)
207 return ops->client_error(cl, 404, "Not Found", "No such session %s", sid);
208
209 if (!uh_ubus_session_acl_allowed(ses, obj, fun))
210 return ops->client_error(cl, 403, "Denied", "Access to object denied");
211
212 if (ubus_lookup_id(ctx, obj, &d->ubus.obj))
213 return ops->client_error(cl, 500, "Not Found", "No such object");
214
215 d->close_fds = uh_ubus_close_fds;
216 d->free = uh_ubus_request_free;
217 d->ubus.func = fun;
218
219 if (cl->request.method == UH_HTTP_MSG_POST)
220 uh_ubus_defer_post(cl);
221 else
222 uh_ubus_send_request(cl, NULL);
223 }
224
225 static bool
226 uh_ubus_check_url(const char *url)
227 {
228 return ops->path_match(conf.ubus_prefix, url);
229 }
230
231 static int
232 uh_ubus_init(void)
233 {
234 static struct dispatch_handler ubus_dispatch = {
235 .check_url = uh_ubus_check_url,
236 .handle_request = uh_ubus_handle_request,
237 };
238
239 ctx = ubus_connect(conf.ubus_socket);
240 if (!ctx) {
241 fprintf(stderr, "Unable to connect to ubus socket\n");
242 exit(1);
243 }
244
245 ops->dispatch_add(&ubus_dispatch);
246 if (ubus_session_api_init(ctx)) {
247 fprintf(stderr, "Unable to initialize ubus session API\n");
248 exit(1);
249 }
250
251 uloop_done();
252 return 0;
253 }
254
255
256 static int uh_ubus_plugin_init(const struct uhttpd_ops *o, struct config *c)
257 {
258 ops = o;
259 _conf = c;
260 return uh_ubus_init();
261 }
262
263 static void uh_ubus_post_init(void)
264 {
265 ubus_add_uloop(ctx);
266 }
267
268 const struct uhttpd_plugin uhttpd_plugin = {
269 .init = uh_ubus_plugin_init,
270 .post_init = uh_ubus_post_init,
271 };