X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Futils-jbus.c;h=cdf165fc48fa1d16a2c78de462405b7b089e8481;hb=3a6e947bef1b2942e24d2fdee1a76dbf3305b508;hp=7e65723a2b2142690277fbbb0dfc94156dbe84d4;hpb=aa181a38a00903bcbf94518abfc185d3459feace;p=src%2Fapp-framework-main.git diff --git a/src/utils-jbus.c b/src/utils-jbus.c index 7e65723..cdf165f 100644 --- a/src/utils-jbus.c +++ b/src/utils-jbus.c @@ -1,5 +1,5 @@ /* - Copyright 2015 IoT.bzh + Copyright 2015, 2016, 2017 IoT.bzh author: José Bollo @@ -21,105 +21,183 @@ #include #include #include +#include +#include -#include -#include +#include +#include +#include #include "utils-jbus.h" -struct jreq; -struct jservice; -struct jbus; +/* + * max depth of json messages + */ +#define MAX_JSON_DEPTH 10 -/* structure for handled requests */ -struct jreq { - DBusConnection *connection; - DBusMessage *request; -}; +/* + * errors messages generated by jbus + */ +static const char out_of_memory_string[] = "out of memory"; -/* structure for recorded services */ +/* + * structure for services + */ struct jservice { - struct jservice *next; - char *method; - void (*oncall_s)(struct jreq *, const char *); - void (*oncall_j)(struct jreq *, struct json_object *); + struct jservice *next; /* link to the next service */ + char *method; /* method name for the service */ + void (*oncall_s) (struct sd_bus_message *, const char *, void *); + /* string callback */ + void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *); + /* json callback */ + void *data; /* closure data for the callbacks */ }; -/* structure for signal handlers */ +/* + * structure for signals + */ struct jsignal { - struct jsignal *next; - char *name; - void (*onsignal_s)(const char *); - void (*onsignal_j)(struct json_object *); + struct jsignal *next; /* link to the next signal */ + char *name; /* name of the expected signal */ + void (*onsignal_s) (const char *, void *); + /* string callback */ + void (*onsignal_j) (struct json_object *, void *); + /* json callback */ + void *data; /* closure data for the callbacks */ }; -/* structure for recording asynchronous requests */ +/* + * structure for asynchronous requests + */ struct jrespw { - struct jrespw *next; - dbus_uint32_t serial; - void *data; - void (*onresp_s)(int, const char*, void *); - void (*onresp_j)(int, struct json_object*, void *); -}; - -/* structure for synchronous requests */ -struct respsync { - int replied; - char *value; + struct jbus *jbus; + void (*onresp_s) (int, const char *, void *); + /* string callback */ + void (*onresp_j) (int, struct json_object *, void *); + /* json callback */ + void *data; /* closure data for the callbacks */ }; -/* structure for handling either client or server jbus on dbus */ +/* + * structure for handling either client or server jbus on dbus + */ struct jbus { - int refcount; - struct jservice *services; - DBusConnection *connection; - struct jsignal *signals; - struct jrespw *waiters; - char *path; - char *name; + int refcount; /* referenced how many time */ + struct sd_bus *sdbus; + struct sd_bus_slot *sservice; + struct sd_bus_slot *ssignal; + struct json_tokener *tokener; /* string to json tokenizer */ + struct jservice *services; /* first service */ + struct jsignal *signals; /* first signal */ + char *path; /* dbus path */ + char *name; /* dbus name */ }; /*********************** STATIC COMMON METHODS *****************/ -static inline void free_jreq(struct jreq *jreq) +static int mkerrno(int rc) { - dbus_message_unref(jreq->request); - dbus_connection_unref(jreq->connection); - free(jreq); + if (rc >= 0) + return rc; + errno = -rc; + return -1; } -static inline int reply_out_of_memory(struct jreq *jreq) -{ - static const char out_of_memory[] = "out of memory"; - jbus_reply_error_s(jreq, out_of_memory); +/* + * Replies the error "out of memory". + * This function is intended to be used in services when an + * allocation fails. Thus, it set errno to ENOMEM and + * returns -1. + */ +static inline int reply_out_of_memory(struct sd_bus_message *smsg) +{ + jbus_reply_error_s(smsg, out_of_memory_string); errno = ENOMEM; return -1; } -static inline int reply_invalid_request(struct jreq *jreq) -{ - static const char invalid_request[] = "invalid request"; - jbus_reply_error_s(jreq, invalid_request); - return DBUS_HANDLER_RESULT_HANDLED; +/* + * Parses the json-string 'msg' to create a json object stored + * in 'obj'. It uses the tokener of 'jbus'. This is a small + * improvement to avoid recreation of tokeners. + * + * Returns 1 in case of success and put the result in *'obj'. + * Returns 0 in case of error and put NULL in *'obj'. + */ +static int jparse(struct jbus *jbus, const char *msg, struct json_object **obj) +{ + json_tokener_reset(jbus->tokener); + *obj = json_tokener_parse_ex(jbus->tokener, msg, -1); + if (json_tokener_get_error(jbus->tokener) == json_tokener_success) + return 1; + json_object_put(*obj); + *obj = NULL; + return 0; } -static int matchitf(struct jbus *jbus, DBusMessage *message) +static int on_service_call(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error) { - const char *itf = dbus_message_get_interface(message); - return itf != NULL && !strcmp(itf, jbus->name); + struct jservice *service; + const char *member, *content; + struct json_object *obj; + + /* check the type */ + if (!sd_bus_message_has_signature(smsg, "s") + || sd_bus_message_read_basic(smsg, 's', &content) < 0) { + sd_bus_error_set_const(error, "bad signature", ""); + return 1; + } + + /* dispatch */ + member = sd_bus_message_get_member(smsg); + service = jbus->services; + while (service != NULL) { + if (!strcmp(service->method, member)) { + sd_bus_message_ref(smsg); + if (service->oncall_s) + service->oncall_s(smsg, content, service->data); + else if (service->oncall_j) { + if (!jparse(jbus, content, &obj)) + obj = json_object_new_string(content); + service->oncall_j(smsg, obj, service->data); + json_object_put(obj); + } + return 1; + } + service = service->next; + } + return 0; } +/* + * Adds to 'jbus' a service of name 'method'. The service is + * performed by one of the callback 'oncall_s' (for string) or + * 'oncall_j' (for json) that will receive the request and the + * closure parameter 'data'. + * + * returns 0 in case of success or -1 in case of error (ENOMEM). + */ static int add_service( struct jbus *jbus, const char *method, - void (*oncall_s)(struct jreq*, const char*), - void (*oncall_j)(struct jreq*, struct json_object*) -) + void (*oncall_s) (struct sd_bus_message *, const char *, void *), + void (*oncall_j) (struct sd_bus_message *, struct json_object *, void *), + void *data) { + int rc; struct jservice *srv; + /* connection of the service */ + if (jbus->sservice == NULL) { + rc = sd_bus_add_object(jbus->sdbus, &jbus->sservice, jbus->path, (void*)on_service_call, jbus); + if (rc < 0) { + errno = -rc; + goto error; + } + } + /* allocation */ - srv = malloc(sizeof * srv); + srv = malloc(sizeof *srv); if (srv == NULL) { errno = ENOMEM; goto error; @@ -133,272 +211,244 @@ static int add_service( /* record the service */ srv->oncall_s = oncall_s; srv->oncall_j = oncall_j; + srv->data = data; srv->next = jbus->services; jbus->services = srv; return 0; -error2: + error2: free(srv); -error: + error: return -1; } +static int on_signal_event(struct sd_bus_message *smsg, struct jbus *jbus, sd_bus_error *error) +{ + struct jsignal *signal; + const char *member, *content; + struct json_object *obj; + + /* check the type */ + if (!sd_bus_message_has_signature(smsg, "s") + || sd_bus_message_read_basic(smsg, 's', &content) < 0) + return 0; + + /* dispatch */ + member = sd_bus_message_get_member(smsg); + signal = jbus->signals; + while (signal != NULL) { + if (!strcmp(signal->name, member)) { + if (signal->onsignal_s) + signal->onsignal_s(content, signal->data); + else if (signal->onsignal_j) { + if (!jparse(jbus, content, &obj)) + obj = json_object_new_string(content); + signal->onsignal_j(obj, signal->data); + json_object_put(obj); + } + } + signal = signal->next; + } + return 0; +} + +/* + * Adds to 'jbus' a handler for the signal of 'name' emmited by + * the sender and the interface that 'jbus' is linked to. + * The signal is handled by one of the callback 'onsignal_s' + * (for string) or 'onsignal_j' (for json) that will receive + * parameters associated with the signal and the closure + * parameter 'data'. + * + * returns 0 in case of success or -1 in case of error (ENOMEM). + */ static int add_signal( - struct jbus *jbus, - const char *name, - void (*onsignal_s)(const char*), - void (*onsignal_j)(struct json_object*) -) + struct jbus *jbus, + const char *name, + void (*onsignal_s) (const char *, void *), + void (*onsignal_j) (struct json_object *, void *), + void *data) { - char *rule; + int rc; struct jsignal *sig; - - /* record the signal */ - if (jbus->signals == NULL) { - if (0 >= asprintf(&rule, "type='signal',sender='%s',interface='%s',path='%s'", jbus->name, jbus->name, jbus->path)) - return -1; - dbus_bus_add_match(jbus->connection, rule, NULL); - free(rule); + char *match; + + /* connection of the signal */ + if (jbus->ssignal == NULL) { + rc = asprintf(&match, "type='signal',path='%s',interface='%s'", jbus->path, jbus->name); + if (rc < 0) { + errno = ENOMEM; + goto error; + } + rc = sd_bus_add_match(jbus->sdbus, &jbus->ssignal, match, (void*)on_signal_event, jbus); + free(match); + if (rc < 0) { + errno = -rc; + goto error; + } } /* allocation */ - sig = malloc(sizeof * sig); - if (sig == NULL) + sig = malloc(sizeof *sig); + if (sig == NULL) { + errno = ENOMEM; goto error; + } sig->name = strdup(name); - if (!sig->name) + if (!sig->name) { + errno = ENOMEM; goto error2; + } /* record the signal */ sig->onsignal_s = onsignal_s; sig->onsignal_j = onsignal_j; + sig->data = data; sig->next = jbus->signals; jbus->signals = sig; return 0; -error2: + error2: free(sig); -error: - errno = ENOMEM; + error: return -1; } -static int call( - struct jbus *jbus, - const char *method, - const char *query, - void (*onresp_s)(int status, const char *response, void *data), - void (*onresp_j)(int status, struct json_object *response, void *data), - void *data -) -{ - DBusMessage *msg; - struct jrespw *resp; - - resp = malloc(sizeof * resp); - if (resp == NULL) { - errno = ENOMEM; - goto error; - } - - msg = dbus_message_new_method_call(jbus->name, jbus->path, jbus->name, method); - if (msg == NULL) { - errno = ENOMEM; - goto error2; - } - - if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) { - errno = ENOMEM; - goto error3; +static int on_reply(struct sd_bus_message *smsg, struct jrespw *jrespw, sd_bus_error *error) +{ + struct json_object *obj; + const char *reply; + int iserror; + + /* check the type */ + if (!sd_bus_message_has_signature(smsg, "s") + || sd_bus_message_read_basic(smsg, 's', &reply) < 0) { + sd_bus_error_set_const(error, "bad signature", ""); + goto end; } + iserror = sd_bus_message_is_method_error(smsg, NULL); - if (!dbus_connection_send(jbus->connection, msg, &resp->serial)) { - goto error3; + /* dispatch string? */ + if (jrespw->onresp_s != NULL) { + jrespw->onresp_s(iserror, reply, jrespw->data); + goto end; } - dbus_message_unref(msg); - resp->data = data; - resp->onresp_s = onresp_s; - resp->onresp_j = onresp_j; - resp->next = jbus->waiters; - jbus->waiters = resp; - return 0; - -error3: - dbus_message_unref(msg); -error2: - free(resp); -error: - return -1; -} + /* dispatch json */ + if (!jparse(jrespw->jbus, reply, &obj)) + obj = json_object_new_string(reply); + jrespw->onresp_j(iserror, obj, jrespw->data); + json_object_put(obj); -static void sync_of_replies(int status, const char *value, void *data) -{ - struct respsync *s = data; - s->value = status ? NULL : strdup(value ? value : ""); - s->replied = 1; + end: + free(jrespw); + return 1; } -static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage *message, struct jbus *jbus, int iserror) +/* + * Creates a message for 'method' with one string parameter being 'query' + * and sends it to the destination, object and interface linked to 'jbus'. + * + * Adds to 'jbus' the response handler defined by the callbacks 'onresp_s' + * (for string) and 'onresp_j' (for json) and the closure parameter 'data'. + * + * Returns 0 in case of success or -1 in case of error. + */ +static int call( + struct jbus *jbus, + const char *method, + const char *query, + void (*onresp_s) (int, const char *, void *), + void (*onresp_j) (int, struct json_object *, void *), + void *data) { - int status; - const char *str; - struct jrespw *jrw, **prv; - struct json_object *reply; - dbus_uint32_t serial; - - /* search for the waiter */ - serial = dbus_message_get_reply_serial(message); - prv = &jbus->waiters; - while ((jrw = *prv) != NULL && jrw->serial != serial) - prv = &jrw->next; - if (jrw == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - *prv = jrw->next; - - /* retrieve the string value */ - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { - status = -1; - str = NULL; - reply = NULL; - } + int rc; + struct jrespw *resp; - /* treat it */ - if (jrw->onresp_s) - jrw->onresp_s(iserror ? -1 : status, str, jrw->data); - else { - reply = json_tokener_parse(str); - status = reply ? 0 : -1; - jrw->onresp_j(iserror ? -1 : status, reply, jrw->data); + /* allocates the response structure */ + resp = malloc(sizeof *resp); + if (resp == NULL) { + errno = ENOMEM; + goto error; } - free(jrw); - return DBUS_HANDLER_RESULT_HANDLED; -} + /* fulfill the response structure */ + resp->jbus = jbus; + resp->onresp_s = onresp_s; + resp->onresp_j = onresp_j; + resp->data = data; -static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage *message, struct jbus *jbus) -{ - struct jservice *srv; - struct jreq *jreq; - const char *str; - const char *method; - struct json_object *query; - - /* search for the service */ - if (!matchitf(jbus, message)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - method = dbus_message_get_member(message); - if (method == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - srv = jbus->services; - while(srv != NULL && strcmp(method, srv->method)) - srv = srv->next; - if (srv == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - /* handle the message */ - jreq = malloc(sizeof * jreq); - if (jreq == NULL) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - jreq->request = dbus_message_ref(message); - jreq->connection = dbus_connection_ref(jbus->connection); - - /* retrieve the string value */ - if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) - return reply_invalid_request(jreq); - if (srv->oncall_s) { - /* handling strings only */ - srv->oncall_s(jreq, str); - } - else { - /* handling json only */ - query = json_tokener_parse(str); - if (query == NULL) - return reply_invalid_request(jreq); - srv->oncall_j(jreq, query); + rc = sd_bus_call_method_async(jbus->sdbus, NULL, jbus->name, jbus->path, jbus->name, method, (void*)on_reply, resp, "s", query); + if (rc < 0) { + errno = -rc; + goto error2; } - return DBUS_HANDLER_RESULT_HANDLED; -} -static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage *message, struct jbus *jbus) -{ - struct jsignal *sig; - const char *str; - const char *name; - struct json_object *obj; - - /* search for the service */ - if (!matchitf(jbus, message)) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - name = dbus_message_get_member(message); - if (name == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - sig = jbus->signals; - while(sig != NULL && strcmp(name, sig->name)) - sig = sig->next; - if (sig == NULL) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - /* retrieve the string value */ - if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { - if (sig->onsignal_s) { - /* handling strings only */ - sig->onsignal_s(str); - } - else { - /* handling json only */ - obj = json_tokener_parse(str); - if (obj != NULL) - sig->onsignal_j(obj); - } - } - return DBUS_HANDLER_RESULT_HANDLED; -} + return 0; -static DBusHandlerResult incoming(DBusConnection *connection, DBusMessage *message, void *data) -{ - switch(dbus_message_get_type(message)) { - case DBUS_MESSAGE_TYPE_METHOD_CALL: - return incoming_call(connection, message, (struct jbus*)data); - case DBUS_MESSAGE_TYPE_METHOD_RETURN: - return incoming_resp(connection, message, (struct jbus*)data, 0); - case DBUS_MESSAGE_TYPE_ERROR: - return incoming_resp(connection, message, (struct jbus*)data, 1); - case DBUS_MESSAGE_TYPE_SIGNAL: - return incoming_signal(connection, message, (struct jbus*)data); - } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + error2: + free(resp); + error: + return -1; } -/************************** MAIN FUNCTIONS *****************************************/ +/********************* MAIN FUNCTIONS *****************************************/ -struct jbus *create_jbus(int session, const char *path) +/* + * Creates a 'jbus' bound the 'path' and it derived names and linked + * either to the DBUS SYSTEM when 'session' is nul or to the DBUS SESSION + * if 'session' is not nul. + * + * The parameter 'path' is intended to be the path of a DBUS single object. + * Single means that it exists only one instance of the object on the + * given bus. That path implies 2 derived DBUS names: + * 1. the destination name of the program that handles the object + * 2. the interface name of the object + * These names are derived by removing the heading slash (/) and + * by replacing all occurences of slashes by dots. + * For example, passing path = /a/b/c means that the object /a/b/c is + * handled by the destination a.b.c and replies to the interface a.b.c + * + * Returns the created jbus or NULL in case of error. + */ +struct jbus *create_jbus(struct sd_bus *sdbus, const char *path) { struct jbus *jbus; char *name; - /* create the context and connect */ - jbus = calloc(1, sizeof * jbus); + /* create the jbus object */ + jbus = calloc(1, sizeof *jbus); if (jbus == NULL) { errno = ENOMEM; goto error; } jbus->refcount = 1; + + /* create the tokener */ + jbus->tokener = json_tokener_new_ex(MAX_JSON_DEPTH); + if (jbus->tokener == NULL) { + errno = ENOMEM; + goto error2; + } + + /* records the path */ jbus->path = strdup(path); - jbus->name = NULL; if (jbus->path == NULL) { errno = ENOMEM; goto error2; } - while(*path == '/') path++; + + /* makes the name from the path */ + while (*path == '/') + path++; jbus->name = name = strdup(path); if (name == NULL) { errno = ENOMEM; goto error2; } - while(*name) { + while (*name) { if (*name == '/') *name = '.'; name++; @@ -411,119 +461,124 @@ struct jbus *create_jbus(int session, const char *path) goto error2; } - /* connect */ - jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, NULL); - if (jbus->connection == NULL) { - goto error2; - } - if (!dbus_connection_add_filter(jbus->connection, incoming, jbus, NULL)) { - goto error2; - } + /* connect and init */ + jbus->sdbus = sd_bus_ref(sdbus); return jbus; -error2: + error2: jbus_unref(jbus); -error: + error: return NULL; } +/* + * Adds one reference to 'jbus'. + */ void jbus_addref(struct jbus *jbus) { jbus->refcount++; } +/* + * Removes one reference to 'jbus'. Destroys 'jbus' and it related + * data if the count of references decrease to zero. + */ void jbus_unref(struct jbus *jbus) { struct jservice *srv; + struct jsignal *sig; if (!--jbus->refcount) { - dbus_connection_unref(jbus->connection); - while((srv = jbus->services) != NULL) { + while ((srv = jbus->services) != NULL) { jbus->services = srv->next; free(srv->method); free(srv); } + while ((sig = jbus->signals) != NULL) { + jbus->signals = sig->next; + free(sig->name); + free(sig); + } + if (jbus->sservice != NULL) + sd_bus_slot_unref(jbus->sservice); + if (jbus->ssignal != NULL) + sd_bus_slot_unref(jbus->ssignal); + if (jbus->tokener != NULL) + json_tokener_free(jbus->tokener); + sd_bus_unref(jbus->sdbus); free(jbus->name); free(jbus->path); free(jbus); } } -int jbus_reply_error_s(struct jreq *jreq, const char *error) +/* + * Replies an error of string 'error' to the request handled by 'smsg'. + * Also destroys the request 'smsg' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_reply_error_s(struct sd_bus_message *smsg, const char *error) { - int rc = -1; - DBusMessage *message; - - message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, error); - if (message == NULL) - errno = ENOMEM; - else { - if (dbus_connection_send(jreq->connection, message, NULL)) - rc = 0; - dbus_message_unref(message); - } - free_jreq(jreq); - return rc; + int rc = sd_bus_reply_method_errorf(smsg, SD_BUS_ERROR_FAILED, "%s", error); + sd_bus_message_unref(smsg); + return mkerrno(rc); } -int jbus_reply_error_j(struct jreq *jreq, struct json_object *reply) +/* + * Replies an error of json 'reply' to the request handled by 'smsg'. + * Also destroys the request 'smsg' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_reply_error_j(struct sd_bus_message *smsg, struct json_object *reply) { const char *str = json_object_to_json_string(reply); - return str ? jbus_reply_error_s(jreq, str) : reply_out_of_memory(jreq); + return str ? jbus_reply_error_s(smsg, str) : reply_out_of_memory(smsg); } -int jbus_reply_s(struct jreq *jreq, const char *reply) +/* + * Replies normally the string 'reply' to the request handled by 'smsg'. + * Also destroys the request 'smsg' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_reply_s(struct sd_bus_message *smsg, const char *reply) { - int rc = -1; - DBusMessage *message; - - message = dbus_message_new_method_return(jreq->request); - if (message == NULL) - return reply_out_of_memory(jreq); - - if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) { - dbus_message_unref(message); - return reply_out_of_memory(jreq); - } - - if (dbus_connection_send(jreq->connection, message, NULL)) - rc = 0; - dbus_message_unref(message); - free_jreq(jreq); - return rc; + int rc = sd_bus_reply_method_return(smsg, "s", reply); + sd_bus_message_unref(smsg); + return mkerrno(rc); } -int jbus_reply_j(struct jreq *jreq, struct json_object *reply) +/* + * Replies normally the json 'reply' to the request handled by 'smsg'. + * Also destroys the request 'smsg' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_reply_j(struct sd_bus_message *smsg, struct json_object *reply) { const char *str = json_object_to_json_string(reply); - return str ? jbus_reply_s(jreq, str) : reply_out_of_memory(jreq); + return str ? jbus_reply_s(smsg, str) : reply_out_of_memory(smsg); } +/* + * Sends from 'jbus' the signal of 'name' handling the string 'content'. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content) { - int rc = -1; - DBusMessage *message; - - message = dbus_message_new_signal(jbus->path, jbus->name, name); - if (message == NULL) - goto error; - - if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID)) { - dbus_message_unref(message); - goto error; - } - - if (dbus_connection_send(jbus->connection, message, NULL)) - rc = 0; - dbus_message_unref(message); - return rc; - -error: - errno = ENOMEM; - return -1; + return mkerrno(sd_bus_emit_signal(jbus->sdbus, jbus->path, jbus->name, name, "s", content)); } -int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object *content) +/* + * Sends from 'jbus' the signal of 'name' handling the json 'content'. + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_send_signal_j(struct jbus *jbus, const char *name, + struct json_object *content) { const char *str = json_object_to_json_string(content); if (str == NULL) { @@ -533,50 +588,121 @@ int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object * return jbus_send_signal_s(jbus, name, str); } -int jbus_add_service_s(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, const char *)) +/* + * Adds to 'jbus' a service handling calls to the 'method' using + * the "string" callback 'oncall' and the closure value 'data'. + * + * The callback 'oncall' is invoked for handling incoming method + * calls. It receives 3 parameters: + * 1. struct sd_bus_message *: a handler to data to be used for replying + * 2. const char *: the received string + * 3. void *: the closure 'data' set by this function + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_add_service_s( + struct jbus *jbus, + const char *method, + void (*oncall) (struct sd_bus_message *, const char *, void *), + void *data) { - return add_service(jbus, method, oncall, NULL); + return add_service(jbus, method, oncall, NULL, data); } -int jbus_add_service_j(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *)) +/* + * Adds to 'jbus' a service handling calls to the 'method' using + * the "json" callback 'oncall' and the closure value 'data'. + * + * The callback 'oncall' is invoked for handling incoming method + * calls. It receives 3 parameters: + * 1. struct sd_bus_message *: a handler to data to be used for replying + * 2. struct json_object *: the received json + * 3. void *: the closure 'data' set by this function + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_add_service_j( + struct jbus *jbus, + const char *method, + void (*oncall) (struct sd_bus_message *, struct json_object *, void *), + void *data) { - return add_service(jbus, method, NULL, oncall); + return add_service(jbus, method, NULL, oncall, data); } +/* + * Start to serve: activate services declared for 'jbus'. + * This function, in fact, declares 'jbus' as the receiver + * for calls to the destination derived from the path set at + * 'jbus' creation. + * It also allows 'jbus' to emit signals of that origin. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_start_serving(struct jbus *jbus) { - int status = dbus_bus_request_name(jbus->connection, jbus->name, DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL); - switch (status) { - case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: - case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - return 0; - case DBUS_REQUEST_NAME_REPLY_EXISTS: - case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: - default: - errno = EADDRINUSE; - return -1; - } -} - -int jbus_read_write_dispatch(struct jbus *jbus, int toms) -{ - if (dbus_connection_read_write_dispatch(jbus->connection, toms)); - return 0; - errno = EPIPE; - return -1; + return mkerrno(sd_bus_request_name(jbus->sdbus, jbus->name, 0)); } -int jbus_call_ss(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, const char*, void*), void *data) +/* + * Asynchronous call to 'method' of 'jbus' passing the string 'query'. + * On response, the function 'onresp' is called with the returned string + * value and the closure 'data'. + * The function 'onresp' is invoked with 3 parameters: + * 1. int: 0 if no error or -1 if error. + * 2. const char *: the returned string (might be NULL if error) + * 3. void *: the closure 'data' + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_call_ss( + struct jbus *jbus, + const char *method, + const char *query, + void (*onresp) (int, const char *, void *), + void *data) { return call(jbus, method, query, onresp, NULL, data); } -int jbus_call_sj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object*, void*), void *data) +/* + * Asynchronous call to 'method' of 'jbus' passing the string 'query'. + * On response, the function 'onresp' is called with the returned json + * value and the closure 'data'. + * The function 'onresp' is invoked with 3 parameters: + * 1. int: 0 if no error or -1 if error. + * 2. const char *: the returned json (might be NULL if error) + * 3. void *: the closure 'data' + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_call_sj( + struct jbus *jbus, + const char *method, + const char *query, + void (*onresp) (int, struct json_object *, void *), + void *data) { return call(jbus, method, query, NULL, onresp, data); } -int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, const char*, void*), void *data) +/* + * Asynchronous call to 'method' of 'jbus' passing the json 'query'. + * On response, the function 'onresp' is called with the returned string + * value and the closure 'data'. + * The function 'onresp' is invoked with 3 parameters: + * 1. int: 0 if no error or -1 if error. + * 2. const char *: the returned string (might be NULL if error) + * 3. void *: the closure 'data' + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_call_js( + struct jbus *jbus, + const char *method, + struct json_object *query, + void (*onresp) (int, const char *, void *), + void *data) { const char *str = json_object_to_json_string(query); if (str == NULL) { @@ -586,7 +712,23 @@ int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *quer return call(jbus, method, str, onresp, NULL, data); } -int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object*, void*), void *data) +/* + * Asynchronous call to 'method' of 'jbus' passing the json 'query'. + * On response, the function 'onresp' is called with the returned json + * value and the closure 'data'. + * The function 'onresp' is invoked with 3 parameters: + * 1. int: 0 if no error or -1 if error. + * 2. const char *: the returned json (might be NULL if error) + * 3. void *: the closure 'data' + * + * Returns 0 in case of success or -1 in case of error. + */ +int jbus_call_jj( + struct jbus *jbus, + const char *method, + struct json_object *query, + void (*onresp) (int, struct json_object *, void *), + void *data) { const char *str = json_object_to_json_string(query); if (str == NULL) { @@ -596,23 +738,76 @@ int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *quer return call(jbus, method, str, NULL, onresp, data); } -char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query) +/* + * Synchronous call to 'method' of 'jbus' passing the string 'query'. + * The returned string response is returned. + * + * Returns the string response or NULL in case of error. + */ +char *jbus_call_ss_sync( + struct jbus *jbus, + const char *method, + const char *query) { - struct respsync synchro; - synchro.value = NULL; - synchro.replied = jbus_call_ss(jbus, method, query, sync_of_replies, &synchro); - while (!synchro.replied && !jbus_read_write_dispatch(jbus, -1)); - return synchro.value; + sd_bus_message *smsg = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + char *result = NULL; + const char *reply; + + /* makes the call */ + if (mkerrno(sd_bus_call_method(jbus->sdbus, jbus->name, jbus->path, jbus->name, method, &error, &smsg, "s", query)) < 0) + goto error; + + /* check if error */ + if (sd_bus_message_is_method_error(smsg, NULL)) + goto error; + + /* check the returned type */ + if (!sd_bus_message_has_signature(smsg, "s") + || sd_bus_message_read_basic(smsg, 's', &reply) < 0) + goto error; + + /* get the result */ + result = strdup(reply); + +error: + sd_bus_message_unref(smsg); + sd_bus_error_free(&error); + return result; } -struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query) +/* + * Synchronous call to 'method' of 'jbus' passing the string 'query'. + * The returned json response is returned. + * + * Returns the json response or NULL in case of error. + */ +struct json_object *jbus_call_sj_sync( + struct jbus *jbus, + const char *method, + const char *query) { - const char *str = jbus_call_ss_sync(jbus, method, query); - return str ? json_tokener_parse(str) : NULL; + struct json_object *obj; + char *str = jbus_call_ss_sync(jbus, method, query); + if (str == NULL) + obj = NULL; + else { + jparse(jbus, str, &obj); + free(str); + } + return obj; } - -char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query) +/* + * Synchronous call to 'method' of 'jbus' passing the json 'query'. + * The returned string response is returned. + * + * Returns the string response or NULL in case of error. + */ +char *jbus_call_js_sync( + struct jbus *jbus, + const char *method, + struct json_object *query) { const char *str = json_object_to_json_string(query); if (str == NULL) { @@ -622,7 +817,16 @@ char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_objec return jbus_call_ss_sync(jbus, method, str); } -struct json_object *jbus_call_jj_sync(struct jbus *jbus, const char *method, struct json_object *query) +/* + * Synchronous call to 'method' of 'jbus' passing the json 'query'. + * The returned json response is returned. + * + * Returns the json response or NULL in case of error. + */ +struct json_object *jbus_call_jj_sync( + struct jbus *jbus, + const char *method, + struct json_object *query) { const char *str = json_object_to_json_string(query); if (str == NULL) { @@ -632,83 +836,140 @@ struct json_object *jbus_call_jj_sync(struct jbus *jbus, const char *method, str return jbus_call_sj_sync(jbus, method, str); } -int jbus_on_signal_s(struct jbus *jbus, const char *name, void (*onsig)(const char *)) +/* + * Records for 'jbus' the string signal handler 'onsig' with closure 'data' + * for the signal of 'name'. + * The callback handler is called with 2 arguments: + * 1. char *: the string parameter associated to the signal + * 2. void *: the closure data. + * + * Returns 0 in case of success or -1 otherwise. + */ +int jbus_on_signal_s( + struct jbus *jbus, + const char *name, + void (*onsig) (const char *, void *), + void *data) { - return add_signal(jbus, name, onsig, NULL); + return add_signal(jbus, name, onsig, NULL, data); } -int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct json_object *)) +/* + * Records for 'jbus' the json signal handler 'onsig' with closure 'data' + * for the signal of 'name'. + * The callback handler is called with 2 arguments: + * 1. struct json_object *: the json parameter associated to the signal + * 2. void *: the closure data. + * + * Returns 0 in case of success or -1 otherwise. + */ +int jbus_on_signal_j( + struct jbus *jbus, + const char *name, + void (*onsig) (struct json_object *, void *), + void *data) { - return add_signal(jbus, name, NULL, onsig); + return add_signal(jbus, name, NULL, onsig, data); } -/************************** FEW LITTLE TESTS *****************************************/ +/****************** FEW LITTLE TESTS *****************************************/ -#ifdef SERVER +#if defined(SERVER)||defined(CLIENT) #include #include -struct jbus *jbus; -void ping(struct jreq *jreq, struct json_object *request) + +static struct sd_bus *msbus() +{ + static struct sd_bus *r = NULL; + if (r == NULL) { + static sd_event *e; + sd_event_default(&e); + sd_bus_open_user(&r); + sd_bus_attach_event(r, e, 0); + } + return r; +} + +static sd_event *events() { -printf("ping(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(request)); - jbus_reply_j(jreq, request); - json_object_put(request); + static sd_event *ev = NULL; + if (ev == NULL) + ev = sd_bus_get_event(msbus()); + return ev; } -void incr(struct jreq *jreq, struct json_object *request) + +static int mwait(int timeout, void *closure) +{ + sd_event_run(events(), -1); + return 0; +} + +static struct jbus *jbus; + +#ifdef SERVER +void ping(struct sd_bus_message *smsg, struct json_object *request, void *unused) +{ + printf("ping(%s) -> %s\n", json_object_to_json_string(request), + json_object_to_json_string(request)); + jbus_reply_j(smsg, request); + json_object_put(request); +} + +void incr(struct sd_bus_message *smsg, struct json_object *request, void *unused) { static int counter = 0; struct json_object *res = json_object_new_int(++counter); -printf("incr(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(res)); - jbus_reply_j(jreq, res); -jbus_send_signal_j(jbus, "incremented", res); + printf("incr(%s) -> %s\n", json_object_to_json_string(request), + json_object_to_json_string(res)); + jbus_reply_j(smsg, res); + jbus_send_signal_j(jbus, "incremented", res); json_object_put(res); json_object_put(request); } + int main() { int s1, s2, s3; - jbus = create_jbus(1, "/bzh/iot/jdbus"); - s1 = jbus_add_service_j(jbus, "ping", ping); - s2 = jbus_add_service_j(jbus, "incr", incr); + jbus = create_jbus(msbus(), "/bzh/iot/jdbus"); + s1 = jbus_add_service_j(jbus, "ping", ping, NULL); + s2 = jbus_add_service_j(jbus, "incr", incr, NULL); s3 = jbus_start_serving(jbus); printf("started %d %d %d\n", s1, s2, s3); - while (!jbus_read_write_dispatch (jbus, -1)) - ; + while (!mwait(-1,jbus)) ; + return 0; } #endif + #ifdef CLIENT -#include -#include -struct jbus *jbus; void onresp(int status, struct json_object *response, void *data) { - printf("resp: %d, %s, %s\n",status,(char*)data,json_object_to_json_string(response)); + printf("resp: %d, %s, %s\n", status, (char *)data, + json_object_to_json_string(response)); json_object_put(response); } -void signaled(const char *data) + +void signaled(const char *content, void *data) { - printf("signaled with {%s}\n", data); + printf("signaled with {%s}/%s\n", content, (char*)data); } + int main() { - int i = 10; - jbus = create_jbus(1, "/bzh/iot/jdbus"); - jbus_on_signal_s(jbus, "incremented", signaled); - while(i--) { - jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}", onresp, "ping"); - jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp, "incr"); - jbus_read_write_dispatch (jbus, 1); + int i = 1; + jbus = create_jbus(msbus(), "/bzh/iot/jdbus"); + jbus_on_signal_s(jbus, "incremented", signaled, "closure-signal"); + while (i--) { + jbus_call_sj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}", + onresp, "ping"); + jbus_call_sj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp, + "incr"); + mwait(-1,jbus); } - printf("[[[%s]]]\n", jbus_call_ss_sync(jbus, "ping", "\"formidable!\"")); - while (!jbus_read_write_dispatch (jbus, -1)) - ; + printf("[[[%s]]]\n", + jbus_call_ss_sync(jbus, "ping", "\"formidable!\"")); + while (!mwait(-1,jbus)) ; + return 0; } #endif - - - - - - - +#endif