/*
- * Copyright (C) 2015-2018 "IoT.bzh"
+ * Copyright (C) 2015-2019 "IoT.bzh"
* Author "Fulup Ar Foll"
* Author José Bollo <jose.bollo@iot.bzh>
*
#include <systemd/sd-daemon.h>
-#include "afb-config.h"
+#include "afb-args.h"
#include "afb-hswitch.h"
#include "afb-apiset.h"
#include "afb-autoset.h"
#include "afb-api-so.h"
-#if defined(WITH_DBUS_TRANSPARENCY)
+#if WITH_DBUS_TRANSPARENCY
# include "afb-api-dbus.h"
#endif
#include "afb-api-ws.h"
#include "afb-common.h"
#include "afb-export.h"
#include "afb-monitor.h"
+#if WITH_AFB_HOOK
#include "afb-hook.h"
#include "afb-hook-flags.h"
+#endif
#include "afb-debug.h"
-#if defined(WITH_SUPERVISION)
+#if WITH_SUPERVISION
# include "afb-supervision.h"
#endif
#include "wrap-json.h"
#include "jobs.h"
#include "sig-monitor.h"
+#include "watchdog.h"
+
+#if !defined(DEFAULT_BINDER_INTERFACE)
+# define DEFAULT_BINDER_INTERFACE NULL
+#endif
/*
if SELF_PGROUP == 0 the launched command is the group leader
return p ? addenv(name, p) : -1;
}
+/**
+ * Tiny helper around addenv that export an integer
+ *
+ * @param name name of the variable to set
+ * @param value the integer value to export
+ *
+ * @return 0 in case of success or -1 in case of error (with errno set to ENOMEM)
+ */
+static int addenv_int(const char *name, int value)
+{
+ char buffer[64];
+ snprintf(buffer, sizeof buffer, "%d", value);
+ return addenv(name, buffer);
+}
+
/*----------------------------------------------------------
| helpers for handling list of arguments
+--------------------------------------------------------- */
*path++ = 0;
INFO("Alias for url=%s to path=%s", spec, path);
return afb_hsrv_add_alias(hsrv, spec, afb_common_rootdir_get_fd(), path,
- 0, 0);
+ 0, 1);
}
static int init_http_server(struct afb_hsrv *hsrv)
return 1;
}
+static int add_interface(void *closure, const char *value)
+{
+ struct afb_hsrv *hsrv = closure;
+ int rc;
+
+ rc = afb_hsrv_add_interface(hsrv, value);
+ return rc > 0;
+}
+
static struct afb_hsrv *start_http_server()
{
int rc;
- const char *uploaddir, *rootdir;
+ const char *uploaddir, *rootdir, *errs;
struct afb_hsrv *hsrv;
int cache_timeout, http_port;
+ struct json_object *junk;
- rc = wrap_json_unpack(main_config, "{ss ss si si}",
+ http_port = -1;
+ rc = wrap_json_unpack(main_config, "{ss ss si s?i}",
"uploaddir", &uploaddir,
"rootdir", &rootdir,
"cache-eol", &cache_timeout,
}
if (afb_hreq_init_download_path(uploaddir)) {
- ERROR("unable to set the upload directory %s", uploaddir);
- return NULL;
+ static const char fallback_uploaddir[] = "/tmp";
+ WARNING("unable to set the upload directory %s", uploaddir);
+ if (afb_hreq_init_download_path(fallback_uploaddir)) {
+ ERROR("unable to fallback to upload directory %s", fallback_uploaddir);
+ return NULL;
+ }
+ uploaddir = fallback_uploaddir;
}
hsrv = afb_hsrv_create();
return NULL;
}
- NOTICE("Waiting port=%d rootdir=%s", http_port, rootdir);
- NOTICE("Browser URL= http://localhost:%d", http_port);
-
- rc = afb_hsrv_start(hsrv, (uint16_t) http_port, 15);
+ rc = afb_hsrv_start(hsrv, 15);
if (!rc) {
ERROR("starting of httpd failed");
afb_hsrv_put(hsrv);
return NULL;
}
+ NOTICE("Serving rootdir=%s uploaddir=%s", rootdir, uploaddir);
+
+ /* check if port is set */
+ if (http_port < 0) {
+ /* not set, check existing interfaces */
+ if (!json_object_object_get_ex(main_config, "interface", &junk)) {
+ ERROR("No port and no interface ");
+ }
+ } else {
+ rc = afb_hsrv_add_interface_tcp(hsrv, DEFAULT_BINDER_INTERFACE, (uint16_t) http_port);
+ if (!rc) {
+ ERROR("setting interface failed");
+ afb_hsrv_put(hsrv);
+ return NULL;
+ }
+ NOTICE("Browser URL= http://localhost:%d", http_port);
+ }
+
+ errs = run_for_config_array_opt("interface", add_interface, hsrv);
+ if (errs) {
+ ERROR("setting interface %s failed", errs);
+ afb_hsrv_put(hsrv);
+ return NULL;
+ }
+
return hsrv;
}
| execute_command
+--------------------------------------------------------- */
+static void exit_at_end()
+{
+ exit(0);
+}
+
+static void wait_child(int signum, void* arg)
+{
+ pid_t pid = (pid_t)(intptr_t)arg;
+ pid_t pidchld = childpid;
+
+ if (pidchld == pid) {
+ childpid = 0;
+ if (!SELF_PGROUP)
+ killpg(pidchld, SIGKILL);
+ waitpid(pidchld, NULL, 0);
+ jobs_exit(exit_at_end);
+ } else {
+ waitpid(pid, NULL, 0);
+ }
+}
+
static void on_sigchld(int signum, siginfo_t *info, void *uctx)
{
if (info->si_pid == childpid) {
case CLD_EXITED:
case CLD_KILLED:
case CLD_DUMPED:
- childpid = 0;
- if (!SELF_PGROUP)
- killpg(info->si_pid, SIGKILL);
- waitpid(info->si_pid, NULL, 0);
- exit(0);
+ jobs_queue_lazy(0, 0, wait_child, (void*)(intptr_t)info->si_pid);
+ default:
+ break;
}
}
}
#define SUBST_CHAR '@'
#define SUBST_STR "@"
-static char *instanciate_string(const char *arg, const char *port, const char *token)
+static char *instanciate_string(char *arg, const char *port, const char *token)
{
- char *resu, *it, *wr;
+ char *resu, *it, *wr, c;
int chg, dif;
/* get the changes */
dif = 0;
it = strchrnul(arg, SUBST_CHAR);
while (*it) {
- switch(*++it) {
- case 'p': chg++; dif += (int)strlen(port) - 2; break;
- case 't': chg++; dif += (int)strlen(token) - 2; break;
- case SUBST_CHAR: it++; chg++; dif--; break;
- default: break;
+ c = *++it;
+ if (c == 'p' && port) {
+ chg++;
+ dif += (int)strlen(port) - 2;
+ } else if (c == 't' && token) {
+ chg++;
+ dif += (int)strlen(token) - 2;
+ } else if (c == SUBST_CHAR) {
+ it++;
+ chg++;
+ dif--;
}
it = strchrnul(it, SUBST_CHAR);
}
/* return arg when no change */
if (!chg)
- return strdup(arg);
+ return arg;
/* allocates the result */
resu = malloc((it - arg) + dif + 1);
wr = mempcpy(wr, arg, it - arg);
if (!*it)
break;
- switch(*++it) {
- case 'p': wr = stpcpy(wr, port); break;
- case 't': wr = stpcpy(wr, token); break;
- default: *wr++ = SUBST_CHAR; /*@fallthrough@*/
- case SUBST_CHAR: *wr++ = *it;
+ c = *++it;
+ if (c == 'p' && port)
+ wr = stpcpy(wr, port);
+ else if (c == 't' && token)
+ wr = stpcpy(wr, token);
+ else {
+ if (c != SUBST_CHAR)
+ *wr++ = SUBST_CHAR;
+ *wr++ = *it;
}
arg = ++it;
}
static char **instanciate_command_args(struct json_object *exec, const char *port, const char *token)
{
char **result;
- char *repl;
+ char *repl, *item;
int i, n;
/* allocates the result */
/* instanciate the arguments */
for (i = 0 ; i < n ; i++) {
- repl = instanciate_string(json_object_get_string(json_object_array_get_idx(exec, i)), port, token);
+ item = (char*)json_object_get_string(json_object_array_get_idx(exec, i));
+ repl = instanciate_string(item, port, token);
if (!repl) {
- while(i)
- free(result[--i]);
free(result);
return NULL;
}
static int execute_command()
{
- struct json_object *exec, *oport;
+ struct json_object *exec, *oport, *otok;
struct sigaction siga;
- char port[20];
- const char *token;
+ const char *token, *port;
char **args;
- int rc;
/* check whether a command is to execute or not */
if (!json_object_object_get_ex(main_config, "exec", &exec))
/* compute the string for port */
if (json_object_object_get_ex(main_config, "port", &oport))
- rc = snprintf(port, sizeof port, "%s", json_object_get_string(oport));
+ port = json_object_get_string(oport);
else
- rc = snprintf(port, sizeof port, "%cp", SUBST_CHAR);
- if (rc < 0 || rc >= (int)(sizeof port)) {
- ERROR("port->txt failed");
- }
- else {
- /* instanciate arguments and environment */
- token = afb_session_initial_token();
- args = instanciate_command_args(exec, port, token);
- if (args && instanciate_environ(port, token) >= 0) {
- /* run */
- if (!SELF_PGROUP)
- setpgid(0, 0);
- execv(args[0], args);
- ERROR("can't launch %s: %m", args[0]);
- }
+ port = 0;
+ /* instantiate arguments and environment */
+ if (json_object_object_get_ex(main_config, "token", &otok))
+ token = json_object_get_string(otok);
+ else
+ token = 0;
+ args = instanciate_command_args(exec, port, token);
+ if (args && instanciate_environ(port, token) >= 0) {
+ /* run */
+ if (!SELF_PGROUP)
+ setpgid(0, 0);
+ execv(args[0], args);
+ ERROR("can't launch %s: %m", args[0]);
}
exit(1);
return -1;
json = strchr(verb, ':');
if (json) {
afb_xreq_init(&sreq->xreq, &startup_xreq_itf);
- afb_context_init(&sreq->xreq.context, sreq->session, NULL);
- sreq->xreq.context.validated = 1;
+ afb_context_init_validated(&sreq->xreq.context, sreq->session, NULL, NULL);
sreq->api = strndup(api, verb - api);
sreq->verb = strndup(verb + 1, json - verb - 1);
sreq->xreq.request.called_api = sreq->api;
static void start(int signum, void *arg)
{
- const char *tracereq, *traceapi, *traceevt, *traceses, *tracesvc, *traceditf, *traceglob;
+#if WITH_AFB_HOOK
+ const char *tracereq = NULL, *traceapi = NULL, *traceevt = NULL,
+#if !defined(REMOVE_LEGACY_TRACE)
+ *tracesvc = NULL, *traceditf = NULL,
+#endif
+ *traceses = NULL, *traceglob = NULL;
+#endif
const char *workdir, *rootdir, *token, *rootapi;
struct json_object *settings;
struct afb_hsrv *hsrv;
}
settings = NULL;
- token = rootapi = tracesvc = traceditf = tracereq =
- traceapi = traceevt = traceses = traceglob = NULL;
- no_httpd = http_port = 0;
+ no_httpd = 0;
+ http_port = -1;
+ rootapi = token = NULL;
rc = wrap_json_unpack(main_config, "{"
"ss ss s?s"
"si si si"
"s?b s?i s?s"
"s?o"
-#if !defined(REMOVE_LEGACY_TRACE)
- "s?s s?s"
-#endif
- "s?s s?s s?s s?s s?s"
"}",
"rootdir", &rootdir,
"port", &http_port,
"rootapi", &rootapi,
- "set", &settings,
+ "set", &settings
+ );
+ if (rc < 0) {
+ ERROR("Unable to get start config");
+ exit(1);
+ }
+
+#if WITH_AFB_HOOK
+ rc = wrap_json_unpack(main_config, "{"
+#if !defined(REMOVE_LEGACY_TRACE)
+ "s?s s?s"
+#endif
+ "s?s s?s s?s s?s s?s"
+ "}",
+
#if !defined(REMOVE_LEGACY_TRACE)
"tracesvc", &tracesvc,
"traceditf", &traceditf,
"traceglob", &traceglob
);
if (rc < 0) {
- ERROR("Unable to get start config");
+ ERROR("Unable to get hook config");
exit(1);
}
+#endif
+
+ /* initialize session handling */
+ if (afb_session_init(max_session_count, session_timeout)) {
+ ERROR("initialisation of session manager failed");
+ goto error;
+ }
/* set the directories */
mkdir(workdir, S_IRWXU | S_IRGRP | S_IXGRP);
}
if (addenv_realpath("AFB_WORKDIR", "." /* resolved by realpath */)
|| addenv_realpath("AFB_ROOTDIR", rootdir /* relative to current directory */)) {
- ERROR("can't set environment");
+ ERROR("can't set DIR environment");
goto error;
}
+ /* setup HTTP */
+ if (!no_httpd) {
+ if (http_port == 0) {
+ ERROR("random port is not implemented");
+ goto error;
+ }
+ if ((http_port > 0 && addenv_int("AFB_PORT", http_port))
+ || (token && addenv("AFB_TOKEN", token))) {
+ ERROR("can't set HTTP environment");
+ goto error;
+ }
+ }
+
/* configure the daemon */
afb_export_set_config(settings);
- if (afb_session_init(max_session_count, session_timeout, token)) {
- ERROR("initialisation of session manager failed");
- goto error;
- }
main_apiset = afb_apiset_create("main", api_timeout);
if (!main_apiset) {
ERROR("can't create main api set");
ERROR("failed to setup monitor");
goto error;
}
-#if defined(WITH_SUPERVISION)
+#if WITH_SUPERVISION
if (afb_supervision_init(main_apiset, main_config) < 0) {
ERROR("failed to setup supervision");
goto error;
}
#endif
+#if WITH_AFB_HOOK
/* install hooks */
if (tracereq)
afb_hook_create_xreq(NULL, NULL, NULL, afb_hook_flags_xreq_from_text(tracereq), NULL, NULL);
afb_hook_create_session(NULL, afb_hook_flags_session_from_text(traceses), NULL, NULL);
if (traceglob)
afb_hook_create_global(afb_hook_flags_global_from_text(traceglob), NULL, NULL);
+#endif
/* load bindings and apis */
afb_debug("start-load");
+#if WITH_DYNAMIC_BINDING
apiset_start_list("binding", afb_api_so_add_binding, "the binding");
apiset_start_list("ldpaths", afb_api_so_add_pathset_fails, "the binding path set");
apiset_start_list("weak-ldpaths", afb_api_so_add_pathset_nofails, "the weak binding path set");
+#endif
apiset_start_list("auto-api", afb_autoset_add_any, "the automatic api path set");
-#if defined(WITH_DBUS_TRANSPARENCY)
+#if WITH_DBUS_TRANSPARENCY
apiset_start_list("dbus-client", afb_api_dbus_add_client, "the afb-dbus client");
#endif
apiset_start_list("ws-client", afb_api_ws_add_client_weak, "the afb-websocket client");
/* export started apis */
apiset_start_list("ws-server", afb_api_ws_add_server, "the afb-websocket service");
-#if defined(WITH_DBUS_TRANSPARENCY)
+#if WITH_DBUS_TRANSPARENCY
apiset_start_list("dbus-server", afb_api_dbus_add_server, "the afb-dbus service");
#endif
/* start the HTTP server */
afb_debug("start-http");
if (!no_httpd) {
- if (http_port <= 0) {
- ERROR("no port is defined");
- goto error;
- }
-
if (!afb_hreq_init_cookie(http_port, rootapi, session_timeout)) {
ERROR("initialisation of HTTP cookies failed");
goto error;
/* run the command */
afb_debug("start-exec");
- if (execute_command() < 0)
+ if (execute_command(http_port, token) < 0)
goto error;
/* ready */
sd_notify(1, "READY=1");
+
+ /* activate the watchdog */
+#if HAS_WATCHDOG
+ if (watchdog_activate() < 0)
+ ERROR("can't start the watchdog");
+#endif
+
return;
error:
exit(1);
afb_debug("main-entry");
// ------------- Build session handler & init config -------
- main_config = afb_config_parse_arguments(argc, argv);
+ main_config = afb_args_parse(argc, argv);
if (sig_monitor_init(
!json_object_object_get_ex(main_config, "trap-faults", &obj)
|| json_object_get_boolean(obj)) < 0) {
afb_debug("main-start");
/* enter job processing */
- jobs_start(3, 0, 50, start, NULL);
+ jobs_start(3, 0, 100, start, NULL);
WARNING("hoops returned from jobs_enter! [report bug]");
return 1;
}