session: introduce api to register session create and destroy callbacks
[project/rpcd.git] / session.c
1 /*
2  * rpcd - UBUS RPC server
3  *
4  *   Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
5  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include <libubox/avl-cmp.h>
21 #include <libubox/utils.h>
22 #include <libubus.h>
23 #include <fnmatch.h>
24
25 #include "session.h"
26
27 static struct avl_tree sessions;
28 static struct blob_buf buf;
29
30 static LIST_HEAD(create_callbacks);
31 static LIST_HEAD(destroy_callbacks);
32
33 static const struct blobmsg_policy new_policy = {
34         .name = "timeout", .type = BLOBMSG_TYPE_INT32
35 };
36
37 static const struct blobmsg_policy sid_policy = {
38         .name = "sid", .type = BLOBMSG_TYPE_STRING
39 };
40
41 enum {
42         RPC_SS_SID,
43         RPC_SS_VALUES,
44         __RPC_SS_MAX,
45 };
46 static const struct blobmsg_policy set_policy[__RPC_SS_MAX] = {
47         [RPC_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
48         [RPC_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
49 };
50
51 enum {
52         RPC_SG_SID,
53         RPC_SG_KEYS,
54         __RPC_SG_MAX,
55 };
56 static const struct blobmsg_policy get_policy[__RPC_SG_MAX] = {
57         [RPC_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
58         [RPC_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY },
59 };
60
61 enum {
62         RPC_SA_SID,
63         RPC_SA_SCOPE,
64         RPC_SA_OBJECTS,
65         __RPC_SA_MAX,
66 };
67 static const struct blobmsg_policy acl_policy[__RPC_SA_MAX] = {
68         [RPC_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
69         [RPC_SA_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
70         [RPC_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
71 };
72
73 enum {
74         RPC_SP_SID,
75         RPC_SP_SCOPE,
76         RPC_SP_OBJECT,
77         RPC_SP_FUNCTION,
78         __RPC_SP_MAX,
79 };
80 static const struct blobmsg_policy perm_policy[__RPC_SP_MAX] = {
81         [RPC_SP_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
82         [RPC_SP_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
83         [RPC_SP_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_STRING },
84         [RPC_SP_FUNCTION] = { .name = "function", .type = BLOBMSG_TYPE_STRING },
85 };
86
87 /*
88  * Keys in the AVL tree contain all pattern characters up to the first wildcard.
89  * To look up entries, start with the last entry that has a key less than or
90  * equal to the method name, then work backwards as long as the AVL key still
91  * matches its counterpart in the object name
92  */
93 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func)         \
94         for (_acl = avl_find_le_element(_avl, _obj, _acl, avl);                 \
95              _acl;                                                                                                              \
96              _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL :                   \
97                     avl_prev_element((_acl), avl))
98
99 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func)                        \
100         uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func)                 \
101                 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) &&         \
102                     !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) &&           \
103                     !fnmatch((_acl)->function, (_func), FNM_NOESCAPE))
104
105 static void
106 rpc_random(char *dest)
107 {
108         unsigned char buf[16] = { 0 };
109         FILE *f;
110         int i;
111
112         f = fopen("/dev/urandom", "r");
113         if (!f)
114                 return;
115
116         fread(buf, 1, sizeof(buf), f);
117         fclose(f);
118
119         for (i = 0; i < sizeof(buf); i++)
120                 sprintf(dest + (i<<1), "%02x", buf[i]);
121 }
122
123 static void
124 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b)
125 {
126         struct rpc_session_data *d;
127
128         avl_for_each_element(&ses->data, d, avl) {
129                 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
130                                   blobmsg_data(d->attr), blobmsg_data_len(d->attr));
131         }
132 }
133
134 static void
135 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b)
136 {
137         struct rpc_session_acl *acl;
138         struct rpc_session_acl_scope *acl_scope;
139         const char *lastobj = NULL;
140         const char *lastscope = NULL;
141         void *c = NULL, *d = NULL;
142
143         avl_for_each_element(&ses->acls, acl_scope, avl) {
144                 if (!lastscope || strcmp(acl_scope->avl.key, lastscope))
145                 {
146                         if (c) blobmsg_close_table(b, c);
147                         c = blobmsg_open_table(b, acl_scope->avl.key);
148                         lastobj = NULL;
149                 }
150
151                 d = NULL;
152
153                 avl_for_each_element(&acl_scope->acls, acl, avl) {
154                         if (!lastobj || strcmp(acl->object, lastobj))
155                         {
156                                 if (d) blobmsg_close_array(b, d);
157                                 d = blobmsg_open_array(b, acl->object);
158                         }
159
160                         blobmsg_add_string(b, NULL, acl->function);
161                         lastobj = acl->object;
162                 }
163
164                 if (d) blobmsg_close_array(b, d);
165         }
166
167         if (c) blobmsg_close_table(b, c);
168 }
169
170 static void
171 rpc_session_dump(struct rpc_session *ses,
172                                          struct ubus_context *ctx,
173                                          struct ubus_request_data *req)
174 {
175         void *c;
176
177         blob_buf_init(&buf, 0);
178
179         blobmsg_add_string(&buf, "sid", ses->id);
180         blobmsg_add_u32(&buf, "timeout", ses->timeout);
181         blobmsg_add_u32(&buf, "expires", uloop_timeout_remaining(&ses->t) / 1000);
182
183         c = blobmsg_open_table(&buf, "acls");
184         rpc_session_dump_acls(ses, &buf);
185         blobmsg_close_table(&buf, c);
186
187         c = blobmsg_open_table(&buf, "data");
188         rpc_session_dump_data(ses, &buf);
189         blobmsg_close_table(&buf, c);
190
191         ubus_send_reply(ctx, req, buf.head);
192 }
193
194 static void
195 rpc_touch_session(struct rpc_session *ses)
196 {
197         uloop_timeout_set(&ses->t, ses->timeout * 1000);
198 }
199
200 static void
201 rpc_session_destroy(struct rpc_session *ses)
202 {
203         struct rpc_session_acl *acl, *nacl;
204         struct rpc_session_acl_scope *acl_scope, *nacl_scope;
205         struct rpc_session_data *data, *ndata;
206         struct rpc_session_cb *cb;
207
208         list_for_each_entry(cb, &destroy_callbacks, list)
209                 cb->cb(ses, cb->priv);
210
211         uloop_timeout_cancel(&ses->t);
212
213         avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) {
214                 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl)
215                         free(acl);
216
217                 avl_delete(&ses->acls, &acl_scope->avl);
218                 free(acl_scope);
219         }
220
221         avl_remove_all_elements(&ses->data, data, avl, ndata)
222                 free(data);
223
224         avl_delete(&sessions, &ses->avl);
225         free(ses);
226 }
227
228 static void rpc_session_timeout(struct uloop_timeout *t)
229 {
230         struct rpc_session *ses;
231
232         ses = container_of(t, struct rpc_session, t);
233         rpc_session_destroy(ses);
234 }
235
236 static struct rpc_session *
237 rpc_session_create(int timeout)
238 {
239         struct rpc_session *ses;
240         struct rpc_session_cb *cb;
241
242         ses = calloc(1, sizeof(*ses));
243         if (!ses)
244                 return NULL;
245
246         ses->timeout  = timeout;
247         ses->avl.key  = ses->id;
248         rpc_random(ses->id);
249
250         avl_insert(&sessions, &ses->avl);
251         avl_init(&ses->acls, avl_strcmp, true, NULL);
252         avl_init(&ses->data, avl_strcmp, false, NULL);
253
254         ses->t.cb = rpc_session_timeout;
255         rpc_touch_session(ses);
256
257         list_for_each_entry(cb, &create_callbacks, list)
258                 cb->cb(ses, cb->priv);
259
260         return ses;
261 }
262
263 static struct rpc_session *
264 rpc_session_get(const char *id)
265 {
266         struct rpc_session *ses;
267
268         ses = avl_find_element(&sessions, id, ses, avl);
269         if (!ses)
270                 return NULL;
271
272         rpc_touch_session(ses);
273         return ses;
274 }
275
276 static int
277 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
278                   struct ubus_request_data *req, const char *method,
279                   struct blob_attr *msg)
280 {
281         struct rpc_session *ses;
282         struct blob_attr *tb;
283         int timeout = RPC_DEFAULT_SESSION_TIMEOUT;
284
285         blobmsg_parse(&new_policy, 1, &tb, blob_data(msg), blob_len(msg));
286         if (tb)
287                 timeout = blobmsg_get_u32(tb);
288
289         ses = rpc_session_create(timeout);
290         if (ses)
291                 rpc_session_dump(ses, ctx, req);
292
293         return 0;
294 }
295
296 static int
297 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
298                 struct ubus_request_data *req, const char *method,
299                 struct blob_attr *msg)
300 {
301         struct rpc_session *ses;
302         struct blob_attr *tb;
303
304         blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
305
306         if (!tb) {
307                 avl_for_each_element(&sessions, ses, avl)
308                         rpc_session_dump(ses, ctx, req);
309                 return 0;
310         }
311
312         ses = rpc_session_get(blobmsg_data(tb));
313         if (!ses)
314                 return UBUS_STATUS_NOT_FOUND;
315
316         rpc_session_dump(ses, ctx, req);
317
318         return 0;
319 }
320
321 static int
322 uh_id_len(const char *str)
323 {
324         return strcspn(str, "*?[");
325 }
326
327 static int
328 rpc_session_grant(struct rpc_session *ses, struct ubus_context *ctx,
329                   const char *scope, const char *object, const char *function)
330 {
331         struct rpc_session_acl *acl;
332         struct rpc_session_acl_scope *acl_scope;
333         char *new_scope, *new_obj, *new_func, *new_id;
334         int id_len;
335
336         if (!object || !function)
337                 return UBUS_STATUS_INVALID_ARGUMENT;
338
339         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
340
341         if (acl_scope) {
342                 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) {
343                         if (!strcmp(acl->object, object) &&
344                                 !strcmp(acl->function, function))
345                                 return 0;
346                 }
347         }
348
349         if (!acl_scope) {
350                 acl_scope = calloc_a(sizeof(*acl_scope),
351                                      &new_scope, strlen(scope) + 1);
352
353                 if (!acl_scope)
354                         return UBUS_STATUS_UNKNOWN_ERROR;
355
356                 acl_scope->avl.key = strcpy(new_scope, scope);
357                 avl_init(&acl_scope->acls, avl_strcmp, true, NULL);
358                 avl_insert(&ses->acls, &acl_scope->avl);
359         }
360
361         id_len = uh_id_len(object);
362         acl = calloc_a(sizeof(*acl),
363                 &new_obj, strlen(object) + 1,
364                 &new_func, strlen(function) + 1,
365                 &new_id, id_len + 1);
366
367         if (!acl)
368                 return UBUS_STATUS_UNKNOWN_ERROR;
369
370         acl->object = strcpy(new_obj, object);
371         acl->function = strcpy(new_func, function);
372         acl->avl.key = strncpy(new_id, object, id_len);
373         avl_insert(&acl_scope->acls, &acl->avl);
374
375         return 0;
376 }
377
378 static int
379 rpc_session_revoke(struct rpc_session *ses, struct ubus_context *ctx,
380                    const char *scope, const char *object, const char *function)
381 {
382         struct rpc_session_acl *acl, *next;
383         struct rpc_session_acl_scope *acl_scope;
384         int id_len;
385         char *id;
386
387         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
388
389         if (!acl_scope)
390                 return 0;
391
392         if (!object && !function) {
393                 avl_remove_all_elements(&acl_scope->acls, acl, avl, next)
394                         free(acl);
395                 avl_delete(&ses->acls, &acl_scope->avl);
396                 free(acl_scope);
397                 return 0;
398         }
399
400         id_len = uh_id_len(object);
401         id = alloca(id_len + 1);
402         strncpy(id, object, id_len);
403         id[id_len] = 0;
404
405         acl = avl_find_element(&acl_scope->acls, id, acl, avl);
406         while (acl) {
407                 if (!avl_is_last(&acl_scope->acls, &acl->avl))
408                         next = avl_next_element(acl, avl);
409                 else
410                         next = NULL;
411
412                 if (strcmp(id, acl->avl.key) != 0)
413                         break;
414
415                 if (!strcmp(acl->object, object) &&
416                     !strcmp(acl->function, function)) {
417                         avl_delete(&acl_scope->acls, &acl->avl);
418                         free(acl);
419                 }
420                 acl = next;
421         }
422
423         if (avl_is_empty(&acl_scope->acls)) {
424                 avl_delete(&ses->acls, &acl_scope->avl);
425                 free(acl_scope);
426         }
427
428         return 0;
429 }
430
431
432 static int
433 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj,
434                struct ubus_request_data *req, const char *method,
435                struct blob_attr *msg)
436 {
437         struct rpc_session *ses;
438         struct blob_attr *tb[__RPC_SA_MAX];
439         struct blob_attr *attr, *sattr;
440         const char *object, *function;
441         const char *scope = "ubus";
442         int rem1, rem2;
443
444         int (*cb)(struct rpc_session *ses, struct ubus_context *ctx,
445                   const char *scope, const char *object, const char *function);
446
447         blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg));
448
449         if (!tb[RPC_SA_SID])
450                 return UBUS_STATUS_INVALID_ARGUMENT;
451
452         ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID]));
453         if (!ses)
454                 return UBUS_STATUS_NOT_FOUND;
455
456         if (tb[RPC_SA_SCOPE])
457                 scope = blobmsg_data(tb[RPC_SA_SCOPE]);
458
459         if (!strcmp(method, "grant"))
460                 cb = rpc_session_grant;
461         else
462                 cb = rpc_session_revoke;
463
464         if (!tb[RPC_SA_OBJECTS])
465                 return cb(ses, ctx, scope, NULL, NULL);
466
467         blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) {
468                 if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
469                         continue;
470
471                 object = NULL;
472                 function = NULL;
473
474                 blobmsg_for_each_attr(sattr, attr, rem2) {
475                         if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
476                                 continue;
477
478                         if (!object)
479                                 object = blobmsg_data(sattr);
480                         else if (!function)
481                                 function = blobmsg_data(sattr);
482                         else
483                                 break;
484                 }
485
486                 if (object && function)
487                         cb(ses, ctx, scope, object, function);
488         }
489
490         return 0;
491 }
492
493 static bool
494 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope,
495                         const char *obj, const char *fun)
496 {
497         struct rpc_session_acl *acl;
498         struct rpc_session_acl_scope *acl_scope;
499
500         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
501
502         if (acl_scope) {
503                 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun)
504                         return true;
505         }
506
507         return false;
508 }
509
510 static int
511 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj,
512                   struct ubus_request_data *req, const char *method,
513                   struct blob_attr *msg)
514 {
515         struct rpc_session *ses;
516         struct blob_attr *tb[__RPC_SP_MAX];
517         const char *scope = "ubus";
518         bool allow;
519
520         blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg));
521
522         if (!tb[RPC_SP_SID] || !tb[RPC_SP_OBJECT] || !tb[RPC_SP_FUNCTION])
523                 return UBUS_STATUS_INVALID_ARGUMENT;
524
525         ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID]));
526         if (!ses)
527                 return UBUS_STATUS_NOT_FOUND;
528
529         if (tb[RPC_SP_SCOPE])
530                 scope = blobmsg_data(tb[RPC_SP_SCOPE]);
531
532         allow = rpc_session_acl_allowed(ses, scope,
533                                                                         blobmsg_data(tb[RPC_SP_OBJECT]),
534                                                                         blobmsg_data(tb[RPC_SP_FUNCTION]));
535
536         blob_buf_init(&buf, 0);
537         blobmsg_add_u8(&buf, "access", allow);
538         ubus_send_reply(ctx, req, buf.head);
539
540         return 0;
541 }
542
543 static int
544 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
545                struct ubus_request_data *req, const char *method,
546                struct blob_attr *msg)
547 {
548         struct rpc_session *ses;
549         struct rpc_session_data *data;
550         struct blob_attr *tb[__RPC_SA_MAX];
551         struct blob_attr *attr;
552         int rem;
553
554         blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg));
555
556         if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES])
557                 return UBUS_STATUS_INVALID_ARGUMENT;
558
559         ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID]));
560         if (!ses)
561                 return UBUS_STATUS_NOT_FOUND;
562
563         blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) {
564                 if (!blobmsg_name(attr)[0])
565                         continue;
566
567                 data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl);
568                 if (data) {
569                         avl_delete(&ses->data, &data->avl);
570                         free(data);
571                 }
572
573                 data = calloc(1, sizeof(*data) + blob_pad_len(attr));
574                 if (!data)
575                         break;
576
577                 memcpy(data->attr, attr, blob_pad_len(attr));
578                 data->avl.key = blobmsg_name(data->attr);
579                 avl_insert(&ses->data, &data->avl);
580         }
581
582         return 0;
583 }
584
585 static int
586 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
587                struct ubus_request_data *req, const char *method,
588                struct blob_attr *msg)
589 {
590         struct rpc_session *ses;
591         struct rpc_session_data *data;
592         struct blob_attr *tb[__RPC_SA_MAX];
593         struct blob_attr *attr;
594         void *c;
595         int rem;
596
597         blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
598
599         if (!tb[RPC_SG_SID])
600                 return UBUS_STATUS_INVALID_ARGUMENT;
601
602         ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
603         if (!ses)
604                 return UBUS_STATUS_NOT_FOUND;
605
606         blob_buf_init(&buf, 0);
607         c = blobmsg_open_table(&buf, "values");
608
609         if (tb[RPC_SG_KEYS])
610                 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
611                         if (blob_id(attr) != BLOBMSG_TYPE_STRING)
612                                 continue;
613
614                         data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
615                         if (!data)
616                                 continue;
617
618                         blobmsg_add_field(&buf, blobmsg_type(data->attr),
619                                           blobmsg_name(data->attr),
620                                           blobmsg_data(data->attr),
621                                           blobmsg_data_len(data->attr));
622                 }
623         else
624                 rpc_session_dump_data(ses, &buf);
625
626         blobmsg_close_table(&buf, c);
627         ubus_send_reply(ctx, req, buf.head);
628
629         return 0;
630 }
631
632 static int
633 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
634                  struct ubus_request_data *req, const char *method,
635                  struct blob_attr *msg)
636 {
637         struct rpc_session *ses;
638         struct rpc_session_data *data, *ndata;
639         struct blob_attr *tb[__RPC_SA_MAX];
640         struct blob_attr *attr;
641         int rem;
642
643         blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
644
645         if (!tb[RPC_SG_SID])
646                 return UBUS_STATUS_INVALID_ARGUMENT;
647
648         ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
649         if (!ses)
650                 return UBUS_STATUS_NOT_FOUND;
651
652         if (!tb[RPC_SG_KEYS]) {
653                 avl_remove_all_elements(&ses->data, data, avl, ndata)
654                         free(data);
655                 return 0;
656         }
657
658         blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
659                 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
660                         continue;
661
662                 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
663                 if (!data)
664                         continue;
665
666                 avl_delete(&ses->data, &data->avl);
667                 free(data);
668         }
669
670         return 0;
671 }
672
673 static int
674 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
675                    struct ubus_request_data *req, const char *method,
676                    struct blob_attr *msg)
677 {
678         struct rpc_session *ses;
679         struct blob_attr *tb;
680
681         blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
682
683         if (!tb)
684                 return UBUS_STATUS_INVALID_ARGUMENT;
685
686         ses = rpc_session_get(blobmsg_data(tb));
687         if (!ses)
688                 return UBUS_STATUS_NOT_FOUND;
689
690         rpc_session_destroy(ses);
691
692         return 0;
693 }
694
695 int rpc_session_api_init(struct ubus_context *ctx)
696 {
697         static const struct ubus_method session_methods[] = {
698                 UBUS_METHOD("create",  rpc_handle_create,  &new_policy),
699                 UBUS_METHOD("list",    rpc_handle_list,    &sid_policy),
700                 UBUS_METHOD("grant",   rpc_handle_acl,     acl_policy),
701                 UBUS_METHOD("revoke",  rpc_handle_acl,     acl_policy),
702                 UBUS_METHOD("access",  rpc_handle_access,  perm_policy),
703                 UBUS_METHOD("set",     rpc_handle_set,     set_policy),
704                 UBUS_METHOD("get",     rpc_handle_get,     get_policy),
705                 UBUS_METHOD("unset",   rpc_handle_unset,   get_policy),
706                 UBUS_METHOD("destroy", rpc_handle_destroy, &sid_policy),
707         };
708
709         static struct ubus_object_type session_type =
710                 UBUS_OBJECT_TYPE("luci-rpc-session", session_methods);
711
712         static struct ubus_object obj = {
713                 .name = "session",
714                 .type = &session_type,
715                 .methods = session_methods,
716                 .n_methods = ARRAY_SIZE(session_methods),
717         };
718
719         avl_init(&sessions, avl_strcmp, false, NULL);
720
721         return ubus_add_object(ctx, &obj);
722 }
723
724 bool rpc_session_access(const char *sid, const char *scope,
725                         const char *object, const char *function)
726 {
727         struct rpc_session *ses = rpc_session_get(sid);
728
729         if (!ses)
730                 return false;
731
732         return rpc_session_acl_allowed(ses, scope, object, function);
733 }
734
735 void rpc_session_create_cb(struct rpc_session_cb *cb)
736 {
737         if (cb && cb->cb)
738                 list_add(&cb->list, &create_callbacks);
739 }
740
741 void rpc_session_destroy_cb(struct rpc_session_cb *cb)
742 {
743         if (cb && cb->cb)
744                 list_add(&cb->list, &destroy_callbacks);
745 }