From 0efeda172772008327416ec5b1846ae7f7cc7d85 Mon Sep 17 00:00:00 2001 From: Adam Sutton Date: Sat, 10 Aug 2013 14:08:36 +0100 Subject: [PATCH] api: started work on creating a common API framework The idea is that this will become a common API framework for both HTTP and HTSP. And anything else we might think off. The only real constraint at the moment is both assume a JSON like input format (for simplicity) since they currently have that in common. --- Makefile | 4 ++ src/api.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ src/api.h | 58 ++++++++++++++++++++++++ src/main.c | 3 ++ src/webui/api.c | 83 ++++++++++++++++++++++++++++++++++ src/webui/webui.c | 1 + src/webui/webui.h | 2 + 7 files changed, 261 insertions(+) create mode 100644 src/api.c create mode 100644 src/api.h create mode 100644 src/webui/api.c diff --git a/Makefile b/Makefile index 850d0f54..15d4bd0e 100644 --- a/Makefile +++ b/Makefile @@ -109,6 +109,9 @@ SRCS = src/version.c \ src/descrambler/descrambler.c \ src/service_mapper.c \ +SRCS += \ + src/api.c \ + SRCS += \ src/parsers/parsers.c \ src/parsers/bitstream.c \ @@ -136,6 +139,7 @@ SRCS += src/webui/webui.c \ src/webui/simpleui.c \ src/webui/statedump.c \ src/webui/html.c\ + src/webui/api.c\ SRCS += src/muxer.c \ src/muxer/muxer_pass.c \ diff --git a/src/api.c b/src/api.c new file mode 100644 index 00000000..79ad6686 --- /dev/null +++ b/src/api.c @@ -0,0 +1,110 @@ +/* + * API - Common functions for control/query API + * + * Copyright (C) 2013 Adam Sutton + * + * 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 . + */ + +#include "tvheadend.h" +#include "api.h" +#include "access.h" + +#include + +typedef struct api_link { + const api_hook_t *hook; + RB_ENTRY(api_link) link; +} api_link_t; + +RB_HEAD(,api_link) api_hook_tree; + +static int ah_cmp + ( api_link_t *a, api_link_t *b ) +{ + return strcmp(a->hook->ah_subsystem, b->hook->ah_subsystem); +} + +void +api_register ( const api_hook_t *hooks ) +{ + static api_link_t *t, *skel = NULL; + while (hooks->ah_subsystem) { + if (!skel) + skel = calloc(1, sizeof(api_link_t)); + skel->hook = hooks; + t = RB_INSERT_SORTED(&api_hook_tree, skel, link, ah_cmp); + if (t) + tvherror("api", "trying to re-register subsystem"); + else + skel = NULL; + hooks++; + } +} + +int +api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp ) +{ + api_hook_t h; + api_link_t *ah, skel; + const char *op; + + /* Args and response must be set */ + if (!args || !resp) + return EINVAL; + + // Note: there is no locking while checking the hook tree, its assumed + // this is all setup during init (if this changes the code will + // need updating) + h.ah_subsystem = subsystem; + skel.hook = &h; + ah = RB_FIND(&api_hook_tree, &skel, link, ah_cmp); + + if (!ah) { + tvhwarn("api", "failed to find subsystem [%s]", subsystem); + return ENOSYS; // TODO: is this really the right error code? + } + + /* Extract method */ + op = htsmsg_get_str(args, "method"); + if (!op) + op = htsmsg_get_str(args, "op"); + // Note: this is not required (so no final validation) + + /* Execute */ + return ah->hook->ah_callback(ah->hook->ah_opaque, op, args, resp); +} + +static int +api_serverinfo + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ) +{ + *resp = htsmsg_create_map(); + htsmsg_add_str(*resp, "sw_version", tvheadend_version); + htsmsg_add_u32(*resp, "api_version", TVH_API_VERSION); + htsmsg_add_str(*resp, "name", "Tvheadend"); + if (tvheadend_webroot) + htsmsg_add_str(*resp, "webroot", tvheadend_webroot); + htsmsg_add_msg(*resp, "capabilities", tvheadend_capabilities_list(1)); + return 0; +} + +void api_init ( void ) +{ + static api_hook_t h[] = { + { "serverinfo", ACCESS_ANONYMOUS, api_serverinfo, NULL }, + { NULL, 0, NULL, NULL } + }; + api_register(h); +} diff --git a/src/api.h b/src/api.h new file mode 100644 index 00000000..3943b1e0 --- /dev/null +++ b/src/api.h @@ -0,0 +1,58 @@ +/* + * API - Common functions for control/query API + * + * Copyright (C) 2013 Adam Sutton + * + * 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 . + */ + +#ifndef __TVH_API_H__ +#define __TVH_API_H__ + +#include "htsmsg.h" +#include "redblack.h" + +#define TVH_API_VERSION 12 + +/* + * Command hook + */ + +typedef int (*api_callback_t) + ( void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp ); + +typedef struct api_hook +{ + const char *ah_subsystem; + int ah_access; + api_callback_t ah_callback; + void *ah_opaque; +} api_hook_t; + +/* + * Regsiter handler + */ +void api_register ( const api_hook_t *hooks ); + +/* + * Execute + */ +int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp ); + +/* + * Initialise + */ +void api_init ( void ); + +#endif /* __TVH_API_H__ */ diff --git a/src/main.c b/src/main.c index 09e51f84..c87830bf 100644 --- a/src/main.c +++ b/src/main.c @@ -37,6 +37,7 @@ #include #include "tvheadend.h" +#include "api.h" #include "tcp.h" #include "access.h" #include "http.h" @@ -687,6 +688,8 @@ main(int argc, char **argv) /** * Initialize subsystems */ + + api_init(); #if ENABLE_LIBAV libav_init(); diff --git a/src/webui/api.c b/src/webui/api.c new file mode 100644 index 00000000..504e2e3d --- /dev/null +++ b/src/webui/api.c @@ -0,0 +1,83 @@ +/* + * tvheadend, WebAPI access point + * + * Copyright (C) 2013 Adam Sutton + * + * 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 . + */ + +#include "tvheadend.h" +#include "webui.h" +#include "access.h" +#include "http.h" +#include "api.h" +#include "htsmsg.h" +#include "htsmsg_json.h" + +static int +webui_api_handler + ( http_connection_t *hc, const char *remain, void *opaque ) +{ + int r; + htsmsg_t *args, *resp; + const char *a = http_arg_get(&hc->hc_req_args, "args"); + const char *op = http_arg_get(&hc->hc_req_args, "method"); + + // Compat + if (!op) + op = http_arg_get(&hc->hc_req_args, "op"); + + + /* Parse arguments */ + if (a) + args = htsmsg_json_deserialize(a); + else + args = htsmsg_create_map(); + if (!args) + return HTTP_STATUS_BAD_REQUEST; + + /* Add operation */ + if (!htsmsg_get_str(args, "method") && op) + htsmsg_add_str(args, "method", op); + + /* Call */ + r = api_exec(remain, args, &resp); + + /* Convert error */ + if (r) { + switch (r) { + case EACCES: + r = HTTP_STATUS_UNAUTHORIZED; + case ENOENT: + case ENOSYS: + r = HTTP_STATUS_NOT_FOUND; + default: + r = HTTP_STATUS_BAD_REQUEST; + } + + /* Output response */ + } else { + htsmsg_json_serialize(resp, &hc->hc_reply, 0); + http_output_content(hc, "text/x-json; charset=UTF-8"); + htsmsg_destroy(resp); + } + + return r; +} + +void +webui_api_init ( void ) +{ + http_path_add("/api", NULL, webui_api_handler, ACCESS_WEB_INTERFACE); +} diff --git a/src/webui/webui.c b/src/webui/webui.c index faae50ba..31248e81 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -974,5 +974,6 @@ webui_init(void) simpleui_start(); extjs_start(); comet_init(); + webui_api_init(); } diff --git a/src/webui/webui.h b/src/webui/webui.h index bf4efcda..409f745e 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -46,6 +46,8 @@ void extjs_service_update(htsmsg_t *in); void extjs_service_delete(htsmsg_t *in); +void webui_api_init ( void ); + /** *