-/*
- * Copyright (C) 2015 "IoT.bzh"
+/*
+ * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
* Author José Bollo <jose.bollo@iot.bzh>
*
* Licensed under the Apache License, Version 2.0 (the "License");
*/
#define _GNU_SOURCE
+#define NO_PLUGIN_VERBOSE_MACRO
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <systemd/sd-bus.h>
-#include <json.h>
+#include <json-c/json.h>
-#include "afb-plugin.h"
-#include "afb-req-itf.h"
+#include <afb/afb-event.h>
#include "afb-common.h"
-#include "session.h"
-#include "afb-apis.h"
-#include "afb-api-so.h"
+#include "afb-session.h"
+#include "afb-msg-json.h"
+#include "afb-api.h"
+#include "afb-apiset.h"
+#include "afb-api-dbus.h"
#include "afb-context.h"
+#include "afb-cred.h"
+#include "afb-evt.h"
+#include "afb-xreq.h"
#include "verbose.h"
static const char DEFAULT_PATH_PREFIX[] = "/org/agl/afb/api/";
+struct dbus_memo;
+struct dbus_event;
+struct origin;
+
/*
* The path given are of the form
* system:/org/agl/afb/api/...
char *path; /* path of the object for the API */
char *name; /* name/interface of the object */
char *api; /* api name of the interface */
+ union {
+ struct {
+ struct sd_bus_slot *slot_broadcast;
+ struct sd_bus_slot *slot_event;
+ struct dbus_event *events;
+ struct dbus_memo *memos;
+ } client;
+ struct {
+ struct sd_bus_slot *slot_call;
+ struct afb_evt_listener *listener; /* listener for broadcasted events */
+ struct origin *origins;
+ struct afb_apiset *apiset;
+ } server;
+ };
};
#define RETOK 1
#define RETERR 2
-#define RETRAW 3
/******************* common part **********************************/
goto error2;
}
api->api++;
+ if (!afb_api_is_valid_name(api->api, 1)) {
+ errno = EINVAL;
+ goto error2;
+ }
/* the name/interface is copied after the path */
api->name = &api->path[pathlen + 1];
* structure for recording query data
*/
struct dbus_memo {
- struct afb_req req; /* the request handle */
- struct afb_context *context; /* the context of the query */
+ struct dbus_memo *next; /* the next memo */
+ struct api_dbus *api; /* the dbus api */
+ struct afb_xreq *xreq; /* the request */
+ uint64_t msgid; /* the message identifier */
+};
+
+struct dbus_event
+{
+ struct dbus_event *next;
+ struct afb_eventid *eventid;
+ int id;
+ int refcount;
};
/* allocates and init the memorizing data */
-static struct dbus_memo *api_dbus_client_make_memo(struct afb_req req, struct afb_context *context)
+static struct dbus_memo *api_dbus_client_memo_make(struct api_dbus *api, struct afb_xreq *xreq)
{
struct dbus_memo *memo;
memo = malloc(sizeof *memo);
if (memo != NULL) {
- afb_req_addref(req);
- memo->req = req;
- memo->context = context;
+ afb_xreq_unhooked_addref(xreq);
+ memo->xreq = xreq;
+ memo->msgid = 0;
+ memo->api = api;
+ memo->next = api->client.memos;
+ api->client.memos = memo;
}
return memo;
}
/* free and release the memorizing data */
-static void api_dbus_client_free_memo(struct dbus_memo *memo)
+static void api_dbus_client_memo_destroy(struct dbus_memo *memo)
{
- afb_req_unref(memo->req);
+ struct dbus_memo **prv;
+
+ prv = &memo->api->client.memos;
+ while (*prv != NULL) {
+ if (*prv == memo) {
+ *prv = memo->next;
+ break;
+ }
+ prv = &(*prv)->next;
+ }
+
+ afb_xreq_unhooked_unref(memo->xreq);
free(memo);
}
+/* search a memorized request */
+static struct dbus_memo *api_dbus_client_memo_search(struct api_dbus *api, uint64_t msgid)
+{
+ struct dbus_memo *memo;
+
+ memo = api->client.memos;
+ while (memo != NULL && memo->msgid != msgid)
+ memo = memo->next;
+
+ return memo;
+}
+
/* callback when received answer */
static int api_dbus_client_on_reply(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
{
rc = sd_bus_message_read(message, "yssu", &type, &first, &second, &flags);
if (rc < 0) {
/* failing to have the answer */
- afb_req_fail(memo->req, "error", "dbus error");
+ afb_xreq_fail(memo->xreq, "error", "dbus error");
} else {
/* report the answer */
- memo->context->flags = (unsigned)flags;
+ memo->xreq->context.flags = (unsigned)flags;
switch(type) {
case RETOK:
- afb_req_success(memo->req, json_tokener_parse(first), second);
+ afb_xreq_success(memo->xreq, json_tokener_parse(first), *second ? second : NULL);
break;
case RETERR:
- afb_req_fail(memo->req, first, second);
- break;
- case RETRAW:
- afb_req_send(memo->req, first, strlen(first));
+ afb_xreq_fail(memo->xreq, first, *second ? second : NULL);
break;
default:
- afb_req_fail(memo->req, "error", "dbus link broken");
+ afb_xreq_fail(memo->xreq, "error", "dbus link broken");
break;
}
}
- api_dbus_client_free_memo(memo);
+ api_dbus_client_memo_destroy(memo);
return 1;
}
/* on call, propagate it to the dbus service */
-static void api_dbus_client_call(struct api_dbus *api, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb)
+static void api_dbus_client_call(void *closure, struct afb_xreq *xreq)
{
+ struct api_dbus *api = closure;
size_t size;
int rc;
- char *method = strndupa(verb, lenverb);
struct dbus_memo *memo;
+ struct sd_bus_message *msg;
/* create the recording data */
- memo = api_dbus_client_make_memo(req, context);
+ memo = api_dbus_client_memo_make(api, xreq);
if (memo == NULL) {
- afb_req_fail(req, "error", "out of memory");
+ afb_xreq_fail(xreq, "error", "out of memory");
return;
}
+ /* creates the message */
+ msg = NULL;
+ rc = sd_bus_message_new_method_call(api->sdbus, &msg, api->name, api->path, api->name, xreq->request.verb);
+ if (rc < 0)
+ goto error;
+
+ rc = sd_bus_message_append(msg, "ssu",
+ afb_xreq_raw(xreq, &size),
+ afb_session_uuid(xreq->context.session),
+ (uint32_t)xreq->context.flags);
+ if (rc < 0)
+ goto error;
+
/* makes the call */
- rc = sd_bus_call_method_async(api->sdbus, NULL,
- api->name, api->path, api->name, method,
- api_dbus_client_on_reply, memo,
- "ssu",
- afb_req_raw(req, &size),
- ctxClientGetUuid(context->session),
- (uint32_t)context->flags);
+ rc = sd_bus_call_async(api->sdbus, NULL, msg, api_dbus_client_on_reply, memo, (uint64_t)-1);
+ if (rc < 0)
+ goto error;
+
+ rc = sd_bus_message_get_cookie(msg, &memo->msgid);
+ if (rc >= 0)
+ goto end;
+error:
/* if there was an error report it directly */
+ errno = -rc;
+ afb_xreq_fail(xreq, "error", "dbus error");
+ api_dbus_client_memo_destroy(memo);
+end:
+ sd_bus_message_unref(msg);
+}
+
+/* receives broadcasted events */
+static int api_dbus_client_on_broadcast_event(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
+{
+ struct json_object *object;
+ const char *event, *data;
+ int rc = sd_bus_message_read(m, "ss", &event, &data);
+ if (rc < 0)
+ ERROR("unreadable broadcasted event");
+ else {
+ object = json_tokener_parse(data);
+ afb_evt_broadcast(event, object);
+ }
+ return 1;
+}
+
+/* search the eventid */
+static struct dbus_event *api_dbus_client_event_search(struct api_dbus *api, int id, const char *name)
+{
+ struct dbus_event *ev;
+
+ ev = api->client.events;
+ while (ev != NULL && (ev->id != id || 0 != strcmp(afb_evt_eventid_fullname(ev->eventid), name)))
+ ev = ev->next;
+
+ return ev;
+}
+
+/* adds an eventid */
+static void api_dbus_client_event_create(struct api_dbus *api, int id, const char *name)
+{
+ struct dbus_event *ev;
+
+ /* check conflicts */
+ ev = api_dbus_client_event_search(api, id, name);
+ if (ev != NULL) {
+ ev->refcount++;
+ return;
+ }
+
+ /* no conflict, try to add it */
+ ev = malloc(sizeof *ev);
+ if (ev != NULL) {
+ ev->eventid = afb_evt_eventid_create(name);
+ if (ev->eventid == NULL)
+ free(ev);
+ else {
+ ev->refcount = 1;
+ ev->id = id;
+ ev->next = api->client.events;
+ api->client.events = ev;
+ return;
+ }
+ }
+ ERROR("can't create event %s, out of memory", name);
+}
+
+/* removes an eventid */
+static void api_dbus_client_event_drop(struct api_dbus *api, int id, const char *name)
+{
+ struct dbus_event *ev, **prv;
+
+ /* retrieves the event */
+ ev = api_dbus_client_event_search(api, id, name);
+ if (ev == NULL) {
+ ERROR("event %s not found", name);
+ return;
+ }
+
+ /* decrease the reference count */
+ if (--ev->refcount)
+ return;
+
+ /* unlinks the event */
+ prv = &api->client.events;
+ while (*prv != ev)
+ prv = &(*prv)->next;
+ *prv = ev->next;
+
+ /* destroys the event */
+ afb_evt_eventid_unref(ev->eventid);
+ free(ev);
+}
+
+/* pushs an event */
+static void api_dbus_client_event_push(struct api_dbus *api, int id, const char *name, const char *data)
+{
+ struct json_object *object;
+ struct dbus_event *ev;
+
+ /* retrieves the event */
+ ev = api_dbus_client_event_search(api, id, name);
+ if (ev == NULL) {
+ ERROR("event %s not found", name);
+ return;
+ }
+
+ /* destroys the event */
+ object = json_tokener_parse(data);
+ afb_evt_eventid_push(ev->eventid, object);
+}
+
+/* subscribes an event */
+static void api_dbus_client_event_subscribe(struct api_dbus *api, int id, const char *name, uint64_t msgid)
+{
+ int rc;
+ struct dbus_event *ev;
+ struct dbus_memo *memo;
+
+ /* retrieves the event */
+ ev = api_dbus_client_event_search(api, id, name);
+ if (ev == NULL) {
+ ERROR("event %s not found", name);
+ return;
+ }
+
+ /* retrieves the memo */
+ memo = api_dbus_client_memo_search(api, msgid);
+ if (memo == NULL) {
+ ERROR("message not found");
+ return;
+ }
+
+ /* subscribe the request to the event */
+ rc = afb_xreq_subscribe(memo->xreq, ev->eventid);
+ if (rc < 0)
+ ERROR("can't subscribe: %m");
+}
+
+/* unsubscribes an event */
+static void api_dbus_client_event_unsubscribe(struct api_dbus *api, int id, const char *name, uint64_t msgid)
+{
+ int rc;
+ struct dbus_event *ev;
+ struct dbus_memo *memo;
+
+ /* retrieves the event */
+ ev = api_dbus_client_event_search(api, id, name);
+ if (ev == NULL) {
+ ERROR("event %s not found", name);
+ return;
+ }
+
+ /* retrieves the memo */
+ memo = api_dbus_client_memo_search(api, msgid);
+ if (memo == NULL) {
+ ERROR("message not found");
+ return;
+ }
+
+ /* unsubscribe the request from the event */
+ rc = afb_xreq_unsubscribe(memo->xreq, ev->eventid);
+ if (rc < 0)
+ ERROR("can't unsubscribe: %m");
+}
+
+/* receives calls for event */
+static int api_dbus_client_on_manage_event(sd_bus_message *m, void *userdata, sd_bus_error *ret_error)
+{
+ const char *eventname, *data;
+ int rc;
+ int32_t eventid;
+ uint8_t order;
+ struct api_dbus *api;
+ uint64_t msgid;
+
+ /* check if expected message */
+ api = userdata;
+ if (0 != strcmp(api->name, sd_bus_message_get_interface(m)))
+ return 0; /* not the expected interface */
+ if (0 != strcmp("event", sd_bus_message_get_member(m)))
+ return 0; /* not the expected member */
+ if (sd_bus_message_get_expect_reply(m))
+ return 0; /* not the expected type of message */
+
+ /* reads the message */
+ rc = sd_bus_message_read(m, "yisst", &order, &eventid, &eventname, &data, &msgid);
if (rc < 0) {
- errno = -rc;
- afb_req_fail(req, "error", "dbus error");
- api_dbus_client_free_memo(memo);
+ ERROR("unreadable event");
+ return 1;
+ }
+
+ /* what is the order ? */
+ switch ((char)order) {
+ case '+': /* creates the event */
+ api_dbus_client_event_create(api, eventid, eventname);
+ break;
+ case '-': /* drops the event */
+ api_dbus_client_event_drop(api, eventid, eventname);
+ break;
+ case '!': /* pushs the event */
+ api_dbus_client_event_push(api, eventid, eventname, data);
+ break;
+ case 'S': /* subscribe event for a request */
+ api_dbus_client_event_subscribe(api, eventid, eventname, msgid);
+ break;
+ case 'U': /* unsubscribe event for a request */
+ api_dbus_client_event_unsubscribe(api, eventid, eventname, msgid);
+ break;
+ default:
+ /* unexpected order */
+ ERROR("unexpected order '%c' received", (char)order);
+ break;
}
+ return 1;
}
+static struct afb_api_itf dbus_api_itf = {
+ .call = api_dbus_client_call
+};
+
/* adds a afb-dbus-service client api */
-int afb_api_dbus_add_client(const char *path)
+int afb_api_dbus_add_client(const char *path, struct afb_apiset *apiset)
{
+ int rc;
struct api_dbus *api;
struct afb_api afb_api;
+ char *match;
/* create the dbus client api */
api = make_api_dbus(path);
if (api == NULL)
goto error;
+ /* connect to broadcasted events */
+ rc = asprintf(&match, "type='signal',path='%s',interface='%s',member='broadcast'", api->path, api->name);
+ if (rc < 0) {
+ errno = ENOMEM;
+ ERROR("out of memory");
+ goto error;
+ }
+ rc = sd_bus_add_match(api->sdbus, &api->client.slot_broadcast, match, api_dbus_client_on_broadcast_event, api);
+ free(match);
+ if (rc < 0) {
+ errno = -rc;
+ ERROR("can't add dbus match %s for %s", api->path, api->name);
+ goto error;
+ }
+
+ /* connect to event management */
+ rc = sd_bus_add_object(api->sdbus, &api->client.slot_event, api->path, api_dbus_client_on_manage_event, api);
+ if (rc < 0) {
+ errno = -rc;
+ ERROR("can't add dbus object %s for %s", api->path, api->name);
+ goto error;
+ }
+
/* record it as an API */
afb_api.closure = api;
- afb_api.call = (void*)api_dbus_client_call;
- if (afb_apis_add(api->api, afb_api) < 0)
+ afb_api.itf = &dbus_api_itf;
+ afb_api.group = NULL;
+ if (afb_apiset_add(apiset, api->api, afb_api) < 0)
goto error2;
return 0;
return -1;
}
-/******************* dbus request part for server *****************/
+/******************* event structures for server part **********************************/
-/*
- * structure for a dbus request
- */
-struct dbus_req {
- struct afb_context context; /* the context, should be THE FIRST */
- sd_bus_message *message; /* the incoming request message */
- const char *request; /* the readen request as string */
- struct json_object *json; /* the readen request as object */
- int refcount; /* reference count of the request */
+static void afb_api_dbus_server_event_add(void *closure, const char *event, int eventid);
+static void afb_api_dbus_server_event_remove(void *closure, const char *event, int eventid);
+static void afb_api_dbus_server_event_push(void *closure, const char *event, int eventid, struct json_object *object);
+static void afb_api_dbus_server_event_broadcast(void *closure, const char *event, int eventid, struct json_object *object);
+
+/* the interface for events broadcasting */
+static const struct afb_evt_itf evt_broadcast_itf = {
+ .broadcast = afb_api_dbus_server_event_broadcast,
+};
+
+/* the interface for events pushing */
+static const struct afb_evt_itf evt_push_itf = {
+ .push = afb_api_dbus_server_event_push,
+ .add = afb_api_dbus_server_event_add,
+ .remove = afb_api_dbus_server_event_remove
+};
+
+/******************* origin description part for server *****************************/
+
+struct origin
+{
+ /* link to next different origin */
+ struct origin *next;
+
+ /* the server dbus-api */
+ struct api_dbus *api;
+
+ /* count of references */
+ int refcount;
+
+ /* credentials of the origin */
+ struct afb_cred *cred;
+
+ /* the origin */
+ char name[1];
};
-/* increment the reference count of the request */
-static void dbus_req_addref(struct dbus_req *dreq)
+/* get the credentials for the message */
+static void init_origin_creds(struct origin *origin)
{
- dreq->refcount++;
+ int rc;
+ sd_bus_creds *c;
+ uid_t uid;
+ gid_t gid;
+ pid_t pid;
+ const char *context;
+
+ rc = sd_bus_get_name_creds(origin->api->sdbus, origin->name,
+ SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_SELINUX_CONTEXT,
+ &c);
+ if (rc < 0)
+ origin->cred = NULL;
+ else {
+ afb_cred_unref(origin->cred);
+ sd_bus_creds_get_uid(c, &uid);
+ sd_bus_creds_get_gid(c, &gid);
+ sd_bus_creds_get_pid(c, &pid);
+ sd_bus_creds_get_selinux_context(c, &context);
+ origin->cred = afb_cred_create(uid, gid, pid, context);
+ sd_bus_creds_unref(c);
+ }
}
+static struct origin *afb_api_dbus_server_origin_get(struct api_dbus *api, const char *sender)
+{
+ struct origin *origin;
+
+ /* searchs for an existing origin */
+ origin = api->server.origins;
+ while (origin != NULL) {
+ if (0 == strcmp(origin->name, sender)) {
+ origin->refcount++;
+ return origin;
+ }
+ origin = origin->next;
+ }
+
+ /* not found, create it */
+ origin = malloc(strlen(sender) + sizeof *origin);
+ if (origin == NULL)
+ errno = ENOMEM;
+ else {
+ origin->api = api;
+ origin->refcount = 1;
+ strcpy(origin->name, sender);
+ init_origin_creds(origin);
+ origin->next = api->server.origins;
+ api->server.origins = origin;
+ }
+ return origin;
+}
+
+static void afb_api_dbus_server_origin_unref(struct origin *origin)
+{
+ if (!--origin->refcount) {
+ struct origin **prv;
+
+ prv = &origin->api->server.origins;
+ while(*prv != origin)
+ prv = &(*prv)->next;
+ *prv = origin->next;
+ afb_cred_unref(origin->cred);
+ free(origin);
+ }
+}
+
+struct listener
+{
+ /* link to next different origin */
+ struct origin *origin;
+
+ /* the listener of events */
+ struct afb_evt_listener *listener;
+};
+
+static void afb_api_dbus_server_listener_free(struct listener *listener)
+{
+ afb_evt_listener_unref(listener->listener);
+ afb_api_dbus_server_origin_unref(listener->origin);
+ free(listener);
+}
+
+static struct listener *afb_api_dbus_server_listener_get(struct api_dbus *api, const char *sender, struct afb_session *session)
+{
+ int rc;
+ struct listener *listener;
+ struct origin *origin;
+
+ /* get the origin */
+ origin = afb_api_dbus_server_origin_get(api, sender);
+ if (origin == NULL)
+ return NULL;
+
+ /* retrieves the stored listener */
+ listener = afb_session_get_cookie(session, origin);
+ if (listener != NULL) {
+ /* found */
+ afb_api_dbus_server_origin_unref(origin);
+ return listener;
+ }
+
+ /* creates the listener */
+ listener = malloc(sizeof *listener);
+ if (listener == NULL)
+ errno = ENOMEM;
+ else {
+ listener->origin = origin;
+ listener->listener = afb_evt_listener_create(&evt_push_itf, origin);
+ if (listener->listener != NULL) {
+ rc = afb_session_set_cookie(session, origin, listener, (void*)afb_api_dbus_server_listener_free);
+ if (rc == 0)
+ return listener;
+ afb_evt_listener_unref(listener->listener);
+ }
+ free(listener);
+ }
+ afb_api_dbus_server_origin_unref(origin);
+ return NULL;
+}
+
+/******************* dbus request part for server *****************/
+
+/**
+ * Structure for a dbus request
+ */
+struct dbus_req {
+ struct afb_xreq xreq; /**< the xreq of the request */
+ sd_bus_message *message; /**< the incoming request message */
+ const char *request; /**< the readen request as string */
+ struct json_object *json; /**< the readen request as object */
+ struct listener *listener; /**< the listener for events */
+};
+
/* decrement the reference count of the request and free/release it on falling to null */
-static void dbus_req_unref(struct dbus_req *dreq)
+static void dbus_req_destroy(struct afb_xreq *xreq)
{
- if (dreq == NULL || --dreq->refcount)
- return;
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
- afb_context_disconnect(&dreq->context);
+ afb_context_disconnect(&dreq->xreq.context);
json_object_put(dreq->json);
sd_bus_message_unref(dreq->message);
free(dreq);
}
/* get the object of the request */
-static struct json_object *dbus_req_json(struct dbus_req *dreq)
+static struct json_object *dbus_req_json(struct afb_xreq *xreq)
{
- if (dreq->json == NULL) {
- dreq->json = json_tokener_parse(dreq->request);
- if (dreq->json == NULL) {
- /* lazy error detection of json request. Is it to improve? */
- dreq->json = json_object_new_string(dreq->request);
- }
- }
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
+
return dreq->json;
}
/* get the argument of the request of 'name' */
-static struct afb_arg dbus_req_get(struct dbus_req *dreq, const char *name)
-{
- struct afb_arg arg;
- struct json_object *value, *root;
-
- root = dbus_req_json(dreq);
- if (root != NULL && json_object_object_get_ex(root, name, &value)) {
- arg.name = name;
- arg.value = json_object_get_string(value);
- } else {
- arg.name = NULL;
- arg.value = NULL;
- }
- arg.path = NULL;
- return arg;
-}
-
static void dbus_req_reply(struct dbus_req *dreq, uint8_t type, const char *first, const char *second)
{
int rc;
rc = sd_bus_reply_method_return(dreq->message,
- "yssu", type, first, second, (uint32_t)dreq->context.flags);
+ "yssu", type, first ? : "", second ? : "", (uint32_t)dreq->xreq.context.flags);
+ if (rc < 0)
+ ERROR("sending the reply failed");
}
-static void dbus_req_success(struct dbus_req *dreq, struct json_object *obj, const char *info)
+static void dbus_req_success(struct afb_xreq *xreq, struct json_object *obj, const char *info)
{
- dbus_req_reply(dreq, RETOK, json_object_to_json_string(obj), info);
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
+
+ dbus_req_reply(dreq, RETOK, json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN), info);
}
-static void dbus_req_fail(struct dbus_req *dreq, const char *status, const char *info)
+static void dbus_req_fail(struct afb_xreq *xreq, const char *status, const char *info)
{
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
+
dbus_req_reply(dreq, RETERR, status, info);
}
-static const char *dbus_req_raw(struct dbus_req *dreq, size_t *size)
+static void afb_api_dbus_server_event_send(struct origin *origin, char order, const char *event, int eventid, const char *data, uint64_t msgid);
+
+static int dbus_req_subscribe(struct afb_xreq *xreq, struct afb_eventid *eventid)
{
- if (size != NULL)
- *size = strlen(dreq->request);
- return dreq->request;
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
+ uint64_t msgid;
+ int rc;
+
+ rc = afb_evt_eventid_add_watch(dreq->listener->listener, eventid);
+ sd_bus_message_get_cookie(dreq->message, &msgid);
+ afb_api_dbus_server_event_send(dreq->listener->origin, 'S', afb_evt_eventid_fullname(eventid), afb_evt_eventid_id(eventid), "", msgid);
+ return rc;
}
-static void dbus_req_send(struct dbus_req *dreq, const char *buffer, size_t size)
+static int dbus_req_unsubscribe(struct afb_xreq *xreq, struct afb_eventid *eventid)
{
- /* TODO: how to put sized buffer as strings? things aren't clear here!!! */
- dbus_req_reply(dreq, RETRAW, buffer, "");
+ struct dbus_req *dreq = CONTAINER_OF_XREQ(struct dbus_req, xreq);
+ uint64_t msgid;
+ int rc;
+
+ sd_bus_message_get_cookie(dreq->message, &msgid);
+ afb_api_dbus_server_event_send(dreq->listener->origin, 'U', afb_evt_eventid_fullname(eventid), afb_evt_eventid_id(eventid), "", msgid);
+ rc = afb_evt_eventid_remove_watch(dreq->listener->listener, eventid);
+ return rc;
}
-struct afb_req_itf dbus_req_itf = {
- .json = (void*)dbus_req_json,
- .get = (void*)dbus_req_get,
- .success = (void*)dbus_req_success,
- .fail = (void*)dbus_req_fail,
- .raw = (void*)dbus_req_raw,
- .send = (void*)dbus_req_send,
- .context_get = (void*)afb_context_get,
- .context_set = (void*)afb_context_set,
- .addref = (void*)dbus_req_addref,
- .unref = (void*)dbus_req_unref
+static void dbus_req_subcall(
+ struct afb_xreq *xreq,
+ const char *api,
+ const char *verb,
+ struct json_object *args,
+ void (*callback)(void*, int, struct json_object*),
+ void *cb_closure)
+{
+ ERROR("DBUS API doesn't support subcalls, info: %s/%s(%s)", api, verb, json_object_to_json_string(args));
+ callback(cb_closure, 1, afb_msg_json_reply_error("error", "subcall isn't supported", NULL, NULL));
+ json_object_put(args);
+}
+
+const struct afb_xreq_query_itf afb_api_dbus_xreq_itf = {
+ .json = dbus_req_json,
+ .success = dbus_req_success,
+ .fail = dbus_req_fail,
+ .unref = dbus_req_destroy,
+ .subscribe = dbus_req_subscribe,
+ .unsubscribe = dbus_req_unsubscribe,
+ .subcall = dbus_req_subcall
};
/******************* server part **********************************/
+static void afb_api_dbus_server_event_send(struct origin *origin, char order, const char *event, int eventid, const char *data, uint64_t msgid)
+{
+ int rc;
+ struct api_dbus *api;
+ struct sd_bus_message *msg;
+
+ api = origin->api;
+ msg = NULL;
+
+ rc = sd_bus_message_new_method_call(api->sdbus, &msg, origin->name, api->path, api->name, "event");
+ if (rc < 0)
+ goto error;
+
+ rc = sd_bus_message_append(msg, "yisst", (uint8_t)order, (int32_t)eventid, event, data, msgid);
+ if (rc < 0)
+ goto error;
+
+ rc = sd_bus_send(api->sdbus, msg, NULL); /* NULL for cookie implies no expected reply */
+ if (rc >= 0)
+ goto end;
+
+error:
+ ERROR("error while send event %c%s(%d) to %s", order, event, eventid, origin->name);
+end:
+ sd_bus_message_unref(msg);
+}
+
+static void afb_api_dbus_server_event_add(void *closure, const char *event, int eventid)
+{
+ afb_api_dbus_server_event_send(closure, '+', event, eventid, "", 0);
+}
+
+static void afb_api_dbus_server_event_remove(void *closure, const char *event, int eventid)
+{
+ afb_api_dbus_server_event_send(closure, '-', event, eventid, "", 0);
+}
+
+static void afb_api_dbus_server_event_push(void *closure, const char *event, int eventid, struct json_object *object)
+{
+ const char *data = json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN);
+ afb_api_dbus_server_event_send(closure, '!', event, eventid, data, 0);
+ json_object_put(object);
+}
+
+static void afb_api_dbus_server_event_broadcast(void *closure, const char *event, int eventid, struct json_object *object)
+{
+ int rc;
+ struct api_dbus *api;
+
+ api = closure;
+ rc = sd_bus_emit_signal(api->sdbus, api->path, api->name, "broadcast",
+ "ss", event, json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN));
+ if (rc < 0)
+ ERROR("error while broadcasting event %s", event);
+ json_object_put(object);
+}
+
/* called when the object for the service is called */
static int api_dbus_server_on_object_called(sd_bus_message *message, void *userdata, sd_bus_error *ret_error)
{
const char *uuid;
struct dbus_req *dreq;
struct api_dbus *api = userdata;
- struct afb_req areq;
uint32_t flags;
+ struct afb_session *session;
+ struct listener *listener;
/* check the interface */
if (strcmp(sd_bus_message_get_interface(message), api->name) != 0)
/* create the request */
dreq = calloc(1 , sizeof *dreq);
- if (dreq == NULL) {
- sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
- return 1;
- }
+ if (dreq == NULL)
+ goto out_of_memory;
/* get the data */
rc = sd_bus_message_read(message, "ssu", &dreq->request, &uuid, &flags);
if (rc < 0) {
sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_SIGNATURE, "invalid signature");
- free(dreq);
- return 1;
+ goto error;
}
/* connect to the context */
- if (afb_context_connect(&dreq->context, uuid, NULL) < 0) {
- sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
- free(dreq);
- return 1;
- }
+ afb_xreq_init(&dreq->xreq, &afb_api_dbus_xreq_itf);
+ if (afb_context_connect(&dreq->xreq.context, uuid, NULL) < 0)
+ goto out_of_memory;
+ session = dreq->xreq.context.session;
+
+ /* get the listener */
+ listener = afb_api_dbus_server_listener_get(api, sd_bus_message_get_sender(message), session);
+ if (listener == NULL)
+ goto out_of_memory;
/* fulfill the request and emit it */
- dreq->context.flags = flags;
+ dreq->xreq.context.flags = flags;
dreq->message = sd_bus_message_ref(message);
- dreq->json = NULL;
- dreq->refcount = 1;
- areq.itf = &dbus_req_itf;
- areq.closure = dreq;
- afb_apis_call_(areq, &dreq->context, api->api, method);
- dbus_req_unref(dreq);
+ dreq->json = json_tokener_parse(dreq->request);
+ if (dreq->json == NULL && strcmp(dreq->request, "null")) {
+ /* lazy error detection of json request. Is it to improve? */
+ dreq->json = json_object_new_string(dreq->request);
+ }
+ dreq->listener = listener;
+ dreq->xreq.request.api = api->api;
+ dreq->xreq.request.verb = method;
+ afb_xreq_process(&dreq->xreq, api->server.apiset);
+ return 1;
+
+out_of_memory:
+ sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory");
+error:
+ free(dreq);
return 1;
}
/* create the service */
-int afb_api_dbus_add_server(const char *path)
+int afb_api_dbus_add_server(const char *path, struct afb_apiset *apiset)
{
int rc;
struct api_dbus *api;
- sd_bus_slot *slot;
/* get the dbus api object connected */
api = make_api_dbus(path);
}
/* connect the service to the dbus object */
- rc = sd_bus_add_object(api->sdbus, &slot, api->path, api_dbus_server_on_object_called, api);
+ rc = sd_bus_add_object(api->sdbus, &api->server.slot_call, api->path, api_dbus_server_on_object_called, api);
if (rc < 0) {
errno = -rc;
ERROR("can't add dbus object %s for %s", api->path, api->name);
}
INFO("afb service over dbus installed, name %s, path %s", api->name, api->path);
+ api->server.listener = afb_evt_listener_create(&evt_broadcast_itf, api);
+ api->server.apiset = afb_apiset_addref(apiset);
return 0;
error3:
sd_bus_release_name(api->sdbus, api->name);