diff options
author | José Bollo <jose.bollo@iot.bzh> | 2015-12-15 11:19:49 +0100 |
---|---|---|
committer | José Bollo <jose.bollo@iot.bzh> | 2015-12-15 11:19:49 +0100 |
commit | f145b390adea074c40470b677d8721877520150a (patch) | |
tree | 2e7687dbbb2f0bbcd7f21432b0358ea2184c1097 | |
parent | 509d0ed17a1f59605b7d36f60561d0cafcadc9de (diff) |
adding jbus
Change-Id: Ia712f77d32e3a7b0b15fcf2066b562d8d4c02169
-rw-r--r-- | src/utils-jbus.c | 402 | ||||
-rw-r--r-- | src/utils-jbus.h | 31 |
2 files changed, 433 insertions, 0 deletions
diff --git a/src/utils-jbus.c b/src/utils-jbus.c new file mode 100644 index 0000000..8c48113 --- /dev/null +++ b/src/utils-jbus.c @@ -0,0 +1,402 @@ +/* + Copyright 2015 IoT.bzh + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> + +#include <json.h> +#include <dbus/dbus.h> + +#include "utils-jbus.h" + +struct jreq; +struct jservice; +struct jbus; + +struct jreq { + DBusConnection *connection; + DBusMessage *reply; +}; + +struct jservice { + struct jservice *next; + char *method; + void (*oncall)(struct jreq *, struct json_object *); +}; + +struct jrespw { + struct jrespw *next; + dbus_uint32_t serial; + void *data; + void (*onresp)(int status, struct json_object *response, void *data); +}; + +struct jbus { + int refcount; + struct jservice *services; + DBusConnection *connection; + struct jrespw *waiters; + char *path; + char *name; +}; + +static const char reply_out_of_memory[] = "{\"status\":\"out of memory\"}"; +static const char reply_invalid[] = "{\"status\":\"invalid request\"}"; +static const char interface_jbus[] = "org.jbus"; + +static int send_reply(struct jreq *jreq, const char *reply) +{ + int rc = -1; + if (dbus_message_append_args(jreq->reply, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) { + if (dbus_connection_send(jreq->connection, jreq->reply, NULL)) + rc = 0; + } + dbus_message_unref(jreq->reply); + dbus_connection_unref(jreq->connection); + free(jreq); + return rc; +} + +static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage *message, struct jbus *jbus) +{ + 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_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 json value */ + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { + status = -1; + reply = NULL; + } else { + reply = json_tokener_parse(str); + status = reply ? 0 : -1; + } + + /* treat it */ + jrw->onresp(status, reply, jrw->data); + free(jrw); + return DBUS_HANDLER_RESULT_HANDLED; +} + +static int matchitf(DBusMessage *message) +{ + const char *itf = dbus_message_get_interface(message); + return itf != NULL && !strcmp(itf, interface_jbus); +} + +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(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->reply = dbus_message_new_method_return(message); + if (jreq->reply == NULL) { + free(jreq); + return DBUS_HANDLER_RESULT_NEED_MEMORY; + } + jreq->connection = dbus_connection_ref(jbus->connection); + + /* retrieve the json value */ + if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { + send_reply(jreq, reply_invalid); + return DBUS_HANDLER_RESULT_HANDLED; + } + query = json_tokener_parse(str); + if (query == NULL) { + send_reply(jreq, reply_invalid); + return DBUS_HANDLER_RESULT_HANDLED; + } + + /* treat it */ + srv->oncall(jreq, query); + return DBUS_HANDLER_RESULT_HANDLED; +} + +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); + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +struct jbus *create_jbus(int session, const char *path) +{ + struct jbus *jbus; + char *name; + + /* create the context and connect */ + jbus = calloc(1, sizeof * jbus); + if (jbus == NULL) { + errno = ENOMEM; + goto error; + } + jbus->refcount = 1; + jbus->path = strdup(path); + jbus->name = NULL; + if (jbus->path == NULL) { + errno = ENOMEM; + goto error2; + } + while(*path == '/') path++; + jbus->name = name = strdup(path); + if (name == NULL) { + errno = ENOMEM; + goto error2; + } + while(*name) { + if (*name == '/') + *name = '.'; + name++; + } + name--; + while (name >= jbus->name && *name == '.') + *name-- = 0; + if (!*jbus->name) { + errno = EINVAL; + 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; + } + + return jbus; + +error2: + jbus_unref(jbus); +error: + return NULL; +} + +void jbus_addref(struct jbus *jbus) +{ + jbus->refcount++; +} + +void jbus_unref(struct jbus *jbus) +{ + struct jservice *srv; + if (!--jbus->refcount) { + dbus_connection_unref(jbus->connection); + while((srv = jbus->services) != NULL) { + jbus->services = srv->next; + free(srv->method); + free(srv); + } + free(jbus->name); + free(jbus->path); + free(jbus); + } +} + +int jbus_reply(struct jreq *jreq, struct json_object *reply) +{ + const char *str = json_object_to_json_string(reply); + return send_reply(jreq, str ? str : reply_out_of_memory); +} + +int jbus_add_service(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *jreq, struct json_object *request)) +{ + struct jservice *srv; + + /* allocation */ + srv = malloc(sizeof * srv); + if (srv == NULL) { + errno = ENOMEM; + goto error; + } + srv->method = strdup(method); + if (!srv->method) { + errno = ENOMEM; + goto error2; + } + + /* record the service */ + srv->oncall = oncall; + srv->next = jbus->services; + jbus->services = srv; + + return 0; + +error3: + free(srv->method); +error2: + free(srv); +error: + return -1; +} + +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: + errno = EADDRINUSE; + return -1; + } +} + +int jbus_callj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int status, struct json_object *response, void *data), void *data) +{ + int rc; + 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, interface_jbus, 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; + } + + if (!dbus_connection_send(jbus->connection, msg, &resp->serial)) { + goto error3; + } + + dbus_message_unref(msg); + resp->data = data; + resp->onresp = onresp; + resp->next = jbus->waiters; + jbus->waiters = resp; + return 0; + +error3: + dbus_message_unref(msg); +error2: + free(resp); +error: + return -1; +} + + +int jbus_call(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int status, struct json_object *response, void *data), void *data) +{ + const char *str = json_object_to_json_string(query); + if (str == NULL) { + errno = ENOMEM; + return -1; + } + return jbus_callj(jbus, method, str, onresp, data); +} + +#ifdef SERVER +#include <stdio.h> +#include <unistd.h> +void ping(struct jreq *jreq, struct json_object *request) +{ +printf("ping(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(request)); + jbus_reply(jreq, request); + json_object_put(request); +} +void incr(struct jreq *jreq, struct json_object *request) +{ + 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(jreq, res); + json_object_put(res); + json_object_put(request); +} +int main() +{ + struct jbus *jbus = create_jbus(1, "/bzh/iot/jdbus"); + int s1 = jbus_add_service(jbus, "ping", ping); + int s2 = jbus_add_service(jbus, "incr", incr); + int s3 = jbus_start_serving(jbus); + printf("started %d %d %d\n", s1, s2, s3); + while (dbus_connection_read_write_dispatch (jbus->connection, -1)) + ; +} +#endif +#ifdef CLIENT +#include <stdio.h> +#include <unistd.h> +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)); + json_object_put(response); +} +int main() +{ + struct jbus *jbus = create_jbus(1, "/bzh/iot/jdbus"); + int i = 10; + while(i--) { + jbus_callj(jbus, "ping", "{\"toto\":[1,2,3,4,true,\"toto\"]}", onresp, "ping"); + jbus_callj(jbus, "incr", "{\"doit\":\"for-me\"}", onresp, "incr"); + dbus_connection_read_write_dispatch (jbus->connection, 1); + } + while (dbus_connection_read_write_dispatch (jbus->connection, -1)) + ; +} +#endif + diff --git a/src/utils-jbus.h b/src/utils-jbus.h new file mode 100644 index 0000000..f1ec78f --- /dev/null +++ b/src/utils-jbus.h @@ -0,0 +1,31 @@ +/* + Copyright 2015 IoT.bzh + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + +struct jreq; +struct jbus; + + +struct jbus *create_jbus(int session, const char *path); +void jbus_addref(struct jbus *jbus); +void jbus_unref(struct jbus *jbus); + +int jbus_reply(struct jreq *jreq, struct json_object *reply); +int jbus_add_service(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *)); + +int jbus_callj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object *, void *), void *data); +int jbus_call(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object *response, void *), void *data); + |