diff options
-rw-r--r-- | include/afb-plugin.h | 12 | ||||
-rw-r--r-- | include/afb-poll-itf.h | 31 | ||||
-rw-r--r-- | include/afb-req-itf.h | 2 | ||||
-rw-r--r-- | plugins/afm-main-plugin/afm-main-plugin.c | 10 | ||||
-rw-r--r-- | plugins/afm-main-plugin/utils-jbus.c | 817 | ||||
-rw-r--r-- | plugins/afm-main-plugin/utils-jbus.h | 151 | ||||
-rw-r--r-- | plugins/samples/ClientCtx.c | 6 | ||||
-rw-r--r-- | src/CMakeLists.txt | 6 | ||||
-rw-r--r-- | src/afb-apis.c | 33 | ||||
-rw-r--r-- | src/afb-hreq.c | 61 | ||||
-rw-r--r-- | src/afb-hreq.h | 1 | ||||
-rw-r--r-- | src/afb-hsrv.c (renamed from src/http-svc.c) | 169 | ||||
-rw-r--r-- | src/afb-hsrv.h | 20 | ||||
-rw-r--r-- | src/afb-websock.c | 422 | ||||
-rw-r--r-- | src/afb-websock.h | 2 | ||||
-rw-r--r-- | src/helper-api.c | 312 | ||||
-rw-r--r-- | src/local-def.h | 27 | ||||
-rw-r--r-- | src/main.c | 126 | ||||
-rw-r--r-- | src/proto-def.h | 40 | ||||
-rw-r--r-- | src/session.c | 9 | ||||
-rw-r--r-- | src/session.h | 3 | ||||
-rw-r--r-- | src/utils-upoll.c | 171 | ||||
-rw-r--r-- | src/utils-upoll.h | 32 | ||||
-rw-r--r-- | src/verbose.c | 29 | ||||
-rw-r--r-- | src/verbose.h | 39 |
25 files changed, 1669 insertions, 862 deletions
diff --git a/include/afb-plugin.h b/include/afb-plugin.h index f2f4d6ec..4064f9e9 100644 --- a/include/afb-plugin.h +++ b/include/afb-plugin.h @@ -61,19 +61,13 @@ enum AFB_Mode { AFB_MODE_GLOBAL }; -/* -typedef enum AFB_pluginE AFB_pluginE; -typedef enum AFB_sessionE AFB_sessionE; -typedef void (*AFB_apiCB)(struct afb_req); -typedef void (*AFB_freeCtxCB)(void*); -typedef struct AFB_restapi AFB_restapi; -typedef struct AFB_plugin AFB_plugin; -*/ +struct afb_poll; struct AFB_interface { - int verbose; + int verbosity; enum AFB_Mode mode; + struct afb_poll (*poll_open)(int fd, void *closure); }; extern const struct AFB_plugin *pluginRegister (const struct AFB_interface *interface); diff --git a/include/afb-poll-itf.h b/include/afb-poll-itf.h new file mode 100644 index 00000000..2c8889b6 --- /dev/null +++ b/include/afb-poll-itf.h @@ -0,0 +1,31 @@ +/* + * Copyright 2016 IoT.bzh + * Author: José Bollo <jose.bollo@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 afb_poll_itf +{ + int (*on_readable)(void *, void (*cb)(void *)); + int (*on_writable)(void *, void (*cb)(void *)); + int (*on_hangup)(void *, void (*cb)(void *)); + void (*close)(void *data); +}; + +struct afb_poll +{ + const struct afb_poll_itf *itf; + void *data; +}; + diff --git a/include/afb-req-itf.h b/include/afb-req-itf.h index a9b768db..8d869a6f 100644 --- a/include/afb-req-itf.h +++ b/include/afb-req-itf.h @@ -37,7 +37,7 @@ struct afb_req_itf { struct afb_req { const struct afb_req_itf *itf; void *data; - void *context; + void **context; }; static inline struct afb_arg afb_req_get(struct afb_req req, const char *name) diff --git a/plugins/afm-main-plugin/afm-main-plugin.c b/plugins/afm-main-plugin/afm-main-plugin.c index fbf5b590..d86e9263 100644 --- a/plugins/afm-main-plugin/afm-main-plugin.c +++ b/plugins/afm-main-plugin/afm-main-plugin.c @@ -73,7 +73,7 @@ static struct json_object *embed(const char *tag, struct json_object *obj) static void embed_call_void(struct afb_req request, const char *method) { struct json_object *obj = jbus_call_sj_sync(jbus, method, "true"); - if (interface->verbose) + if (interface->verbosity) fprintf(stderr, "(afm-main-plugin) %s(true) -> %s\n", method, obj ? json_object_to_json_string(obj) : "NULL"); if (obj == NULL) { afb_req_fail(request, "failed", "framework daemon failure"); @@ -101,7 +101,7 @@ static void call_appid(struct afb_req request, const char *method) return; } obj = jbus_call_sj_sync(jbus, method, sid); - if (interface->verbose) + if (interface->verbosity) fprintf(stderr, "(afm-main-plugin) %s(%s) -> %s\n", method, sid, obj ? json_object_to_json_string(obj) : "NULL"); free(sid); if (obj == NULL) { @@ -120,7 +120,7 @@ static void call_runid(struct afb_req request, const char *method) return; } obj = jbus_call_sj_sync(jbus, method, id); - if (interface->verbose) + if (interface->verbosity) fprintf(stderr, "(afm-main-plugin) %s(%s) -> %s\n", method, id, obj ? json_object_to_json_string(obj) : "NULL"); if (obj == NULL) { @@ -171,7 +171,7 @@ static void start(struct afb_req request) /* calls the service */ obj = jbus_call_sj_sync(jbus, _start_, query); - if (interface->verbose) + if (interface->verbosity) fprintf(stderr, "(afm-main-plugin) start(%s) -> %s\n", query, obj ? json_object_to_json_string(obj) : "NULL"); free(query); @@ -234,7 +234,7 @@ static void install(struct afb_req request) } obj = jbus_call_sj_sync(jbus, _install_, query); - if (interface->verbose) + if (interface->verbosity) fprintf(stderr, "(afm-main-plugin) install(%s) -> %s\n", query, obj ? json_object_to_json_string(obj) : "NULL"); free(query); diff --git a/plugins/afm-main-plugin/utils-jbus.c b/plugins/afm-main-plugin/utils-jbus.c index 9d6c1d5d..3bea2c23 100644 --- a/plugins/afm-main-plugin/utils-jbus.c +++ b/plugins/afm-main-plugin/utils-jbus.c @@ -29,68 +29,99 @@ #include "utils-jbus.h" -#define MAX_JSON_DEPTH 5 +/* + * max depth of json messages + */ +#define MAX_JSON_DEPTH 10 -struct jreq; -struct jservice; -struct jbus; +/* + * errors messages generated by jbus + */ +#if defined(NO_JSON_ERROR_STRING) +static const char invalid_request_string[] = "invalid request"; +static const char out_of_memory_string[] = "out of memory"; +#else +static const char invalid_request_string[] = "\"invalid request\""; +static const char out_of_memory_string[] = "\"out of memory\""; +#endif -/* structure for handled requests */ +/* + * structure for handled requests + */ struct jreq { - DBusConnection *connection; - DBusMessage *request; + DBusConnection *connection; /* connection of the request */ + DBusMessage *request; /* message of the request */ }; -/* structure for recorded services */ +/* + * structure for services + */ struct jservice { - struct jservice *next; - char *method; - void (*oncall_s)(struct jreq *, const char *, void *); - void (*oncall_j)(struct jreq *, struct json_object *, void *); - void *data; + struct jservice *next; /* link to the next service */ + char *method; /* method name for the service */ + void (*oncall_s) (struct jreq *, const char *, void *); + /* string callback */ + void (*oncall_j) (struct jreq *, 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 *); - void (*onsignal_j)(struct json_object *, void *); - void *data; + 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 (resp-onse w-aiter) + */ 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 *); + struct jrespw *next; /* next asynchronous */ + dbus_uint32_t serial; /* serial dbus number */ + void *data; /* closure data for the callbacks */ + void (*onresp_s) (int, const char *, void *); + /* string callback */ + void (*onresp_j) (int, struct json_object *, void *); + /* json callback */ }; -/* structure for synchronous requests */ +/* + * structure for synchronous requests + */ struct respsync { - int replied; - char *value; + int replied; /* boolean flag indicating reply */ + char *value; /* copy of the returned value */ }; -/* 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 json_tokener *tokener; - struct jservice *services; - DBusConnection *connection; - struct jsignal *signals; - struct jrespw *waiters; - char *path; - char *name; - int watchnr; - int watchfd; - short watchflags; + int refcount; /* referenced how many time */ + DBusConnection *connection; /* connection to DBU */ + struct json_tokener *tokener; /* string to json tokenizer */ + struct jservice *services; /* first service */ + struct jsignal *signals; /* first signal */ + struct jrespw *waiters; /* first response waiter */ + char *path; /* dbus path */ + char *name; /* dbus name */ + int watchnr; /* counter of watching need */ + int watchfd; /* file to watch */ + short watchflags; /* watched flags */ }; /*********************** STATIC COMMON METHODS *****************/ +/* + * Frees the ressources attached to a request + */ static inline void free_jreq(struct jreq *jreq) { dbus_message_unref(jreq->request); @@ -98,39 +129,50 @@ static inline void free_jreq(struct jreq *jreq) free(jreq); } +/* + * 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 jreq *jreq) { - static const char out_of_memory[] = "out of memory"; - jbus_reply_error_s(jreq, out_of_memory); + jbus_reply_error_s(jreq, 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; -} - -static int matchitf(struct jbus *jbus, DBusMessage *message) +/* + * Checks if the incoming 'message' matches the interface + * linked to 'jbus'. + * + * Returns 1 if it matches or 0 wether it does not matches. + */ +static int matchitf(struct jbus *jbus, DBusMessage * message) { const char *itf = dbus_message_get_interface(message); return itf != NULL && !strcmp(itf, jbus->name); } +/* + * 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*), - void (*oncall_j)(struct jreq*, struct json_object*, void*), - void *data -) + void (*oncall_s) (struct jreq *, const char *, void *), + void (*oncall_j) (struct jreq *, struct json_object *, void *), + void *data) { struct jservice *srv; /* allocation */ - srv = malloc(sizeof * srv); + srv = malloc(sizeof *srv); if (srv == NULL) { errno = ENOMEM; goto error; @@ -150,37 +192,44 @@ static int add_service( return 0; -error2: + error2: free(srv); -error: + error: return -1; } +/* + * 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*), - void (*onsignal_j)(struct json_object*, void*), - void *data -) + struct jbus *jbus, + const char *name, + void (*onsignal_s) (const char *, void *), + void (*onsignal_j) (struct json_object *, void *), + void *data) { char *rule; struct jsignal *sig; /* record the signal */ if (jbus->signals == NULL) { -#if 0 - if (0 >= asprintf(&rule, "type='signal',interface='%s',path='%s'", jbus->name, jbus->path)) -#else - if (0 >= asprintf(&rule, "type='signal',sender='%s',interface='%s',path='%s'", jbus->name, jbus->name, jbus->path)) -#endif + 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); } /* allocation */ - sig = malloc(sizeof * sig); + sig = malloc(sizeof *sig); if (sig == NULL) goto error; sig->name = strdup(name); @@ -196,62 +245,87 @@ static int add_signal( return 0; -error2: + error2: free(sig); -error: + error: errno = ENOMEM; return -1; } +/* + * 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 status, const char *response, void *data), - void (*onresp_j)(int status, struct json_object *response, void *data), - void *data -) + 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) { DBusMessage *msg; struct jrespw *resp; - resp = malloc(sizeof * resp); + /* allocates the response structure */ + resp = malloc(sizeof *resp); if (resp == NULL) { errno = ENOMEM; goto error; } - msg = dbus_message_new_method_call(jbus->name, jbus->path, jbus->name, method); + /* creates the message */ + 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)) { + /* fill it */ + if (!dbus_message_append_args + (msg, DBUS_TYPE_STRING, &query, DBUS_TYPE_INVALID)) { errno = ENOMEM; goto error3; } + /* send it */ if (!dbus_connection_send(jbus->connection, msg, &resp->serial)) { + /* TODO: which error? */ goto error3; } + /* release the message that is not more used */ dbus_message_unref(msg); + + /* fulfill the response structure */ resp->data = data; resp->onresp_s = onresp_s; resp->onresp_j = onresp_j; + + /* links the response to list of reponse waiters */ resp->next = jbus->waiters; jbus->waiters = resp; return 0; -error3: + error3: dbus_message_unref(msg); -error2: + error2: free(resp); -error: + error: return -1; } +/* + * Callback function for synchronous calls. + * This function fills the respsync structure pointed by 'data' + * with the copy of the answer. + */ static void sync_of_replies(int status, const char *value, void *data) { struct respsync *s = data; @@ -259,7 +333,15 @@ static void sync_of_replies(int status, const char *value, void *data) s->replied = 1; } -static int parse(struct jbus *jbus, const char *msg, struct json_object **obj) +/* + * 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); @@ -270,7 +352,19 @@ static int parse(struct jbus *jbus, const char *msg, struct json_object **obj) return 0; } -static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage *message, struct jbus *jbus, int iserror) +/*********************** STATIC DBUS MESSAGE HANDLING *****************/ + +/* + * Handles incomming responses 'message' on 'jbus'. Response are + * either expected if 'iserror' == 0 or errors if 'iserror' != 0. + * + * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED + * as defined by the dbus function 'dbus_connection_add_filter'. + */ +static DBusHandlerResult incoming_resp( + struct jbus *jbus, + DBusMessage * message, + int iserror) { int status; const char *str; @@ -288,7 +382,8 @@ static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage * *prv = jrw->next; /* retrieve the string value */ - if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) + if (dbus_message_get_args + (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) status = 0; else { status = -1; @@ -300,7 +395,7 @@ static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage * if (jrw->onresp_s) jrw->onresp_s(iserror ? -1 : status, str, jrw->data); else { - status = parse(jbus, str, &reply) - 1; + status = jparse(jbus, str, &reply) - 1; jrw->onresp_j(iserror ? -1 : status, reply, jrw->data); json_object_put(reply); } @@ -309,7 +404,15 @@ static DBusHandlerResult incoming_resp(DBusConnection *connection, DBusMessage * return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage *message, struct jbus *jbus) +/* + * Handles incomming on 'jbus' method calls for 'message'. + * + * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED + * as defined by the dbus function 'dbus_connection_add_filter'. + */ +static DBusHandlerResult incoming_call( + struct jbus *jbus, + DBusMessage * message) { struct jservice *srv; struct jreq *jreq; @@ -324,63 +427,79 @@ static DBusHandlerResult incoming_call(DBusConnection *connection, DBusMessage * if (method == NULL) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; srv = jbus->services; - while(srv != NULL && strcmp(method, srv->method)) + 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); + /* creates and init the jreq structure */ + 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); + + /* retrieve the string parameter of the message */ + if (!dbus_message_get_args + (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) + goto invalid_request; + + /* send the message to the callback */ if (srv->oncall_s) { /* handling strings only */ srv->oncall_s(jreq, str, srv->data); - } - else { + } else { /* handling json only */ - if (!parse(jbus, str, &query)) - return reply_invalid_request(jreq); + if (!jparse(jbus, str, &query)) + goto invalid_request; srv->oncall_j(jreq, query, srv->data); json_object_put(query); } return DBUS_HANDLER_RESULT_HANDLED; + +invalid_request: + jbus_reply_error_s(jreq, invalid_request_string); + return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage *message, struct jbus *jbus) +/* + * Handles incomming on 'jbus' signal propagated with 'message'. + * + * This is a design choice to ignore invalid signals. + * + * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED + * as defined by the dbus function 'dbus_connection_add_filter'. + */ +static DBusHandlerResult incoming_signal( + struct jbus *jbus, + DBusMessage * message) { struct jsignal *sig; const char *str; const char *name; struct json_object *obj; - /* search for the service */ + /* search for the signal name */ 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)) + 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 (dbus_message_get_args + (message, NULL, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) { if (sig->onsignal_s) { /* handling strings only */ sig->onsignal_s(str, sig->data); - } - else { - /* handling json only */ - if (parse(jbus, str, &obj)) { + } else { + /* handling json only (if valid) */ + if (jparse(jbus, str, &obj)) { sig->onsignal_j(obj, sig->data); json_object_put(obj); } @@ -389,22 +508,36 @@ static DBusHandlerResult incoming_signal(DBusConnection *connection, DBusMessage return DBUS_HANDLER_RESULT_HANDLED; } -static DBusHandlerResult incoming(DBusConnection *connection, DBusMessage *message, void *data) +/* + * Filters incomming messages as defined by the dbus function + * 'dbus_connection_add_filter'. + * Returns DBUS_HANDLER_RESULT_HANDLED or DBUS_HANDLER_RESULT_NOT_YET_HANDLED. + */ +static DBusHandlerResult incoming( + DBusConnection * connection, + DBusMessage * message, + void *data) { - switch(dbus_message_get_type(message)) { + struct jbus *jbus = data; + switch (dbus_message_get_type(message)) { case DBUS_MESSAGE_TYPE_METHOD_CALL: - return incoming_call(connection, message, (struct jbus*)data); + return incoming_call(jbus, message); case DBUS_MESSAGE_TYPE_METHOD_RETURN: - return incoming_resp(connection, message, (struct jbus*)data, 0); + return incoming_resp(jbus, message, 0); case DBUS_MESSAGE_TYPE_ERROR: - return incoming_resp(connection, message, (struct jbus*)data, 1); + return incoming_resp(jbus, message, 1); case DBUS_MESSAGE_TYPE_SIGNAL: - return incoming_signal(connection, message, (struct jbus*)data); + return incoming_signal(jbus, message); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } -static void watchset(DBusWatch *watch, struct jbus *jbus) +/*********************** STATIC DBUS WATCH/POLLING INTERFACE **********/ + +/* + * Set the watched flags of 'jbus' following what DBUS expects by 'watch' + */ +static void watchset(DBusWatch * watch, struct jbus *jbus) { unsigned int flags; short wf; @@ -416,8 +549,7 @@ static void watchset(DBusWatch *watch, struct jbus *jbus) wf |= POLLIN; if (flags & DBUS_WATCH_WRITABLE) wf |= POLLOUT; - } - else { + } else { if (flags & DBUS_WATCH_READABLE) wf &= ~POLLIN; if (flags & DBUS_WATCH_WRITABLE) @@ -426,7 +558,11 @@ static void watchset(DBusWatch *watch, struct jbus *jbus) jbus->watchflags = wf; } -static void watchdel(DBusWatch *watch, void *data) +/* + * DBUS Callback for removing a 'watch'. + * See function 'dbus_connection_set_watch_functions' + */ +static void watchdel(DBusWatch * watch, void *data) { struct jbus *jbus = data; @@ -435,7 +571,11 @@ static void watchdel(DBusWatch *watch, void *data) jbus->watchnr--; } -static void watchtoggle(DBusWatch *watch, void *data) +/* + * DBUS Callback for changing a 'watch'. + * See function 'dbus_connection_set_watch_functions' + */ +static void watchtoggle(DBusWatch * watch, void *data) { struct jbus *jbus = data; @@ -444,61 +584,96 @@ static void watchtoggle(DBusWatch *watch, void *data) watchset(watch, jbus); } -static dbus_bool_t watchadd(DBusWatch *watch, void *data) +/* + * DBUS Callback for adding a 'watch'. + * See function 'dbus_connection_set_watch_functions' + */ +static dbus_bool_t watchadd(DBusWatch * watch, void *data) { struct jbus *jbus = data; if (jbus->watchnr == 0) { jbus->watchfd = dbus_watch_get_unix_fd(watch); jbus->watchflags = 0; - } - else if (jbus->watchfd != dbus_watch_get_unix_fd(watch)) + } else if (jbus->watchfd != dbus_watch_get_unix_fd(watch)) return FALSE; jbus->watchnr++; watchset(watch, jbus); return TRUE; } -/************************** MAIN FUNCTIONS *****************************************/ +/********************* MAIN FUNCTIONS *****************************************/ +/* + * Creates a 'jbus' bound to DBUS system using 'path' and returns it. + * See 'create_jbus' + */ struct jbus *create_jbus_system(const char *path) { return create_jbus(path, 0); } +/* + * Creates a 'jbus' bound to DBUS session using 'path' and returns it. + * See 'create_jbus' + */ struct jbus *create_jbus_session(const char *path) { return create_jbus(path, 1); } +/* + * 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(const char *path, int session) { 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); 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++; @@ -511,33 +686,43 @@ struct jbus *create_jbus(const char *path, int session) goto error2; } - /* connect */ - jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, NULL); + /* connect and init */ + jbus->connection = dbus_bus_get(session ? DBUS_BUS_SESSION + : DBUS_BUS_SYSTEM, NULL); if (jbus->connection == NULL - || !dbus_connection_add_filter(jbus->connection, incoming, jbus, NULL) - || !dbus_connection_set_watch_functions(jbus->connection, watchadd, watchdel, watchtoggle, jbus, NULL)) + || !dbus_connection_add_filter(jbus->connection, incoming, jbus, + NULL) + || !dbus_connection_set_watch_functions(jbus->connection, watchadd, + watchdel, watchtoggle, jbus, NULL)) goto error2; 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; if (!--jbus->refcount) { if (jbus->connection != NULL) dbus_connection_unref(jbus->connection); - while((srv = jbus->services) != NULL) { + while ((srv = jbus->services) != NULL) { jbus->services = srv->next; free(srv->method); free(srv); @@ -550,12 +735,19 @@ void jbus_unref(struct jbus *jbus) } } +/* + * Replies an error of string 'error' to the request handled by 'jreq'. + * Also destroys the request 'jreq' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_reply_error_s(struct jreq *jreq, const char *error) { int rc = -1; DBusMessage *message; - message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, error); + message = dbus_message_new_error(jreq->request, DBUS_ERROR_FAILED, + error); if (message == NULL) errno = ENOMEM; else { @@ -567,12 +759,24 @@ int jbus_reply_error_s(struct jreq *jreq, const char *error) return rc; } +/* + * Replies an error of json 'reply' to the request handled by 'jreq'. + * Also destroys the request 'jreq' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_reply_error_j(struct jreq *jreq, 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); } +/* + * Replies normally the string 'reply' to the request handled by 'jreq'. + * Also destroys the request 'jreq' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_reply_s(struct jreq *jreq, const char *reply) { int rc = -1; @@ -582,7 +786,8 @@ int jbus_reply_s(struct jreq *jreq, const char *reply) if (message == NULL) return reply_out_of_memory(jreq); - if (!dbus_message_append_args(message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) { + if (!dbus_message_append_args + (message, DBUS_TYPE_STRING, &reply, DBUS_TYPE_INVALID)) { dbus_message_unref(message); return reply_out_of_memory(jreq); } @@ -594,12 +799,23 @@ int jbus_reply_s(struct jreq *jreq, const char *reply) return rc; } +/* + * Replies normally the json 'reply' to the request handled by 'jreq'. + * Also destroys the request 'jreq' that must not be used later. + * + * Returns 0 in case of success or -1 in case of error. + */ int jbus_reply_j(struct jreq *jreq, 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); } +/* + * 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; @@ -610,7 +826,8 @@ int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content) goto error; if (!dbus_message_set_sender(message, jbus->name) - || !dbus_message_append_args(message, DBUS_TYPE_STRING, &content, DBUS_TYPE_INVALID)) { + || !dbus_message_append_args(message, DBUS_TYPE_STRING, &content, + DBUS_TYPE_INVALID)) { dbus_message_unref(message); goto error; } @@ -620,12 +837,18 @@ int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content) dbus_message_unref(message); return rc; -error: + error: errno = ENOMEM; return -1; } -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) { @@ -635,19 +858,61 @@ 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 *, void *), void *data) +/* + * 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 jreq *: 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 jreq *, const char *, void *), + void *data) { 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 *, void *), void *data) +/* + * 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 jreq *: 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 jreq *, struct json_object *, void *), + void *data) { 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); + 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: @@ -660,11 +925,17 @@ int jbus_start_serving(struct jbus *jbus) } } +/* + * Fills the at least 'njbuses' structures of array 'fds' with data needed + * to poll the 'njbuses' buses pointed by 'jbuses'. + * + * Returns the count of 'fds' structures filled. + */ int jbus_fill_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds) { int i, r; - for (r = i = 0 ; i < njbuses ; i++) { + for (r = i = 0; i < njbuses; i++) { if (jbuses[i]->watchnr) { fds[r].fd = jbuses[i]->watchfd; fds[r].events = jbuses[i]->watchflags; @@ -674,18 +945,32 @@ int jbus_fill_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds) return r; } -int jbus_dispatch_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds, int maxcount) +/* + * Dispatchs a maximum of 'maxcount' events received by poll in 'fds' for the + * 'njbuses' jbuses of the array 'jbuses'. + * + * Returns the count of event dispatched. + */ +int jbus_dispatch_pollfds( + struct jbus **jbuses, + int njbuses, + struct pollfd *fds, + int maxcount) { int i, r, n; DBusDispatchStatus sts; - for (r = n = i = 0 ; i < njbuses && n < maxcount ; i++) { + for (r = n = i = 0; i < njbuses && n < maxcount; i++) { if (jbuses[i]->watchnr && fds[r].fd == jbuses[i]->watchfd) { if (fds[r].revents) { - dbus_connection_read_write(jbuses[i]->connection, 0); - sts = dbus_connection_get_dispatch_status(jbuses[i]->connection); - while(sts == DBUS_DISPATCH_DATA_REMAINS && n < maxcount) { - sts = dbus_connection_dispatch(jbuses[i]->connection); + dbus_connection_read_write( + jbuses[i]->connection, 0); + sts = dbus_connection_get_dispatch_status( + jbuses[i]->connection); + while (sts == DBUS_DISPATCH_DATA_REMAINS + && n < maxcount) { + sts = dbus_connection_dispatch( + jbuses[i]->connection); n++; } } @@ -695,15 +980,22 @@ int jbus_dispatch_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds, return n; } +/* + * Dispatches 'maxcount' of buffered data from the 'njbuses' jbuses of the + * array 'jbuses'. + * + * Returns the count of event dispatched. + */ int jbus_dispatch_multiple(struct jbus **jbuses, int njbuses, int maxcount) { int i, r; DBusDispatchStatus sts; - for (i = r = 0 ; i < njbuses && r < maxcount ; i++) { + for (i = r = 0; i < njbuses && r < maxcount; i++) { dbus_connection_read_write(jbuses[i]->connection, 0); - sts = dbus_connection_get_dispatch_status(jbuses[i]->connection); - while(sts == DBUS_DISPATCH_DATA_REMAINS && r < maxcount) { + sts = dbus_connection_get_dispatch_status( + jbuses[i]->connection); + while (sts == DBUS_DISPATCH_DATA_REMAINS && r < maxcount) { sts = dbus_connection_dispatch(jbuses[i]->connection); r++; } @@ -711,7 +1003,17 @@ int jbus_dispatch_multiple(struct jbus **jbuses, int njbuses, int maxcount) return r; } -int jbus_read_write_dispatch_multiple(struct jbus **jbuses, int njbuses, int toms, int maxcount) +/* + * Polls during at most 'toms' milliseconds and dispatches 'maxcount' + * of events from the 'njbuses' jbuses of the array 'jbuses'. + * + * Returns the count of event dispatched or -1 in case of error. + */ +int jbus_read_write_dispatch_multiple( + struct jbus **jbuses, + int njbuses, + int toms, + int maxcount) { int n, r, s; struct pollfd *fds; @@ -720,15 +1022,15 @@ int jbus_read_write_dispatch_multiple(struct jbus **jbuses, int njbuses, int tom errno = EINVAL; return -1; } - fds = alloca((unsigned)njbuses * sizeof * fds); + fds = alloca((unsigned)njbuses * sizeof *fds); assert(fds != NULL); r = jbus_dispatch_multiple(jbuses, njbuses, maxcount); if (r) return r; n = jbus_fill_pollfds(jbuses, njbuses, fds); - for(;;) { - s = poll(fds, (nfds_t)n, toms); + for (;;) { + s = poll(fds, (nfds_t) n, toms); if (s >= 0) break; if (errno != EINTR) @@ -739,23 +1041,77 @@ int jbus_read_write_dispatch_multiple(struct jbus **jbuses, int njbuses, int tom return n >= 0 ? r + n : r ? r : n; } +/* + * Polls during at most 'toms' milliseconds and dispatches + * the events from 'jbus'. + * + * Returns the count of event dispatched or -1 in case of error. + */ int jbus_read_write_dispatch(struct jbus *jbus, int toms) { int r = jbus_read_write_dispatch_multiple(&jbus, 1, toms, 1000); return r < 0 ? r : 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) { @@ -765,7 +1121,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) { @@ -775,29 +1147,57 @@ 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)); + synchro.replied = + jbus_call_ss(jbus, method, query, sync_of_replies, &synchro); + while (!synchro.replied && !jbus_read_write_dispatch(jbus, -1)) ; return synchro.value; } -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) { struct json_object *obj; char *str = jbus_call_ss_sync(jbus, method, query); if (str == NULL) obj = NULL; else { - parse(jbus, str, &obj); + 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) { @@ -807,7 +1207,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) { @@ -817,17 +1226,43 @@ 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 *, void *), void *data) +/* + * 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, data); } -int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct json_object *, void *), void *data) +/* + * 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, data); } -/************************** FEW LITTLE TESTS *****************************************/ +/****************** FEW LITTLE TESTS *****************************************/ #ifdef SERVER #include <stdio.h> @@ -835,20 +1270,24 @@ int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsig)(struct j struct jbus *jbus; void ping(struct jreq *jreq, struct json_object *request, void *unused) { -printf("ping(%s) -> %s\n",json_object_to_json_string(request),json_object_to_json_string(request)); + 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); + json_object_put(request); } + void incr(struct jreq *jreq, 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)); + 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); + jbus_send_signal_j(jbus, "incremented", res); json_object_put(res); json_object_put(request); } + int main() { int s1, s2, s3; @@ -857,7 +1296,7 @@ int main() 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 (!jbus_read_write_dispatch(jbus, -1)) ; } #endif #ifdef CLIENT @@ -866,32 +1305,30 @@ int main() 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) { printf("signaled with {%s}\n", 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); + 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); } - 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 (!jbus_read_write_dispatch(jbus, -1)) ; } #endif - - - - - - - - diff --git a/plugins/afm-main-plugin/utils-jbus.h b/plugins/afm-main-plugin/utils-jbus.h index 11cb8ede..10690984 100644 --- a/plugins/afm-main-plugin/utils-jbus.h +++ b/plugins/afm-main-plugin/utils-jbus.h @@ -16,9 +16,9 @@ limitations under the License. */ - struct jreq; struct jbus; +struct pollfd; extern struct jbus *create_jbus(const char *path, int session); extern struct jbus *create_jbus_session(const char *path); @@ -27,38 +27,133 @@ extern struct jbus *create_jbus_system(const char *path); extern void jbus_addref(struct jbus *jbus); extern void jbus_unref(struct jbus *jbus); -struct pollfd; -extern int jbus_fill_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds); -extern int jbus_dispatch_pollfds(struct jbus **jbuses, int njbuses, struct pollfd *fds, int maxcount); -extern int jbus_read_write_dispatch_multiple(struct jbus **jbuses, int njbuses, int toms, int maxcount); -extern int jbus_dispatch_multiple(struct jbus **jbuses, int njbuses, int maxcount); +extern int jbus_fill_pollfds( + struct jbus **jbuses, + int njbuses, + struct pollfd *fds); -extern int jbus_read_write_dispatch(struct jbus *jbus, int toms); +extern int jbus_dispatch_pollfds( + struct jbus **jbuses, + int njbuses, + struct pollfd *fds, + int maxcount); -/* verbs for the clients */ -extern int jbus_call_ss(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, const char *, void *), void *data); -extern int jbus_call_js(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, const char *, void *), void *data); -extern int jbus_call_sj(struct jbus *jbus, const char *method, const char *query, void (*onresp)(int, struct json_object *, void *), void *data); -extern int jbus_call_jj(struct jbus *jbus, const char *method, struct json_object *query, void (*onresp)(int, struct json_object *, void *), void *data); +extern int jbus_read_write_dispatch_multiple( + struct jbus **jbuses, + int njbuses, + int toms, + int maxcount); -extern char *jbus_call_ss_sync(struct jbus *jbus, const char *method, const char *query); -extern char *jbus_call_js_sync(struct jbus *jbus, const char *method, struct json_object *query); -extern struct json_object *jbus_call_sj_sync(struct jbus *jbus, const char *method, const char *query); -extern struct json_object *jbus_call_jj_sync(struct jbus *jbus, const char *method, struct json_object *query); +extern int jbus_dispatch_multiple( + struct jbus **jbuses, + int njbuses, + int maxcount); -extern int jbus_on_signal_s(struct jbus *jbus, const char *name, void (*onsignal)(const char *, void *), void *data); -extern int jbus_on_signal_j(struct jbus *jbus, const char *name, void (*onsignal)(struct json_object *, void *), void *data); +extern int jbus_read_write_dispatch( + struct jbus *jbus, + int toms); -/* verbs for servers */ -extern int jbus_reply_s(struct jreq *jreq, const char *reply); -extern int jbus_reply_j(struct jreq *jreq, struct json_object *reply); -extern int jbus_reply_error_s(struct jreq *jreq, const char *reply); -extern int jbus_reply_error_j(struct jreq *jreq, struct json_object *reply); -extern int jbus_add_service_s(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, const char *, void *), void *data); -extern int jbus_add_service_j(struct jbus *jbus, const char *method, void (*oncall)(struct jreq *, struct json_object *, void *), void *data); -extern int jbus_start_serving(struct jbus *jbus); -extern int jbus_send_signal_s(struct jbus *jbus, const char *name, const char *content); -extern int jbus_send_signal_j(struct jbus *jbus, const char *name, struct json_object *content); +/* verbs for the clients */ +extern int jbus_call_ss( + struct jbus *jbus, + const char *method, + const char *query, + void (*onresp) (int, const char *, void *), + void *data); + +extern int jbus_call_js( + struct jbus *jbus, + const char *method, + struct json_object *query, + void (*onresp) (int, const char *, void *), + void *data); + +extern int jbus_call_sj( + struct jbus *jbus, + const char *method, + const char *query, + void (*onresp) (int, struct json_object *, void *), + void *data); + +extern int jbus_call_jj( + struct jbus *jbus, + const char *method, + struct json_object *query, + void (*onresp) (int, struct json_object *, void *), + void *data); + +extern char *jbus_call_ss_sync( + struct jbus *jbus, + const char *method, + const char *query); + +extern char *jbus_call_js_sync( + struct jbus *jbus, + const char *method, + struct json_object *query); + +extern struct json_object *jbus_call_sj_sync( + struct jbus *jbus, + const char *method, + const char *query); + +extern struct json_object *jbus_call_jj_sync( + struct jbus *jbus, + const char *method, + struct json_object *query); + +extern int jbus_on_signal_s( + struct jbus *jbus, + const char *name, + void (*onsignal) (const char *, void *), + void *data); + +extern int jbus_on_signal_j( + struct jbus *jbus, + const char *name, + void (*onsignal) (struct json_object *, void *), + void *data); +/* verbs for servers */ +extern int jbus_reply_s( + struct jreq *jreq, + const char *reply); + +extern int jbus_reply_j( + struct jreq *jreq, + struct json_object *reply); + +extern int jbus_reply_error_s( + struct jreq *jreq, + const char *reply); + +extern int jbus_reply_error_j( + struct jreq *jreq, + struct json_object *reply); + +extern int jbus_add_service_s( + struct jbus *jbus, + const char *method, + void (*oncall) (struct jreq *, const char *, void *), + void *data); + +extern int jbus_add_service_j( + struct jbus *jbus, + const char *method, + void (*oncall) (struct jreq *, struct json_object *, void *), + void *data); + +extern int jbus_start_serving( + struct jbus *jbus); + +extern int jbus_send_signal_s( + struct jbus *jbus, + const char *name, + const char *content); + +extern int jbus_send_signal_j( + struct jbus *jbus, + const char *name, + struct json_object *content); diff --git a/plugins/samples/ClientCtx.c b/plugins/samples/ClientCtx.c index 3fa43adf..0ea88087 100644 --- a/plugins/samples/ClientCtx.c +++ b/plugins/samples/ClientCtx.c @@ -75,7 +75,7 @@ static void myCreate (struct afb_req request) ctx->count = 0; ctx->abcd = "SomeThingUseful"; - request.context = ctx; + *request.context = ctx; afb_req_success_f(request, NULL, "SUCCESS: create client context for plugin [%s]", handle->anythingYouWant); } @@ -86,7 +86,7 @@ static void myCreate (struct afb_req request) static void myAction (struct afb_req request) { MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; - MyClientContextT *ctx = (MyClientContextT*) request.context; + MyClientContextT *ctx = (MyClientContextT*) *request.context; // store something in our plugin private client context ctx->count++; @@ -100,7 +100,7 @@ static void myAction (struct afb_req request) static void myClose (struct afb_req request) { MyPluginHandleT *handle = (MyPluginHandleT*) &global_handle; - MyClientContextT *ctx = (MyClientContextT*) request.context; + MyClientContextT *ctx = (MyClientContextT*) *request.context; // store something in our plugin private client context ctx->count++; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9ddb5113..3f8b84c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,13 +2,15 @@ ADD_LIBRARY(src OBJECT main.c session.c - http-svc.c + afb-hsrv.c afb-apis.c afb-method.c afb-hreq.c afb-websock.c websock.c - helper-api.c) + verbose.c + utils-upoll.c +) INCLUDE_DIRECTORIES(${include_dirs}) diff --git a/src/afb-apis.c b/src/afb-apis.c index 735649ca..9dcb1809 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -41,8 +41,12 @@ #include "afb-plugin.h" #include "afb-req-itf.h" +#include "afb-poll-itf.h" + #include "session.h" #include "afb-apis.h" +#include "verbose.h" +#include "utils-upoll.h" struct api_desc { struct AFB_plugin *plugin; /* descriptor */ @@ -58,6 +62,14 @@ static int apis_count = 0; static const char plugin_register_function[] = "pluginRegister"; +static const struct afb_poll_itf upoll_itf = { + .on_readable = (void*)upoll_on_readable, + .on_writable = (void*)upoll_on_writable, + .on_hangup = (void*)upoll_on_hangup, + .close = (void*)upoll_close +}; + + int afb_apis_count() { return apis_count; @@ -75,6 +87,15 @@ void afb_apis_free_context(int apiidx, void *context) free(context); } +static struct afb_poll itf_poll_open(int fd, void *closure) +{ + struct afb_poll result; + result.data = upoll_open(fd, closure); + result.itf = result.data ? &upoll_itf : NULL; + return result; +} + + int afb_apis_add_plugin(const char *path) { struct api_desc *apis; @@ -97,7 +118,7 @@ int afb_apis_add_plugin(const char *path) fprintf(stderr, "[%s] not an AFB plugin, continuing...\n", path); goto error2; } - if (verbose) + if (verbosity) fprintf(stderr, "[%s] is a valid AFB plugin\n", path); /* allocates enough memory */ @@ -114,8 +135,9 @@ int afb_apis_add_plugin(const char *path) fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path); goto error2; } - interface->verbose = 0; + interface->verbosity = 0; interface->mode = AFB_MODE_LOCAL; + interface->poll_open = itf_poll_open; /* init the plugin */ plugin = pluginRegisterFct(interface); @@ -151,7 +173,7 @@ int afb_apis_add_plugin(const char *path) } /* record the plugin */ - if (verbose) + if (verbosity) fprintf(stderr, "Loading plugin[%lu] prefix=[%s] info=%s\n", (unsigned long)apis_count, plugin->prefix, plugin->info); apis = &apis_array[apis_count]; apis->plugin = plugin; @@ -184,7 +206,7 @@ static int adddirs(char path[PATH_MAX], size_t end) fprintf(stderr, "ERROR in scanning plugin directory %s, %m\n", path); return -1; } - if (verbose) + if (verbosity) fprintf(stderr, "Scanning dir=[%s] for plugins\n", path); /* scan each entry */ @@ -347,9 +369,8 @@ int afb_apis_handle(struct afb_req req, struct AFB_clientCtx *context, const cha v = a->plugin->apis; for (j = 0 ; v->name ; j++, v++) { if (!strncasecmp(v->name, verb, lenverb) && !v->name[lenverb]) { - req.context = context->contexts[i]; + req.context = &context->contexts[i]; handle(req, v); - context->contexts[i] = req.context; return 1; } } diff --git a/src/afb-hreq.c b/src/afb-hreq.c index 16c235ab..ece9b399 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -15,6 +15,7 @@ * limitations under the License. */ +#define USE_MAGIC_MIME_TYPE #define _GNU_SOURCE #include <stdlib.h> @@ -27,11 +28,16 @@ #include <microhttpd.h> +#if defined(USE_MAGIC_MIME_TYPE) +#include <magic.h> +#endif + #include "local-def.h" #include "afb-method.h" #include "afb-req-itf.h" #include "afb-hreq.h" #include "session.h" +#include "verbose.h" #define SIZE_RESPONSE_BUFFER 8000 @@ -135,6 +141,51 @@ static int validsubpath(const char *subpath) return 1; } +#if defined(USE_MAGIC_MIME_TYPE) + +#if !defined(MAGIC_DB) +#define MAGIC_DB "/usr/share/misc/magic.mgc" +#endif + +static magic_t lazy_libmagic() +{ + static int done = 0; + static magic_t result = NULL; + + if (!done) { + done = 1; + /* MAGIC_MIME tells magic to return a mime of the file, + but you can specify different things */ + if (verbosity) + printf("Loading mimetype default magic database\n"); + + result = magic_open(MAGIC_MIME_TYPE); + if (result == NULL) { + fprintf(stderr,"ERROR: unable to initialize magic library\n"); + } + /* Warning: should not use NULL for DB + [libmagic bug wont pass efence check] */ + else if (magic_load(result, MAGIC_DB) != 0) { + fprintf(stderr,"cannot load magic database - %s\n", + magic_error(result)); + magic_close(result); + result = NULL; + } + } + + return result; +} + +static const char *magic_mimetype_fd(int fd) +{ + magic_t lib = lazy_libmagic(); + return lib ? magic_descriptor(lib, fd) : NULL; +} + +#endif + + + void afb_hreq_free(struct afb_hreq *hreq) { struct hreq_data *data; @@ -269,7 +320,7 @@ int afb_hreq_reply_file_if_exist(struct afb_hreq *hreq, int dirfd, const char *f if (inm && 0 == strcmp(inm, etag)) { /* etag ok, return NOT MODIFIED */ close(fd); - if (verbose) + if (verbosity) fprintf(stderr, "Not Modified: [%s]\n", filename); response = MHD_create_response_from_buffer(0, empty_string, MHD_RESPMEM_PERSISTENT); status = MHD_HTTP_NOT_MODIFIED; @@ -287,8 +338,8 @@ int afb_hreq_reply_file_if_exist(struct afb_hreq *hreq, int dirfd, const char *f #if defined(USE_MAGIC_MIME_TYPE) /* set the type */ - if (hreq->session->magic) { - const char *mimetype = magic_descriptor(hreq->session->magic, fd); + { + const char *mimetype = magic_mimetype_fd(fd); if (mimetype != NULL) MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_TYPE, mimetype); } @@ -319,7 +370,7 @@ int afb_hreq_redirect_to(struct afb_hreq *hreq, const char *url) MHD_add_response_header(response, MHD_HTTP_HEADER_LOCATION, url); MHD_queue_response(hreq->connection, MHD_HTTP_MOVED_PERMANENTLY, response); MHD_destroy_response(response); - if (verbose) + if (verbosity) fprintf(stderr, "redirect from [%s] to [%s]\n", hreq->url, url); return 1; } @@ -511,7 +562,7 @@ struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq) uuid = afb_hreq_get_argument(hreq, uuid_arg); if (uuid == NULL) uuid = afb_hreq_get_cookie(hreq, uuid_cookie); - hreq->context = ctxClientGet(uuid); + hreq->context = ctxClientGetForUuid(uuid); } return hreq->context; } diff --git a/src/afb-hreq.h b/src/afb-hreq.h index cf236380..853190a8 100644 --- a/src/afb-hreq.h +++ b/src/afb-hreq.h @@ -30,6 +30,7 @@ struct afb_hreq { struct MHD_PostProcessor *postform; struct AFB_clientCtx *context; struct hreq_data *data; + int upgrade; }; extern void afb_hreq_free(struct afb_hreq *request); diff --git a/src/http-svc.c b/src/afb-hsrv.c index c767de71..64cea9d5 100644 --- a/src/http-svc.c +++ b/src/afb-hsrv.c @@ -29,9 +29,12 @@ #include "local-def.h" #include "afb-method.h" #include "afb-hreq.h" +#include "afb-hsrv.h" #include "afb-websock.h" #include "afb-apis.h" #include "afb-req-itf.h" +#include "verbose.h" +#include "utils-upoll.h" #define JSON_CONTENT "application/json" #define FORM_CONTENT MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA @@ -53,32 +56,7 @@ struct afb_diralias { int dirfd; }; -int afb_hreq_one_page_api_redirect( - struct afb_hreq *hreq, - void *data) -{ - size_t plen; - char *url; - - if (hreq->lentail >= 2 && hreq->tail[1] == '#') - return 0; - /* - * Here we have for example: - * url = "/pre/dir/page" lenurl = 13 - * tail = "/dir/page" lentail = 9 - * - * We will produce "/pre/#!dir/page" - * - * Let compute plen that include the / at end (for "/pre/") - */ - plen = hreq->lenurl - hreq->lentail + 1; - url = alloca(hreq->lenurl + 3); - memcpy(url, hreq->url, plen); - url[plen++] = '#'; - url[plen++] = '!'; - memcpy(&url[plen], &hreq->tail[1], hreq->lentail); - return afb_hreq_redirect_to(hreq, url); -} +static struct upoll *upoll = NULL; static struct afb_hsrv_handler *new_handler( struct afb_hsrv_handler *head, @@ -137,6 +115,33 @@ int afb_hsrv_add_handler( return 1; } +int afb_hreq_one_page_api_redirect( + struct afb_hreq *hreq, + void *data) +{ + size_t plen; + char *url; + + if (hreq->lentail >= 2 && hreq->tail[1] == '#') + return 0; + /* + * Here we have for example: + * url = "/pre/dir/page" lenurl = 13 + * tail = "/dir/page" lentail = 9 + * + * We will produce "/pre/#!dir/page" + * + * Let compute plen that include the / at end (for "/pre/") + */ + plen = hreq->lenurl - hreq->lentail + 1; + url = alloca(hreq->lenurl + 3); + memcpy(url, hreq->url, plen); + url[plen++] = '#'; + url[plen++] = '!'; + memcpy(&url[plen], &hreq->tail[1], hreq->lentail); + return afb_hreq_redirect_to(hreq, url); +} + static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) { int later; @@ -146,7 +151,7 @@ static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) return 0; if (!later) { - struct afb_websock *ws = afb_websock_create(hreq->connection); + struct afb_websock *ws = afb_websock_create(hreq); if (ws == NULL) { /* TODO */ } else { @@ -362,7 +367,8 @@ static void end_handler(void *cls, struct MHD_Connection *connection, void **rec struct afb_hreq *hreq; hreq = *recordreq; - + if (hreq->upgrade) + MHD_suspend_connection (connection); afb_hreq_free(hreq); } @@ -371,36 +377,6 @@ static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t return MHD_YES; } -#if defined(USE_MAGIC_MIME_TYPE) - -#if !defined(MAGIC_DB) -#define MAGIC_DB "/usr/share/misc/magic.mgc" -#endif - -static int init_lib_magic (AFB_session *session) -{ - /* MAGIC_MIME tells magic to return a mime of the file, but you can specify different things */ - if (verbose) - printf("Loading mimetype default magic database\n"); - - session->magic = magic_open(MAGIC_MIME_TYPE); - if (session->magic == NULL) { - fprintf(stderr,"ERROR: unable to initialize magic library\n"); - return 0; - } - - /* Warning: should not use NULL for DB [libmagic bug wont pass efence check] */ - if (magic_load(session->magic, MAGIC_DB) != 0) { - fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic)); - magic_close(session->magic); - session->magic = NULL; - return 0; - } - - return 1; -} -#endif - static int my_default_init(AFB_session * session) { int idx; @@ -424,24 +400,28 @@ static int my_default_init(AFB_session * session) return 1; } -AFB_error httpdStart(AFB_session * session) +/* infinite loop */ +static void hsrv_handle_event(struct MHD_Daemon *httpd) +{ + MHD_run(httpd); +} + +int afb_hsrv_start(AFB_session * session) { + struct MHD_Daemon *httpd; + const union MHD_DaemonInfo *info; + if (!my_default_init(session)) { printf("Error: initialisation of httpd failed"); - return AFB_FATAL; + return 0; } -#if defined(USE_MAGIC_MIME_TYPE) - /*TBD open libmagic cache [fail to pass EFENCE check (allocating 0 bytes)] */ - init_lib_magic (session); -#endif - - if (verbose) { + if (verbosity) { printf("AFB:notice Waiting port=%d rootdir=%s\n", session->config->httpdPort, session->config->rootdir); printf("AFB:notice Browser URL= http:/*localhost:%d\n", session->config->httpdPort); } - session->httpd = MHD_start_daemon( + httpd = MHD_start_daemon( MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_TCP_FASTOPEN | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME, (uint16_t) session->config->httpdPort, /* port */ new_client_handler, NULL, /* Tcp Accept call back + extra attribute */ @@ -450,48 +430,37 @@ AFB_error httpdStart(AFB_session * session) MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)15, /* 15 seconds */ MHD_OPTION_END); /* options-end */ - if (session->httpd == NULL) { + if (httpd == NULL) { printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort); - return AFB_FATAL; + return 0; } - return AFB_SUCCESS; -} -/* infinite loop */ -AFB_error httpdLoop(AFB_session * session) -{ - int count = 0; - const union MHD_DaemonInfo *info; - struct pollfd pfd; - - info = MHD_get_daemon_info(session->httpd, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); + info = MHD_get_daemon_info(httpd, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); if (info == NULL) { - printf("Error: httpLoop no pollfd"); - goto error; + MHD_stop_daemon(httpd); + fprintf(stderr, "Error: httpStart no pollfd"); + return 0; } - pfd.fd = info->listen_fd; - pfd.events = POLLIN; - - if (verbose) - fprintf(stderr, "AFB:notice entering httpd waiting loop\n"); - while (TRUE) { - if (verbose) - fprintf(stderr, "AFB:notice httpd alive [%d]\n", count++); - poll(&pfd, 1, 15000); /* 15 seconds (as above timeout when starting) */ - MHD_run(session->httpd); + + upoll = upoll_open(info->listen_fd, httpd); + if (upoll == NULL) { + MHD_stop_daemon(httpd); + fprintf(stderr, "Error: connection to upoll of httpd failed"); + return 0; } + upoll_on_readable(upoll, (void*)hsrv_handle_event); - error: - /* should never return from here */ - return AFB_FATAL; + session->httpd = httpd; + return 1; } -int httpdStatus(AFB_session * session) +void afb_hsrv_stop(AFB_session * session) { - return MHD_run(session->httpd); + if (upoll) + upoll_close(upoll); + upoll = NULL; + if (session->httpd != NULL) + MHD_stop_daemon(session->httpd); + session->httpd = NULL; } -void httpdStop(AFB_session * session) -{ - MHD_stop_daemon(session->httpd); -} diff --git a/src/afb-hsrv.h b/src/afb-hsrv.h new file mode 100644 index 00000000..da4fe09a --- /dev/null +++ b/src/afb-hsrv.h @@ -0,0 +1,20 @@ +/* + Copyright 2016 IoT.bzh + + author: José Bollo <jose.bollo@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. +*/ + +int afb_hsrv_start(AFB_session * session); +void afb_hsrv_stop(AFB_session * session); diff --git a/src/afb-websock.c b/src/afb-websock.c index b9417cbe..dab4aa09 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -24,16 +24,27 @@ #include <sys/uio.h> #include <string.h> +#include <json.h> + #include <openssl/sha.h> +/* #include <openssl/bio.h> #include <openssl/evp.h> +*/ #include "websock.h" #include "local-def.h" +#include "afb-req-itf.h" #include "afb-method.h" #include "afb-hreq.h" +#include "afb-websock.h" +#include "afb-apis.h" +#include "session.h" +#include "utils-upoll.h" + +/**************** WebSocket connection upgrade ****************************/ static const char websocket_s[] = "websocket"; static const char sec_websocket_key_s[] = "Sec-WebSocket-Key"; @@ -42,57 +53,6 @@ static const char sec_websocket_accept_s[] = "Sec-WebSocket-Accept"; static const char sec_websocket_protocol_s[] = "Sec-WebSocket-Protocol"; static const char websocket_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - -struct afb_websock -{ - int fd; - struct MHD_Connection *connection; - struct websock *ws; -}; - -static ssize_t afb_websock_writev(struct afb_websock *ws, const struct iovec *iov, int iovcnt) -{ - ssize_t rc; - do { - rc = writev(ws->fd, iov, iovcnt); - } while(rc == -1 && errno == EINTR); - return rc; -} - -static ssize_t afb_websock_readv(struct afb_websock *ws, const struct iovec *iov, int iovcnt) -{ - ssize_t rc; - do { - rc = readv(ws->fd, iov, iovcnt); - } while(rc == -1 && errno == EINTR); - return rc; -} - -static void afb_websock_disconnect(struct afb_websock *ws) -{ -} - -static void afb_websock_on_close(struct afb_websock *ws, uint16_t code, size_t size) -{ -} - -static void afb_websock_on_content(struct afb_websock *ws, int last, size_t size) -{ -} - -static struct websock_itf afb_websock_itf = { - .writev = (void*)afb_websock_writev, - .readv = (void*)afb_websock_readv, - .disconnect = (void*)afb_websock_disconnect, - - .on_ping = NULL, - .on_pong = NULL, - .on_close = (void*)afb_websock_on_close, - .on_text = (void*)afb_websock_on_content, - .on_binary = (void*)afb_websock_on_content, - .on_continue = (void*)afb_websock_on_content -}; - static void enc64(unsigned char *in, char *out) { static const char tob64[] = @@ -197,20 +157,362 @@ int afb_websock_check(struct afb_hreq *hreq, int *later) return 1; } -struct afb_websock *afb_websock_create(struct MHD_Connection *connection) +/**************** WebSocket handling ****************************/ + +static ssize_t aws_writev(struct afb_websock *ws, const struct iovec *iov, int iovcnt); +static ssize_t aws_readv(struct afb_websock *ws, const struct iovec *iov, int iovcnt); +static void aws_disconnect(struct afb_websock *ws); +static void aws_on_close(struct afb_websock *ws, uint16_t code, size_t size); +static void aws_on_content(struct afb_websock *ws, int last, size_t size); +static void aws_on_readable(struct afb_websock *ws); + +static struct websock_itf aws_itf = { + .writev = (void*)aws_writev, + .readv = (void*)aws_readv, + .disconnect = (void*)aws_disconnect, + + .on_ping = NULL, + .on_pong = NULL, + .on_close = (void*)aws_on_close, + .on_text = (void*)aws_on_content, + .on_binary = (void*)aws_on_content, + .on_continue = (void*)aws_on_content +}; + +struct afb_wsreq +{ + struct afb_websock *aws; + struct afb_wsreq *next; + struct json_object *id; + struct json_object *name; + struct json_object *token; + struct json_object *request; +}; + +struct afb_websock +{ + int fd; + struct MHD_Connection *connection; + struct websock *ws; + struct upoll *up; + struct AFB_clientCtx *context; + struct json_tokener *tokener; + struct afb_wsreq *requests; +}; + +static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name); +static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure); +static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info); +static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info); +static int wsreq_session_create(struct afb_wsreq *wsreq); +static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh); +static void wsreq_session_close(struct afb_wsreq *wsreq); + +static const struct afb_req_itf wsreq_itf = { + .get = (void*)wsreq_get, + .iterate = (void*)wsreq_iterate, + .fail = (void*)wsreq_fail, + .success = (void*)wsreq_success, + .session_create = (void*)wsreq_session_create, + .session_check = (void*)wsreq_session_check, + .session_close = (void*)wsreq_session_close +}; + +struct afb_websock *afb_websock_create(struct afb_hreq *hreq) { struct afb_websock *result; result = malloc(sizeof * result); - if (result) { - result->connection = connection; - result->fd = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CONNECTION_FD)->connect_fd; - result->ws = websock_create(&afb_websock_itf, result); - if (result->ws == NULL) { - free(result); - result = NULL; + if (result == NULL) + goto error; + + result->connection = hreq->connection; + result->fd = MHD_get_connection_info(hreq->connection, + MHD_CONNECTION_INFO_CONNECTION_FD)->connect_fd; + result->context = ctxClientGet(afb_hreq_context(hreq)); + if (result->context == NULL) + goto error2; + + result->tokener = json_tokener_new(); + if (result->tokener == NULL) + goto error2; + + result->ws = websock_create(&aws_itf, result); + if (result->ws == NULL) + goto error3; + + result->up = upoll_open(result->fd, result); + if (result->up == NULL) + goto error4; + + upoll_on_readable(result->up, (void*)aws_on_readable); + upoll_on_hangup(result->up, (void*)aws_disconnect); + return result; +error4: + websock_destroy(result->ws); +error3: + json_tokener_free(result->tokener); +error2: + free(result); +error: + return NULL; +} + +static ssize_t aws_writev(struct afb_websock *ws, const struct iovec *iov, int iovcnt) +{ + ssize_t rc; + do { + rc = writev(ws->fd, iov, iovcnt); + } while(rc == -1 && errno == EINTR); + return rc; +} + +static ssize_t aws_readv(struct afb_websock *ws, const struct iovec *iov, int iovcnt) +{ + ssize_t rc; + do { + rc = readv(ws->fd, iov, iovcnt); + } while(rc == -1 && errno == EINTR); + return rc; +} + +static void aws_disconnect(struct afb_websock *ws) +{ + upoll_close(ws->up); + websock_destroy(ws->ws); + close(ws->fd); + MHD_resume_connection (ws->connection); + ctxClientPut(ws->context); + json_tokener_free(ws->tokener); + free(ws); +} + +static void aws_on_close(struct afb_websock *ws, uint16_t code, size_t size) +{ + /* do nothing */ +} + +static void aws_on_readable(struct afb_websock *ws) +{ + websock_dispatch(ws->ws); +} + +static int aws_handle_json(struct afb_websock *aws, struct json_object *obj) +{ + struct afb_req r; + int count, num, rc; + struct json_object *type, *id, *name, *req, *token; + struct afb_wsreq *wsreq; + const char *api, *verb; + size_t lenapi, lenverb; + + /* protocol inspired by http://www.gir.fr/ocppjs/ocpp_srpc_spec.shtml */ + + /* the object must be an array of 4 or 5 elements */ + if (!json_object_is_type(obj, json_type_array)) + goto error; + count = json_object_array_length(obj); + if (count < 4 || count > 5) + goto error; + + /* get the 5 elements: type id name request token */ + type = json_object_array_get_idx(obj, 0); + id = json_object_array_get_idx(obj, 1); + name = json_object_array_get_idx(obj, 2); + req = json_object_array_get_idx(obj, 3); + token = json_object_array_get_idx(obj, 4); + + /* check the types: int string string object string */ + if (!json_object_is_type(type, json_type_int)) + goto error; + if (!json_object_is_type(id, json_type_string)) + goto error; + if (!json_object_is_type(name, json_type_string)) + goto error; + if (!json_object_is_type(req, json_type_object)) + goto error; + if (token != NULL && !json_object_is_type(token, json_type_string)) + goto error; + + /* the type is only 2 */ + num = json_object_get_int(type); + if (num != 2) + goto error; + + /* checks the api/verb structure of name */ + api = json_object_get_string(name); + for (lenapi = 0 ; api[lenapi] && api[lenapi] != '/' ; lenapi++); + if (!lenapi || !api[lenapi]) + goto error; + verb = &api[lenapi+1]; + for (lenverb = 0 ; verb[lenverb] && verb[lenverb] != '/' ; lenverb++); + if (!lenverb || !verb[lenverb]) + goto error; + + /* allocates the request data */ + wsreq = malloc(sizeof *wsreq); + if (wsreq == NULL) + goto error; + + /* fill and record the request */ + wsreq->aws = aws; + wsreq->id = json_object_get(id); + wsreq->name = json_object_get(name); + wsreq->token = json_object_get(token); + wsreq->request = json_object_get(req); + wsreq->next = aws->requests; + aws->requests = wsreq; + json_object_put(obj); + + r.data = wsreq; + r.itf = &wsreq_itf; + rc = afb_apis_handle(r, aws->context, api, lenapi, verb, lenverb); + if (rc == 0) + wsreq_fail(wsreq, "ail", "api not found"); + return 1; + +error: + json_object_put(obj); + return 0; +} + +static void aws_on_content(struct afb_websock *ws, int last, size_t size) +{ + ssize_t rrc; + char buffer[8000]; + struct json_object *obj; + + json_tokener_reset(ws->tokener); + while(size) { + rrc = websock_read(ws->ws, buffer, + size > sizeof buffer ? sizeof buffer : size); + if (rrc < 0) { + websock_close(ws->ws); + return; + } + size -= (size_t)rrc; + obj = json_tokener_parse_ex(ws->tokener, buffer, (int)rrc); + if (obj != NULL) { + if (!aws_handle_json(ws, obj)) { + websock_close(ws->ws); + return; + } + } else if (json_tokener_get_error(ws->tokener) != json_tokener_continue) { + websock_close(ws->ws); + return; } } - return result; +} + + +static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name) +{ + struct afb_arg arg; + struct json_object *value; + + if (json_object_object_get_ex(wsreq->request, name, &value)) { + arg.name = name; + arg.value = json_object_get_string(value); + } else { + arg.name = NULL; + arg.value = NULL; + } + arg.size = 0; + arg.is_file = 0; + return arg; +} + +static void wsreq_iterate(struct afb_wsreq *wsreq, int (*iterator)(void *closure, struct afb_arg arg), void *closure) +{ + struct afb_arg arg; + struct json_object_iterator it = json_object_iter_begin(wsreq->request); + struct json_object_iterator end = json_object_iter_end(wsreq->request); + + arg.size = 0; + arg.is_file = 0; + while(!json_object_iter_equal(&it, &end)) { + arg.name = json_object_iter_peek_name(&it); + arg.value = json_object_get_string(json_object_iter_peek_value(&it)); + if (!iterator(closure, arg)) + break; + json_object_iter_next(&it); + } +} + +static int wsreq_session_create(struct afb_wsreq *wsreq) +{ + struct AFB_clientCtx *context = wsreq->aws->context; + if (context->created) + return 0; + return wsreq_session_check(wsreq, 1); +} + +static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh) +{ + const char *token; + struct AFB_clientCtx *context = wsreq->aws->context; + + if (wsreq->token == NULL) + return 0; + + token = json_object_get_string(wsreq->token); + if (token == NULL) + return 0; + + if (!ctxTokenCheck (context, token)) + return 0; + + if (refresh) { + ctxTokenNew (context); + } + + return 1; +} + +static void wsreq_session_close(struct afb_wsreq *wsreq) +{ + struct AFB_clientCtx *context = wsreq->aws->context; + ctxClientClose(context); +} + + +static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp) +{ + json_object *root, *request, *reply; + const char *message; + + /* builds the answering structure */ + root = json_object_new_object(); + json_object_object_add(root, "jtype", json_object_new_string("afb-reply")); + request = json_object_new_object(); + json_object_object_add(root, "request", request); + json_object_object_add(request, "status", json_object_new_string(status)); + if (info) + json_object_object_add(request, "info", json_object_new_string(info)); + if (resp) + json_object_object_add(root, "response", resp); + + /* make the reply */ + reply = json_object_new_array(); + json_object_array_add(reply, json_object_new_int(retcode)); + json_object_array_add(reply, wsreq->id); + json_object_array_add(reply, root); + json_object_array_add(reply, json_object_new_string(wsreq->aws->context->token)); + + /* emits the reply */ + message = json_object_to_json_string(reply); + websock_text(wsreq->aws->ws, message, strlen(message)); + + /* TODO eliminates the wsreq */ +} + +static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info) +{ + wsreq_reply(wsreq, 4, status, info, NULL); +} + +static void wsreq_success(struct afb_wsreq *wsreq, json_object *obj, const char *info) +{ + wsreq_reply(wsreq, 3, "success", info, obj); } diff --git a/src/afb-websock.h b/src/afb-websock.h index db40b9ae..be349563 100644 --- a/src/afb-websock.h +++ b/src/afb-websock.h @@ -16,6 +16,6 @@ */ int afb_websock_check(struct afb_hreq *hreq, int *later); -struct afb_websock *afb_websock_create(struct MHD_Connection *connection); +struct afb_websock *afb_websock_create(struct afb_hreq *hreq); diff --git a/src/helper-api.c b/src/helper-api.c deleted file mode 100644 index 9d1ec0b7..00000000 --- a/src/helper-api.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2015 "IoT.bzh" - * Author "Fulup Ar Foll" - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#define _GNU_SOURCE - -#include <stdio.h> -#include <stdarg.h> -#include <string.h> - -/* -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -*/ - -#include "local-def.h" -#include "afb-req-itf.h" - -// handle to hold queryAll values -typedef struct { - char *msg; - size_t idx; - size_t len; -} queryHandleT; - -// Error code are requested through function to manage json usage count -typedef struct { - int level; - const char* label; - json_object *json; -} AFB_errorT; - -static AFB_errorT AFBerr [AFB_UNAUTH+1]; -static json_object *jTypeStatic; - -PUBLIC int verbose; - -static const char *ERROR_LABEL[] = {"false", "true", "fatal", "fail", "warning", "empty", "success", "done", "unauth"}; - - -// Helper to retrieve argument from connection -const char* getQueryValue(const AFB_request * request, const char *name) { - return afb_req_argument(*request->areq, name); -} - -static int getQueryCB (queryHandleT *query, struct afb_arg arg) { - if (query->idx >= query->len) - return 0; - query->idx += (unsigned)snprintf (&query->msg[query->idx], query->len-query->idx, " %s: %s\'%s\',", arg.name, arg.is_file?"FILE=":"", arg.value); - return 1; /* continue to iterate */ -} - -// Helper to retrieve argument from connection -size_t getQueryAll(AFB_request * request, char *buffer, size_t len) { - queryHandleT query; - buffer[0] = '\0'; // start with an empty string - query.msg = buffer; - query.len = len; - query.idx = 0; - - afb_req_iterate(*request->areq, (void*)getQueryCB, &query); - buffer[len-1] = 0; - return query.idx >= len ? len - 1 : query.idx; -} - -#if 0 -char* getPostPath (AFB_request *request) { - AFB_PostHandle *postHandle = getPostHandle(request); - AFB_PostCtx *postFileCtx; - - if (postHandle == NULL) return NULL; - - postFileCtx = (AFB_PostCtx*) postHandle->ctx; - if (postFileCtx == NULL) return NULL; - - return (postFileCtx->path); -} - -json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) { - - AFB_PostHandle *postHandle = getPostHandle(request); - AFB_PostCtx *postFileCtx; - char filepath[512]; - ssize_t len; - - // This is called after PostForm and then after DonePostForm - if (item == NULL) { - json_object* jresp; - postFileCtx = (AFB_PostCtx*) postHandle->ctx; - - // No Post Application Context [something really bad happen] - if (postFileCtx == NULL) { - request->errcode = MHD_HTTP_EXPECTATION_FAILED; - return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n")); - } - - // We have a context but last Xform iteration fail or application set a message - if (request->jresp != NULL) { - jresp = request->jresp; // retrieve previous error from postCtx - } else jresp = jsonNewMessage(AFB_SUCCESS,"getPostFile Post Request done"); - - // Error or not let's free all resources - close(postFileCtx->fd); - free (postFileCtx->path); - free (postFileCtx); - return (jresp); - } -#if defined(PLEASE_FIX_ME_THE_ERROR_IS_postFileCtx_NOT_INITIALIZED) - // Make sure it's a valid PostForm request - if (!request->post && request->post->type != AFB_POST_FORM) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n"); - goto ExitOnError; - } - - // Check this is a file element - if (item->filename == NULL) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key); - goto ExitOnError; - } - - // Check we got something in buffer - if (item->len <= 0) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key); - goto ExitOnError; - } -#endif - // Extract Application Context from posthandle [NULL == 1st iteration] - postFileCtx = (AFB_PostCtx*) postHandle->ctx; - - // This is the 1st Item iteration let's open output file and allocate necessary resources - if (postFileCtx == NULL) { - DIR* destDir; - - // Create an application specific context - postFileCtx = calloc (1, sizeof(AFB_PostCtx)); // May place anything here until post->completeCB handle resources liberation - - // attach application to postHandle - postHandle->ctx = (void*) postFileCtx; // May place anything here until post->completeCB handle resources liberation - - // Build destination directory full path - if (destination[0] != '/') { - strncpy (filepath, request->config->sessiondir, sizeof(filepath)); - strncat (filepath, "/", sizeof(filepath)); - strncat (filepath, destination, sizeof(filepath)); - } else strncpy (filepath, destination, sizeof(filepath)); - - - // make sure destination directory exist - destDir = opendir (filepath); - if (destDir == NULL) { - if (mkdir(filepath,O_RDWR | S_IRWXU | S_IRGRP) < 0) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination directory=[%s] error=%s\n", filepath, strerror(errno)); - goto ExitOnError; - } - } else closedir (destDir); - - strncat (filepath, "/", sizeof(filepath)); - strncat (filepath, item->filename, sizeof(filepath)); - - postFileCtx->path = strdup (filepath); - if (verbose) fprintf(stderr, "getPostFile path=%s\n", filepath); - - if((postFileCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) <= 0) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination File=[%s] error=%s\n", filepath, strerror(errno)); - goto ExitOnError; - } - } else { - // reuse existing application context - postFileCtx = (AFB_PostCtx*) postHandle->ctx; - } - - // Check we successfully wrote full buffer - len = write (postFileCtx->fd, item->data, item->len); - if ((ssize_t)item->len != len) { - postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno)); - goto ExitOnError; - } - - // every intermediary iteration should return Success & NULL - request->errcode = MHD_HTTP_OK; - return NULL; - -ExitOnError: - request->errcode = MHD_HTTP_EXPECTATION_FAILED; - return NULL; -} - -#endif - -static void jsoninit() -{ - int idx, verbosesav; - - if (jTypeStatic) - return; - - // initialise JSON constant messages and increase reference count to make them permanent - verbosesav = verbose; - verbose = 0; // run initialisation in silent mode - jTypeStatic = json_object_new_string ("AFB_message"); - for (idx = 0; idx <= AFB_UNAUTH; idx++) { - AFBerr[idx].level = idx; - AFBerr[idx].label = ERROR_LABEL [idx]; - AFBerr[idx].json = jsonNewMessage (idx, NULL); - } - verbose = verbosesav; -} - - -// build an ERROR message and return it as a valid json object -json_object *json_add_status (json_object *obj, const char *status, const char *info) -{ - if (obj == NULL) - obj = json_object_new_object(); - json_object_object_add(obj, "status", json_object_new_string(status)); - if (info) - json_object_object_add(obj, "info", json_object_new_string(info)); - return obj; -} - -// build an ERROR message and return it as a valid json object -json_object *json_add_status_v (json_object *obj, const char *status, const char *info, va_list args) -{ - char *message; - if (info == NULL || vasprintf(&message, info, args) < 0) - message = NULL; - obj = json_add_status(obj, status, message); - free(message); - return obj; -} - - -// build an ERROR message and return it as a valid json object -json_object *json_add_status_f (json_object *obj, const char *status, const char *info, ...) -{ - va_list args; - va_start(args, info); - obj = json_add_status_v(obj, status, info, args); - va_end(args); - return obj; -} - - - -// build an ERROR message and return it as a valid json object -struct json_object *jsonNewMessage (AFB_error level, char* format, ...) { - static int count = 0; - json_object * AFBResponse; - va_list args; - char message [512]; - - jsoninit(); - - // format message - if (format != NULL) { - va_start(args, format); - vsnprintf (message, sizeof (message), format, args); - va_end(args); - } - - AFBResponse = json_object_new_object(); - json_object_object_add (AFBResponse, "jtype", json_object_get (jTypeStatic)); - json_object_object_add (AFBResponse, "status" , json_object_new_string (ERROR_LABEL[level])); - if (format != NULL) { - json_object_object_add (AFBResponse, "info" , json_object_new_string (message)); - } - if (verbose) { - fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++); - if (format != NULL) { - fprintf (stderr, "%s", message); - } else { - fprintf (stderr, "No Message"); - } - fprintf (stderr, "\n"); - } - - return (AFBResponse); -} - -#if 0 -{ - jtype: "AFB_message" - request: - { - prefix: "", - api: "", - status: "", /* exist, fail, empty, null, processed */ - info: "", - uuid: "", - token: "", - timeout: "" - } - response: ... -} -#endif - diff --git a/src/local-def.h b/src/local-def.h index 39ae3181..5735f6a2 100644 --- a/src/local-def.h +++ b/src/local-def.h @@ -22,7 +22,6 @@ #define LOCAL_DEF_H #include <json.h> -#include <magic.h> #include <microhttpd.h> /* other definitions --------------------------------------------------- */ @@ -40,24 +39,6 @@ #define DEFLT_HTTP_TIMEOUT 15 // Max MibMicroHttp timeout #define AFB_MAX_PLUGINS 20 // Max number of plugins for a given binder -#ifndef FALSE - #define FALSE 0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif - -#define PUBLIC -#define STATIC static -#define FAILED -1 - -#define AUDIO_BUFFER "/tmp/buf" - -extern int verbose; // this is the only global variable - -// prebuild json error are constructed in helper-api.c -typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS, AFB_DONE, AFB_UNAUTH} AFB_error; - #define MAX_POST_SIZE 4096 // maximum size for POST data #define CTX_NBCLIENTS 10 // allow a default of 10 authenticated clients @@ -65,11 +46,6 @@ typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY - - - - - enum AFB_Mode; @@ -127,7 +103,6 @@ struct AFB_session struct MHD_Daemon *httpd; // structure for httpd handler int fakemod; // respond to GET/POST request without interacting with sndboard int readyfd; // a #fd to signal when ready to serve - magic_t magic; // Mime type file magic lib struct afb_hsrv_handler *handlers; }; @@ -135,6 +110,4 @@ struct AFB_session typedef struct AFB_config AFB_config; typedef struct AFB_session AFB_session; -#include "proto-def.h" - #endif /* LOCAL_DEF_H */ @@ -31,7 +31,10 @@ #include "local-def.h" #include "afb-apis.h" +#include "afb-hsrv.h" #include "session.h" +#include "verbose.h" +#include "utils-upoll.h" #if !defined(PLUGIN_INSTALL_DIR) #error "you should define PLUGIN_INSTALL_DIR" @@ -124,24 +127,29 @@ static void printVersion (void) } // load config from disk and merge with CLI option -static AFB_error config_set_default (AFB_session * session) +static void config_set_default (AFB_session * session) { static char cacheTimeout [10]; // default HTTP port - if (session->config->httpdPort == 0) session->config->httpdPort=1234; + if (session->config->httpdPort == 0) + session->config->httpdPort = 1234; // default Plugin API timeout - if (session->config->apiTimeout == 0) session->config->apiTimeout=DEFLT_API_TIMEOUT; + if (session->config->apiTimeout == 0) + session->config->apiTimeout = DEFLT_API_TIMEOUT; // default AUTH_TOKEN - if (session->config->token == NULL) session->config->token= DEFLT_AUTH_TOKEN; + if (session->config->token == NULL) + session->config->token = DEFLT_AUTH_TOKEN; // cache timeout default one hour - if (session->config->cacheTimeout == 0) session->config->cacheTimeout=DEFLT_CACHE_TIMEOUT; + if (session->config->cacheTimeout == 0) + session->config->cacheTimeout = DEFLT_CACHE_TIMEOUT; // cache timeout default one hour - if (session->config->cntxTimeout == 0) session->config->cntxTimeout=DEFLT_CNTX_TIMEOUT; + if (session->config->cntxTimeout == 0) + session->config->cntxTimeout = DEFLT_CNTX_TIMEOUT; if (session->config->rootdir == NULL) { session->config->rootdir = getenv("AFBDIR"); @@ -155,17 +163,14 @@ static AFB_error config_set_default (AFB_session * session) } // if no Angular/HTML5 rootbase let's try '/' as default - if (session->config->rootbase == NULL) { + if (session->config->rootbase == NULL) session->config->rootbase = "/opa"; - } - if (session->config->rootapi == NULL) { + if (session->config->rootapi == NULL) session->config->rootapi = "/api"; - } - if (session->config->ldpaths == NULL) { + if (session->config->ldpaths == NULL) session->config->ldpaths = PLUGIN_INSTALL_DIR; - } // if no session dir create a default path from rootdir if (session->config->sessiondir == NULL) { @@ -184,8 +189,6 @@ static AFB_error config_set_default (AFB_session * session) // cacheTimeout is an integer but HTTPd wants it as a string snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout); session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version - - return AFB_SUCCESS; } @@ -255,7 +258,7 @@ static void parse_arguments(int argc, char *argv[], AFB_session *session) switch (optc) { case SET_VERBOSE: - verbose = 1; + verbosity++; break; case SET_TCP_PORT: @@ -276,19 +279,19 @@ static void parse_arguments(int argc, char *argv[], AFB_session *session) case SET_ROOT_DIR: if (optarg == 0) goto needValueForOption; session->config->rootdir = optarg; - if (verbose) fprintf(stderr, "Forcing Rootdir=%s\n",session->config->rootdir); + if (verbosity) fprintf(stderr, "Forcing Rootdir=%s\n",session->config->rootdir); break; case SET_ROOT_BASE: if (optarg == 0) goto needValueForOption; session->config->rootbase = optarg; - if (verbose) fprintf(stderr, "Forcing Rootbase=%s\n",session->config->rootbase); + if (verbosity) fprintf(stderr, "Forcing Rootbase=%s\n",session->config->rootbase); break; case SET_ROOT_API: if (optarg == 0) goto needValueForOption; session->config->rootapi = optarg; - if (verbose) fprintf(stderr, "Forcing Rootapi=%s\n",session->config->rootapi); + if (verbosity) fprintf(stderr, "Forcing Rootapi=%s\n",session->config->rootapi); break; case SET_ALIAS: @@ -300,7 +303,7 @@ static void parse_arguments(int argc, char *argv[], AFB_session *session) } else { aliasdir[aliascount].path = optarg; aliasdir[aliascount].len = strlen(aliasdir[aliascount].url); - if (verbose) fprintf(stderr, "Alias url=%s path=%s\n", aliasdir[aliascount].url, aliasdir[aliascount].path); + if (verbosity) fprintf(stderr, "Alias url=%s path=%s\n", aliasdir[aliascount].url, aliasdir[aliascount].path); aliascount++; } } else { @@ -404,20 +407,11 @@ static void closeSession (int status, void *data) { /*---------------------------------------------------------- | timeout signalQuit - | +--------------------------------------------------------- */ -void signalQuit (int signum) { - - sigset_t sigset; - - // unlock timeout signal to allow a new signal to come - sigemptyset (&sigset); - sigaddset (&sigset, SIGABRT); - sigprocmask (SIG_UNBLOCK, &sigset, 0); - - fprintf (stderr, "ERR: Received signal quit\n"); - syslog (LOG_ERR, "Daemon got kill3 & quit [please report bug]"); - exit(1); +void signalQuit (int signum) +{ + fprintf(stderr, "Terminating signal received %s\n", strsignal(signum)); + exit(1); } @@ -437,6 +431,10 @@ static void signalError(int signum) sigprocmask(SIG_UNBLOCK, &sigset, 0); longjmp(*error_handler, signum); } + if (signum == SIGALRM) + return; + fprintf(stderr, "Unmonitored signal received %s\n", strsignal(signum)); + exit(2); } static void install_error_handlers() @@ -452,30 +450,6 @@ static void install_error_handlers() } /*---------------------------------------------------------- - | listenLoop - | Main listening HTTP loop - +--------------------------------------------------------- */ -static void listenLoop (AFB_session *session) { - AFB_error err; - - // ------ Start httpd server - - err = httpdStart (session); - if (err != AFB_SUCCESS) return; - - if (session->readyfd != 0) { - static const char readystr[] = "READY=1"; - write(session->readyfd, readystr, sizeof(readystr) - 1); - close(session->readyfd); - } - - // infinite loop - httpdLoop(session); - - fprintf (stderr, "hoops returned from infinite loop [report bug]\n"); -} - -/*---------------------------------------------------------- | daemonize | set the process in background +--------------------------------------------------------- */ @@ -528,6 +502,7 @@ static void daemonize(AFB_session *session) +--------------------------------------------------------- */ int main(int argc, char *argv[]) { + int rc; AFB_session *session; // open syslog if ever needed @@ -558,7 +533,8 @@ int main(int argc, char *argv[]) { install_error_handlers(); // ------------------ Some useful default values ------------------------- - if ((session->background == 0) && (session->foreground == 0)) session->foreground=1; + if ((session->background == 0) && (session->foreground == 0)) + session->foreground = 1; // ------------------ clean exit on CTR-C signal ------------------------ if (signal (SIGINT, signalQuit) == SIG_ERR || signal (SIGABRT, signalQuit) == SIG_ERR) { @@ -566,7 +542,6 @@ int main(int argc, char *argv[]) { return 1; } - // let's run this program with a low priority nice (20); @@ -574,26 +549,19 @@ int main(int argc, char *argv[]) { // let's not take the risk to run as ROOT //if (getuid() == 0) goto errorNoRoot; -#if defined(ALLOWS_SESSION_FILES) - // check session dir and create if it does not exist - if (sessionCheckdir (session) != AFB_SUCCESS) { - fprintf (stderr,"\nERR: AFB-daemon cannot read/write session dir\n\n"); - exit (1); - } -#endif - if (verbose) fprintf (stderr, "AFB: notice Init config done\n"); + if (verbosity) fprintf (stderr, "AFB: notice Init config done\n"); // ---- run in foreground mode -------------------- if (session->foreground) { - if (verbose) fprintf (stderr,"AFB: notice Foreground mode\n"); + if (verbosity) fprintf (stderr,"AFB: notice Foreground mode\n"); } // end foreground // --------- run in background mode ----------- if (session->background) { - if (verbose) printf ("AFB: Entering background mode\n"); + if (verbosity) printf ("AFB: Entering background mode\n"); daemonize(session); @@ -604,10 +572,26 @@ int main(int argc, char *argv[]) { } // end background-foreground - listenLoop(session); - if (verbose) printf ("\n---- Application Framework Binder Normal End ------\n"); - exit(0); + // ------ Start httpd server + + rc = afb_hsrv_start (session); + if (!rc) + exit(1); + + if (session->readyfd != 0) { + static const char readystr[] = "READY=1"; + write(session->readyfd, readystr, sizeof(readystr) - 1); + close(session->readyfd); + } + + // infinite loop + for(;;) + upoll_wait(30000); + + if (verbosity) + fprintf (stderr, "hoops returned from infinite loop [report bug]\n"); + return 0; } diff --git a/src/proto-def.h b/src/proto-def.h deleted file mode 100644 index e2190eb1..00000000 --- a/src/proto-def.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - proto-def.h -- provide a REST/HTTP interface - - Copyright (C) 2015, Fulup Ar Foll - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -// helper-api -extern const char* getQueryValue (const AFB_request * request, const char *name); -extern size_t getQueryAll(AFB_request * request, char *query, size_t len); -/* -extern json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) ; -extern char* getPostPath (AFB_request *request); -*/ - -extern json_object *jsonNewMessage (AFB_error level, char* format, ...); - - -// Httpd server -extern AFB_error httpdStart (AFB_session *session); -extern AFB_error httpdLoop (AFB_session *session); -extern void httpdStop (AFB_session *session); - - - - diff --git a/src/session.c b/src/session.c index e9cf298e..da2294bc 100644 --- a/src/session.c +++ b/src/session.c @@ -168,7 +168,7 @@ static void ctxStoreCleanUp (time_t now) } // This function will return exiting client context or newly created client context -struct AFB_clientCtx *ctxClientGet (const char *uuid) +struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid) { uuid_t newuuid; struct AFB_clientCtx *clientCtx; @@ -215,6 +215,13 @@ struct AFB_clientCtx *ctxClientGet (const char *uuid) return NULL; } +struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx) +{ + if (clientCtx != NULL) + clientCtx->refcount++; + return clientCtx; +} + void ctxClientPut(struct AFB_clientCtx *clientCtx) { if (clientCtx != NULL) { diff --git a/src/session.h b/src/session.h index 28f25991..10af74fd 100644 --- a/src/session.h +++ b/src/session.h @@ -28,7 +28,8 @@ struct AFB_clientCtx extern void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok); -extern struct AFB_clientCtx *ctxClientGet (const char *uuid); +extern struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid); +extern struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx); extern void ctxClientPut(struct AFB_clientCtx *clientCtx); extern void ctxClientClose (struct AFB_clientCtx *clientCtx); extern int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token); diff --git a/src/utils-upoll.c b/src/utils-upoll.c new file mode 100644 index 00000000..6db2246d --- /dev/null +++ b/src/utils-upoll.c @@ -0,0 +1,171 @@ +/* + * Copyright 2016 IoT.bzh + * Author: José Bollo <jose.bollo@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. + */ + +#include <sys/epoll.h> +#include <pthread.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <assert.h> + +#include "utils-upoll.h" + + +struct upoll +{ + int fd; + void (*read)(void *); + void (*write)(void *); + void (*hangup)(void *); + void *closure; + struct upoll *next; +}; + +static int pollfd = 0; +static struct upoll *head = NULL; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +int upoll_is_valid(struct upoll *upoll) +{ + struct upoll *it = head; + while (it != NULL) { + if (it == upoll) + return 1; + it = it->next; + } + return 0; +} + +struct upoll *upoll_open(int fd, void *closure) +{ + struct epoll_event e; + struct upoll *result; + int rc; + + /* opens the epoll stream */ + if (pollfd == 0) { + pollfd = epoll_create1(EPOLL_CLOEXEC); + if (pollfd == 0) { + pollfd = dup(0); + close(0); + } + if (pollfd < 0) { + pollfd = 0; + return NULL; + } + } + + /* allocates */ + result = calloc(1, sizeof *result); + if (result == NULL) + return NULL; + + /* init */ + result->fd = fd; + result->closure = closure; + pthread_mutex_lock(&mutex); + result->next = head; + head = result; + pthread_mutex_unlock(&mutex); + + /* records */ + e.events = 0; + e.data.ptr = result; + rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e); + if (rc == 0) + return result; + + /* revert on error */ + rc = errno; + upoll_close(result); + errno = rc; + return NULL; +} + +static int update(struct upoll *upoll) +{ + struct epoll_event e; + e.events = (uint32_t)((upoll->read != NULL ? EPOLLIN : 0 ) + | (upoll->write != NULL ? EPOLLOUT : 0)); + e.data.ptr = upoll; + return epoll_ctl(pollfd, EPOLL_CTL_MOD, upoll->fd, &e); +} + +int upoll_on_readable(struct upoll *upoll, void (*process)(void *)) +{ + assert(pollfd != 0); + assert(upoll_is_valid(upoll)); + + upoll->read = process; + return update(upoll); +} + +int upoll_on_writable(struct upoll *upoll, void (*process)(void *)) +{ + assert(pollfd != 0); + assert(upoll_is_valid(upoll)); + + upoll->write = process; + return update(upoll); +} + +void upoll_on_hangup(struct upoll *upoll, void (*process)(void *)) +{ + assert(pollfd != 0); + assert(upoll_is_valid(upoll)); + + upoll->hangup = process; +} + +void upoll_close(struct upoll *upoll) +{ + struct upoll **it; + + assert(pollfd != 0); + assert(upoll_is_valid(upoll)); + + epoll_ctl(pollfd, EPOLL_CTL_DEL, upoll->fd, NULL); + pthread_mutex_lock(&mutex); + it = &head; + while (*it != upoll) + it = &(*it)->next; + *it = upoll->next; + pthread_mutex_unlock(&mutex); + free(upoll); +} + +void upoll_wait(int timeout) +{ + int rc; + struct epoll_event e; + struct upoll *upoll; + + if (pollfd == 0) + return; + + rc = epoll_wait(pollfd, &e, 1, timeout); + if (rc == 1) { + upoll = e.data.ptr; + if ((e.events & EPOLLIN) && upoll->read) + upoll->read(upoll->closure); + if ((e.events & EPOLLOUT) && upoll->write) + upoll->write(upoll->closure); + if ((e.events & EPOLLHUP) && upoll->hangup) + upoll->hangup(upoll->closure); + } +} + diff --git a/src/utils-upoll.h b/src/utils-upoll.h new file mode 100644 index 00000000..705fbc36 --- /dev/null +++ b/src/utils-upoll.h @@ -0,0 +1,32 @@ +/* + * Copyright 2016 IoT.bzh + * Author: José Bollo <jose.bollo@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 upoll; + +extern int upoll_is_valid(struct upoll *upoll); + +extern struct upoll *upoll_open(int fd, void *closure); + +extern int upoll_on_readable(struct upoll *upoll, void (*process)(void *closure)); +extern int upoll_on_writable(struct upoll *upoll, void (*process)(void *closure)); + +extern void upoll_on_hangup(struct upoll *upoll, void (*process)(void *closure)); + +extern void upoll_close(struct upoll *upoll); + +extern void upoll_wait(int timeout); + diff --git a/src/verbose.c b/src/verbose.c new file mode 100644 index 00000000..7e46a333 --- /dev/null +++ b/src/verbose.c @@ -0,0 +1,29 @@ +/* + Copyright 2015 IoT.bzh + + author: José Bollo <jose.bollo@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. +*/ + +#include "verbose.h" + +#if !defined(NDEBUG) +int verbosity = 1; +#else +void verbose_error(const char *file, int line) +{ + syslog(LOG_ERR, "error file %s line %d", file, line); +} +#endif + diff --git a/src/verbose.h b/src/verbose.h new file mode 100644 index 00000000..09254b22 --- /dev/null +++ b/src/verbose.h @@ -0,0 +1,39 @@ +/* + Copyright 2016 IoT.bzh + + author: José Bollo <jose.bollo@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. +*/ + +#if !defined(NDEBUG) +#include <syslog.h> +extern int verbosity; +#define LOGUSER(app) openlog(app,LOG_PERROR,LOG_USER) +#define LOGAUTH(app) openlog(app,LOG_PERROR,LOG_AUTH) +#define ERROR(...) syslog(LOG_ERR,__VA_ARGS__) +#define WARNING(...) do{if(verbosity)syslog(LOG_WARNING,__VA_ARGS__);}while(0) +#define NOTICE(...) do{if(verbosity)syslog(LOG_NOTICE,__VA_ARGS__);}while(0) +#define INFO(...) do{if(verbosity>1)syslog(LOG_INFO,__VA_ARGS__);}while(0) +#define DEBUG(...) do{if(verbosity>2)syslog(LOG_DEBUG,__VA_ARGS__);}while(0) +#else +#include <syslog.h> +#define LOGUSER(app) openlog(app,LOG_PERROR,LOG_USER) +#define LOGAUTH(app) openlog(app,LOG_PERROR,LOG_AUTH) +extern void verbose_error(const char *file, int line); +#define ERROR(...) verbose_error(__FILE__,__LINE__) +#define WARNING(...) do{/*nothing*/}while(0) +#define NOTICE(...) do{/*nothing*/}while(0) +#define INFO(...) do{/*nothing*/}while(0) +#define DEBUG(...) do{/*nothing*/}while(0) +#endif |