From 13549775092afa9215de8468e34f6d194c2fd8db Mon Sep 17 00:00:00 2001 From: José Bollo Date: Tue, 5 Apr 2016 15:25:28 +0200 Subject: new main loop in place MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: If297e0a76e74422d456447be52cca460c9e237b3 Signed-off-by: José Bollo --- include/afb-plugin.h | 2 +- include/afb-poll-itf.h | 4 +- src/CMakeLists.txt | 2 +- src/afb-apis.c | 8 +- src/afb-hsrv.c | 499 +++++++++++++++++++++++++++++++++++++++++++++++++ src/http-svc.c | 498 ------------------------------------------------ src/local-def.h | 23 --- src/main.c | 109 +++++------ src/proto-def.h | 30 --- src/utils-upoll.c | 49 ++++- src/utils-upoll.h | 7 +- src/verbose.h | 2 +- 12 files changed, 600 insertions(+), 633 deletions(-) create mode 100644 src/afb-hsrv.c delete mode 100644 src/http-svc.c delete mode 100644 src/proto-def.h diff --git a/include/afb-plugin.h b/include/afb-plugin.h index ba86e181..4064f9e9 100644 --- a/include/afb-plugin.h +++ b/include/afb-plugin.h @@ -67,7 +67,7 @@ struct AFB_interface { int verbosity; enum AFB_Mode mode; - struct afb_poll (*poll_open)(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure); + 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 index 4ce1fa48..2c8889b6 100644 --- a/include/afb-poll-itf.h +++ b/include/afb-poll-itf.h @@ -17,7 +17,9 @@ struct afb_poll_itf { - int (*update)(void *data, uint32_t events); + 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); }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8bddcf0..3f8b84c6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,7 @@ ADD_LIBRARY(src OBJECT main.c session.c - http-svc.c + afb-hsrv.c afb-apis.c afb-method.c afb-hreq.c diff --git a/src/afb-apis.c b/src/afb-apis.c index 55e68c93..9dcb1809 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -63,7 +63,9 @@ static int apis_count = 0; static const char plugin_register_function[] = "pluginRegister"; static const struct afb_poll_itf upoll_itf = { - .update = (void*)upoll_update, + .on_readable = (void*)upoll_on_readable, + .on_writable = (void*)upoll_on_writable, + .on_hangup = (void*)upoll_on_hangup, .close = (void*)upoll_close }; @@ -85,10 +87,10 @@ void afb_apis_free_context(int apiidx, void *context) free(context); } -static struct afb_poll itf_poll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure) +static struct afb_poll itf_poll_open(int fd, void *closure) { struct afb_poll result; - result.data = upoll_open(fd, events, process, closure); + result.data = upoll_open(fd, closure); result.itf = result.data ? &upoll_itf : NULL; return result; } diff --git a/src/afb-hsrv.c b/src/afb-hsrv.c new file mode 100644 index 00000000..a358cbd7 --- /dev/null +++ b/src/afb-hsrv.c @@ -0,0 +1,499 @@ +/* + * Copyright 2016 IoT.bzh + * Author: José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include + +#include "local-def.h" +#include "afb-method.h" +#include "afb-hreq.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 + + +struct afb_hsrv_handler { + struct afb_hsrv_handler *next; + const char *prefix; + size_t length; + int (*handler) (struct afb_hreq *, void *); + void *data; + int priority; +}; + +struct afb_diralias { + const char *alias; + const char *directory; + size_t lendir; + int dirfd; +}; + +static struct upoll *upoll = NULL; + +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 afb_hsrv_handler *new_handler( + struct afb_hsrv_handler *head, + const char *prefix, + int (*handler) (struct afb_hreq *, void *), + void *data, + int priority) +{ + struct afb_hsrv_handler *link, *iter, *previous; + size_t length; + + /* get the length of the prefix without its leading / */ + length = strlen(prefix); + while (length && prefix[length - 1] == '/') + length--; + + /* allocates the new link */ + link = malloc(sizeof *link); + if (link == NULL) + return NULL; + + /* initialize it */ + link->prefix = prefix; + link->length = length; + link->handler = handler; + link->data = data; + link->priority = priority; + + /* adds it */ + previous = NULL; + iter = head; + while (iter && (priority < iter->priority || (priority == iter->priority && length <= iter->length))) { + previous = iter; + iter = iter->next; + } + link->next = iter; + if (previous == NULL) + return link; + previous->next = link; + return head; +} + +int afb_hsrv_add_handler( + AFB_session * session, + const char *prefix, + int (*handler) (struct afb_hreq *, void *), + void *data, + int priority) +{ + struct afb_hsrv_handler *head; + + head = new_handler(session->handlers, prefix, handler, data, priority); + if (head == NULL) + return 0; + session->handlers = head; + return 1; +} + +static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) +{ + int later; + + afb_hreq_context(hreq); + if (hreq->lentail != 0 || !afb_websock_check(hreq, &later)) + return 0; + + if (!later) { + struct afb_websock *ws = afb_websock_create(hreq->connection); + if (ws == NULL) { + /* TODO */ + } else { + /* TODO */ + } + } + return 1; +} + +static int afb_hreq_rest_api(struct afb_hreq *hreq, void *data) +{ + const char *api, *verb; + size_t lenapi, lenverb; + struct AFB_clientCtx *context; + + api = &hreq->tail[strspn(hreq->tail, "/")]; + lenapi = strcspn(api, "/"); + verb = &api[lenapi]; + verb = &verb[strspn(verb, "/")]; + lenverb = strcspn(verb, "/"); + + if (!(*api && *verb && lenapi && lenverb)) + return 0; + + context = afb_hreq_context(hreq); + return afb_apis_handle(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb); +} + +static int handle_alias(struct afb_hreq *hreq, void *data) +{ + struct afb_diralias *da = data; + + if (hreq->method != afb_method_get) { + afb_hreq_reply_error(hreq, MHD_HTTP_METHOD_NOT_ALLOWED); + return 1; + } + + if (!afb_hreq_valid_tail(hreq)) { + afb_hreq_reply_error(hreq, MHD_HTTP_FORBIDDEN); + return 1; + } + + return afb_hreq_reply_file(hreq, da->dirfd, &hreq->tail[1]); +} + +int afb_hsrv_add_alias(AFB_session * session, const char *prefix, const char *alias, int priority) +{ + struct afb_diralias *da; + int dirfd; + + dirfd = open(alias, O_PATH|O_DIRECTORY); + if (dirfd < 0) { + /* TODO message */ + return 0; + } + da = malloc(sizeof *da); + if (da != NULL) { + da->alias = prefix; + da->directory = alias; + da->lendir = strlen(da->directory); + da->dirfd = dirfd; + if (afb_hsrv_add_handler(session, prefix, handle_alias, da, priority)) + return 1; + free(da); + } + close(dirfd); + return 0; +} + +void afb_hsrv_reply_error(struct MHD_Connection *connection, unsigned int status) +{ + char *buffer; + int length; + struct MHD_Response *response; + + length = asprintf(&buffer, "error %u", status); + if (length > 0) + response = MHD_create_response_from_buffer((unsigned)length, buffer, MHD_RESPMEM_MUST_FREE); + else { + buffer = "error"; + response = MHD_create_response_from_buffer(strlen(buffer), buffer, MHD_RESPMEM_PERSISTENT); + } + if (!MHD_queue_response(connection, status, response)) + fprintf(stderr, "Failed to reply error code %u", status); + MHD_destroy_response(response); +} + +static int postproc(void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) +{ + struct afb_hreq *hreq = cls; + if (filename != NULL) + return afb_hreq_post_add_file(hreq, key, filename, data, size); + else + return afb_hreq_post_add(hreq, key, data, size); +} + +static int access_handler( + void *cls, + struct MHD_Connection *connection, + const char *url, + const char *methodstr, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **recordreq) +{ + int rc; + struct afb_hreq *hreq; + enum afb_method method; + AFB_session *session; + struct afb_hsrv_handler *iter; + const char *type; + + session = cls; + hreq = *recordreq; + if (hreq == NULL) { + /* create the request */ + hreq = calloc(1, sizeof *hreq); + if (hreq == NULL) + goto internal_error; + *recordreq = hreq; + + /* get the method */ + method = get_method(methodstr); + method &= afb_method_get | afb_method_post; + if (method == afb_method_none) + goto bad_request; + + /* init the request */ + hreq->session = cls; + hreq->connection = connection; + hreq->method = method; + hreq->version = version; + hreq->tail = hreq->url = url; + hreq->lentail = hreq->lenurl = strlen(url); + + /* init the post processing */ + if (method == afb_method_post) { + type = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONTENT_TYPE); + if (type == NULL) { + /* an empty post, let's process it as a get */ + hreq->method = afb_method_get; + } else if (strcasestr(type, FORM_CONTENT) != NULL) { + hreq->postform = MHD_create_post_processor (connection, 65500, postproc, hreq); + if (hreq->postform == NULL) + goto internal_error; + } else if (strcasestr(type, JSON_CONTENT) == NULL) { + afb_hsrv_reply_error(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE); + return MHD_YES; + } + } + } + + /* process further data */ + if (*upload_data_size) { + if (hreq->postform != NULL) { + if (!MHD_post_process (hreq->postform, upload_data, *upload_data_size)) + goto internal_error; + } else { + if (!afb_hreq_post_add(hreq, NULL, upload_data, *upload_data_size)) + goto internal_error; + } + *upload_data_size = 0; + return MHD_YES; + } + + /* flush the data */ + afb_hreq_post_end(hreq); + if (hreq->postform != NULL) { + rc = MHD_destroy_post_processor(hreq->postform); + hreq->postform = NULL; + if (rc == MHD_NO) + goto bad_request; + } + + /* search an handler for the request */ + iter = session->handlers; + while (iter) { + if (afb_hreq_unprefix(hreq, iter->prefix, iter->length)) { + if (iter->handler(hreq, iter->data)) + return MHD_YES; + hreq->tail = hreq->url; + hreq->lentail = hreq->lenurl; + } + iter = iter->next; + } + + /* no handler */ + afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND); + return MHD_YES; + +bad_request: + afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST); + return MHD_YES; + +internal_error: + afb_hsrv_reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR); + return MHD_YES; +} + +/* Because of POST call multiple time requestApi we need to free POST handle here */ +static void end_handler(void *cls, struct MHD_Connection *connection, void **recordreq, + enum MHD_RequestTerminationCode toe) +{ + struct afb_hreq *hreq; + + hreq = *recordreq; + + afb_hreq_free(hreq); +} + +static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t addrlen) +{ + 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 (verbosity) + 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; + + if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_websocket_switch, NULL, 20)) + return 0; + + if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_rest_api, NULL, 10)) + return 0; + + for (idx = 0; session->config->aliasdir[idx].url != NULL; idx++) + if (!afb_hsrv_add_alias (session, session->config->aliasdir[idx].url, session->config->aliasdir[idx].path, 0)) + return 0; + + if (!afb_hsrv_add_alias(session, "", session->config->rootdir, -10)) + return 0; + + if (!afb_hsrv_add_handler(session, session->config->rootbase, afb_hreq_one_page_api_redirect, NULL, -20)) + 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 + + return 1; +} + +/* 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 0; + } + + 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); + } + + 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 */ + access_handler, session, /* Http Request Call back + extra attribute */ + MHD_OPTION_NOTIFY_COMPLETED, end_handler, session, + MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)15, /* 15 seconds */ + MHD_OPTION_END); /* options-end */ + + if (httpd == NULL) { + printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort); + return 0; + } + + info = MHD_get_daemon_info(httpd, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY); + if (info == NULL) { + MHD_stop_daemon(httpd); + fprintf(stderr, "Error: httpStart no pollfd"); + return 0; + } + + 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); + + session->httpd = httpd; + return 1; +} + +void afb_hsrv_stop(AFB_session * session) +{ + if (upoll) + upoll_close(upoll); + upoll = NULL; + if (session->httpd != NULL) + MHD_stop_daemon(session->httpd); + session->httpd = NULL; +} + diff --git a/src/http-svc.c b/src/http-svc.c deleted file mode 100644 index b36d5f7e..00000000 --- a/src/http-svc.c +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright 2016 IoT.bzh - * Author: José Bollo - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include - -#include - -#include "local-def.h" -#include "afb-method.h" -#include "afb-hreq.h" -#include "afb-websock.h" -#include "afb-apis.h" -#include "afb-req-itf.h" -#include "verbose.h" - -#define JSON_CONTENT "application/json" -#define FORM_CONTENT MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA - - -struct afb_hsrv_handler { - struct afb_hsrv_handler *next; - const char *prefix; - size_t length; - int (*handler) (struct afb_hreq *, void *); - void *data; - int priority; -}; - -struct afb_diralias { - const char *alias; - const char *directory; - size_t lendir; - 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 afb_hsrv_handler *new_handler( - struct afb_hsrv_handler *head, - const char *prefix, - int (*handler) (struct afb_hreq *, void *), - void *data, - int priority) -{ - struct afb_hsrv_handler *link, *iter, *previous; - size_t length; - - /* get the length of the prefix without its leading / */ - length = strlen(prefix); - while (length && prefix[length - 1] == '/') - length--; - - /* allocates the new link */ - link = malloc(sizeof *link); - if (link == NULL) - return NULL; - - /* initialize it */ - link->prefix = prefix; - link->length = length; - link->handler = handler; - link->data = data; - link->priority = priority; - - /* adds it */ - previous = NULL; - iter = head; - while (iter && (priority < iter->priority || (priority == iter->priority && length <= iter->length))) { - previous = iter; - iter = iter->next; - } - link->next = iter; - if (previous == NULL) - return link; - previous->next = link; - return head; -} - -int afb_hsrv_add_handler( - AFB_session * session, - const char *prefix, - int (*handler) (struct afb_hreq *, void *), - void *data, - int priority) -{ - struct afb_hsrv_handler *head; - - head = new_handler(session->handlers, prefix, handler, data, priority); - if (head == NULL) - return 0; - session->handlers = head; - return 1; -} - -static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) -{ - int later; - - afb_hreq_context(hreq); - if (hreq->lentail != 0 || !afb_websock_check(hreq, &later)) - return 0; - - if (!later) { - struct afb_websock *ws = afb_websock_create(hreq->connection); - if (ws == NULL) { - /* TODO */ - } else { - /* TODO */ - } - } - return 1; -} - -static int afb_hreq_rest_api(struct afb_hreq *hreq, void *data) -{ - const char *api, *verb; - size_t lenapi, lenverb; - struct AFB_clientCtx *context; - - api = &hreq->tail[strspn(hreq->tail, "/")]; - lenapi = strcspn(api, "/"); - verb = &api[lenapi]; - verb = &verb[strspn(verb, "/")]; - lenverb = strcspn(verb, "/"); - - if (!(*api && *verb && lenapi && lenverb)) - return 0; - - context = afb_hreq_context(hreq); - return afb_apis_handle(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb); -} - -static int handle_alias(struct afb_hreq *hreq, void *data) -{ - struct afb_diralias *da = data; - - if (hreq->method != afb_method_get) { - afb_hreq_reply_error(hreq, MHD_HTTP_METHOD_NOT_ALLOWED); - return 1; - } - - if (!afb_hreq_valid_tail(hreq)) { - afb_hreq_reply_error(hreq, MHD_HTTP_FORBIDDEN); - return 1; - } - - return afb_hreq_reply_file(hreq, da->dirfd, &hreq->tail[1]); -} - -int afb_hsrv_add_alias(AFB_session * session, const char *prefix, const char *alias, int priority) -{ - struct afb_diralias *da; - int dirfd; - - dirfd = open(alias, O_PATH|O_DIRECTORY); - if (dirfd < 0) { - /* TODO message */ - return 0; - } - da = malloc(sizeof *da); - if (da != NULL) { - da->alias = prefix; - da->directory = alias; - da->lendir = strlen(da->directory); - da->dirfd = dirfd; - if (afb_hsrv_add_handler(session, prefix, handle_alias, da, priority)) - return 1; - free(da); - } - close(dirfd); - return 0; -} - -void afb_hsrv_reply_error(struct MHD_Connection *connection, unsigned int status) -{ - char *buffer; - int length; - struct MHD_Response *response; - - length = asprintf(&buffer, "error %u", status); - if (length > 0) - response = MHD_create_response_from_buffer((unsigned)length, buffer, MHD_RESPMEM_MUST_FREE); - else { - buffer = "error"; - response = MHD_create_response_from_buffer(strlen(buffer), buffer, MHD_RESPMEM_PERSISTENT); - } - if (!MHD_queue_response(connection, status, response)) - fprintf(stderr, "Failed to reply error code %u", status); - MHD_destroy_response(response); -} - -static int postproc(void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *data, - uint64_t off, - size_t size) -{ - struct afb_hreq *hreq = cls; - if (filename != NULL) - return afb_hreq_post_add_file(hreq, key, filename, data, size); - else - return afb_hreq_post_add(hreq, key, data, size); -} - -static int access_handler( - void *cls, - struct MHD_Connection *connection, - const char *url, - const char *methodstr, - const char *version, - const char *upload_data, - size_t *upload_data_size, - void **recordreq) -{ - int rc; - struct afb_hreq *hreq; - enum afb_method method; - AFB_session *session; - struct afb_hsrv_handler *iter; - const char *type; - - session = cls; - hreq = *recordreq; - if (hreq == NULL) { - /* create the request */ - hreq = calloc(1, sizeof *hreq); - if (hreq == NULL) - goto internal_error; - *recordreq = hreq; - - /* get the method */ - method = get_method(methodstr); - method &= afb_method_get | afb_method_post; - if (method == afb_method_none) - goto bad_request; - - /* init the request */ - hreq->session = cls; - hreq->connection = connection; - hreq->method = method; - hreq->version = version; - hreq->tail = hreq->url = url; - hreq->lentail = hreq->lenurl = strlen(url); - - /* init the post processing */ - if (method == afb_method_post) { - type = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONTENT_TYPE); - if (type == NULL) { - /* an empty post, let's process it as a get */ - hreq->method = afb_method_get; - } else if (strcasestr(type, FORM_CONTENT) != NULL) { - hreq->postform = MHD_create_post_processor (connection, 65500, postproc, hreq); - if (hreq->postform == NULL) - goto internal_error; - } else if (strcasestr(type, JSON_CONTENT) == NULL) { - afb_hsrv_reply_error(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE); - return MHD_YES; - } - } - } - - /* process further data */ - if (*upload_data_size) { - if (hreq->postform != NULL) { - if (!MHD_post_process (hreq->postform, upload_data, *upload_data_size)) - goto internal_error; - } else { - if (!afb_hreq_post_add(hreq, NULL, upload_data, *upload_data_size)) - goto internal_error; - } - *upload_data_size = 0; - return MHD_YES; - } - - /* flush the data */ - afb_hreq_post_end(hreq); - if (hreq->postform != NULL) { - rc = MHD_destroy_post_processor(hreq->postform); - hreq->postform = NULL; - if (rc == MHD_NO) - goto bad_request; - } - - /* search an handler for the request */ - iter = session->handlers; - while (iter) { - if (afb_hreq_unprefix(hreq, iter->prefix, iter->length)) { - if (iter->handler(hreq, iter->data)) - return MHD_YES; - hreq->tail = hreq->url; - hreq->lentail = hreq->lenurl; - } - iter = iter->next; - } - - /* no handler */ - afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND); - return MHD_YES; - -bad_request: - afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST); - return MHD_YES; - -internal_error: - afb_hsrv_reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR); - return MHD_YES; -} - -/* Because of POST call multiple time requestApi we need to free POST handle here */ -static void end_handler(void *cls, struct MHD_Connection *connection, void **recordreq, - enum MHD_RequestTerminationCode toe) -{ - struct afb_hreq *hreq; - - hreq = *recordreq; - - afb_hreq_free(hreq); -} - -static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t addrlen) -{ - 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 (verbosity) - 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; - - if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_websocket_switch, NULL, 20)) - return 0; - - if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_rest_api, NULL, 10)) - return 0; - - for (idx = 0; session->config->aliasdir[idx].url != NULL; idx++) - if (!afb_hsrv_add_alias (session, session->config->aliasdir[idx].url, session->config->aliasdir[idx].path, 0)) - return 0; - - if (!afb_hsrv_add_alias(session, "", session->config->rootdir, -10)) - return 0; - - if (!afb_hsrv_add_handler(session, session->config->rootbase, afb_hreq_one_page_api_redirect, NULL, -20)) - return 0; - - return 1; -} - -AFB_error httpdStart(AFB_session * session) -{ - if (!my_default_init(session)) { - printf("Error: initialisation of httpd failed"); - return AFB_FATAL; - } - -#if defined(USE_MAGIC_MIME_TYPE) - /*TBD open libmagic cache [fail to pass EFENCE check (allocating 0 bytes)] */ - init_lib_magic (session); -#endif - - 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( - 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 */ - access_handler, session, /* Http Request Call back + extra attribute */ - MHD_OPTION_NOTIFY_COMPLETED, end_handler, session, - MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)15, /* 15 seconds */ - MHD_OPTION_END); /* options-end */ - - if (session->httpd == NULL) { - printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort); - return AFB_FATAL; - } - 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); - if (info == NULL) { - printf("Error: httpLoop no pollfd"); - goto error; - } - pfd.fd = info->listen_fd; - pfd.events = POLLIN; - - if (verbosity) - fprintf(stderr, "AFB:notice entering httpd waiting loop\n"); - while (TRUE) { - if (verbosity) - fprintf(stderr, "AFB:notice httpd alive [%d]\n", count++); - poll(&pfd, 1, 15000); /* 15 seconds (as above timeout when starting) */ - MHD_run(session->httpd); - } - - error: - /* should never return from here */ - return AFB_FATAL; -} - -int httpdStatus(AFB_session * session) -{ - return MHD_run(session->httpd); -} - -void httpdStop(AFB_session * session) -{ - MHD_stop_daemon(session->httpd); -} diff --git a/src/local-def.h b/src/local-def.h index 3298322e..c21918cd 100644 --- a/src/local-def.h +++ b/src/local-def.h @@ -40,22 +40,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" - -// 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 @@ -63,11 +47,6 @@ typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY - - - - - enum AFB_Mode; @@ -133,6 +112,4 @@ struct AFB_session typedef struct AFB_config AFB_config; typedef struct AFB_session AFB_session; -#include "proto-def.h" - #endif /* LOCAL_DEF_H */ diff --git a/src/main.c b/src/main.c index 15f84b18..41296e2c 100644 --- a/src/main.c +++ b/src/main.c @@ -32,8 +32,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" @@ -126,24 +128,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"); @@ -157,17 +164,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) { @@ -186,8 +190,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; } @@ -406,20 +408,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); } @@ -439,6 +432,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() @@ -453,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 @@ -530,6 +503,7 @@ static void daemonize(AFB_session *session) +--------------------------------------------------------- */ int main(int argc, char *argv[]) { + int rc; AFB_session *session; // open syslog if ever needed @@ -560,7 +534,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) { @@ -568,7 +543,6 @@ int main(int argc, char *argv[]) { return 1; } - // let's run this program with a low priority nice (20); @@ -576,13 +550,6 @@ 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 (verbosity) fprintf (stderr, "AFB: notice Init config done\n"); // ---- run in foreground mode -------------------- @@ -606,10 +573,26 @@ int main(int argc, char *argv[]) { } // end background-foreground - listenLoop(session); - if (verbosity) 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 b549a62e..00000000 --- a/src/proto-def.h +++ /dev/null @@ -1,30 +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. - -*/ - - -// 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/utils-upoll.c b/src/utils-upoll.c index af4a6161..eb0e9674 100644 --- a/src/utils-upoll.c +++ b/src/utils-upoll.c @@ -28,7 +28,9 @@ struct upoll { int fd; - void (*process)(void *closure, int fd, uint32_t events); + void (*read)(void *); + void (*write)(void *); + void (*hangup)(void *); void *closure; struct upoll *next; }; @@ -48,7 +50,7 @@ int upoll_is_valid(struct upoll *upoll) return 0; } -struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure) +struct upoll *upoll_open(int fd, void *closure) { struct epoll_event e; struct upoll *result; @@ -68,13 +70,12 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, } /* allocates */ - result = malloc(sizeof *result); + result = calloc(1, sizeof *result); if (result == NULL) return NULL; /* init */ result->fd = fd; - result->process = process; result->closure = closure; pthread_mutex_lock(&mutex); result->next = head; @@ -82,7 +83,7 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, pthread_mutex_unlock(&mutex); /* records */ - e.events = events; + e.events = 0; e.data.ptr = result; rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e); if (rc == 0) @@ -95,16 +96,39 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, return NULL; } -int upoll_update(struct upoll *upoll, uint32_t events) +static int update(struct upoll *upoll) { struct epoll_event e; + e.events = (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)); - e.events = events; - e.data.ptr = upoll; - return epoll_ctl(pollfd, EPOLL_CTL_MOD, upoll->fd, &e); + 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) @@ -136,7 +160,12 @@ void upoll_wait(int timeout) rc = epoll_wait(pollfd, &e, 1, timeout); if (rc == 1) { upoll = e.data.ptr; - upoll->process(upoll->closure, upoll->fd, e.events); + 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 index 24aaf41a..705fbc36 100644 --- a/src/utils-upoll.h +++ b/src/utils-upoll.h @@ -19,9 +19,12 @@ struct upoll; extern int upoll_is_valid(struct upoll *upoll); -extern struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure); +extern struct upoll *upoll_open(int fd, void *closure); -extern int upoll_update(struct upoll *upoll, uint32_t events); +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); diff --git a/src/verbose.h b/src/verbose.h index 7b32a663..09254b22 100644 --- a/src/verbose.h +++ b/src/verbose.h @@ -1,5 +1,5 @@ /* - Copyright 2015 IoT.bzh + Copyright 2016 IoT.bzh author: José Bollo -- cgit 1.2.3-korg