X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fmain-afb-daemon.c;h=e9472d50b7e549013c1e2ed069eaf147d396b8bb;hb=65353dce81a629e042800bb7b86fcd869a76727e;hp=d5d2aae254df614db2505a0a22548109e615690d;hpb=5ef271effacb83552f9ea56572c751c2f5a556b6;p=src%2Fapp-framework-binder.git diff --git a/src/main-afb-daemon.c b/src/main-afb-daemon.c index d5d2aae2..e9472d50 100644 --- a/src/main-afb-daemon.c +++ b/src/main-afb-daemon.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2018 "IoT.bzh" + * Copyright (C) 2015-2020 "IoT.bzh" * Author "Fulup Ar Foll" * Author José Bollo * @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -36,31 +38,40 @@ #include -#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-hsrv.h" #include "afb-hreq.h" #include "afb-xreq.h" -#include "jobs.h" #include "afb-session.h" #include "verbose.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 WITH_SUPERVISION +# include "afb-supervision.h" +#endif + #include "process-name.h" #include "wrap-json.h" -#if defined(WITH_SUPERVISION) -# include "afb-supervision.h" +#include "jobs.h" +#include "sig-monitor.h" +#include "watchdog.h" + +#if !defined(DEFAULT_BINDER_INTERFACE) +# define DEFAULT_BINDER_INTERFACE NULL #endif /* @@ -74,6 +85,59 @@ struct json_object *main_config; static pid_t childpid; +/** + * Tiny helper around putenv: add the variable name=value + * + * @param name name of the variable to set + * @param value value to set to the variable + * + * @return 0 in case of success or -1 in case of error (with errno set to ENOMEM) + */ +static int addenv(const char *name, const char *value) +{ + char *head, *middle; + + head = malloc(2 + strlen(name) + strlen(value)); + if (head == NULL) { + errno = ENOMEM; + return -1; + } + middle = stpcpy(head, name); + middle[0] = '='; + strcpy(&middle[1], value); + return putenv(head); +} + +/** + * Tiny helper around addenv that export the real path + * + * @param name name of the variable to set + * @param path the path value to export to the variable + * + * @return 0 in case of success or -1 in case of error (with errno set to ENOMEM) + */ +static int addenv_realpath(const char *name, const char *path) +{ + char buffer[PATH_MAX]; + char *p = realpath(path, buffer); + 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 +--------------------------------------------------------- */ @@ -173,13 +237,14 @@ static void setup_daemon() +--------------------------------------------------------- */ static void daemonize() { - int fd, daemon; + int fd = 0, daemon, nostdin; const char *output; pid_t pid; daemon = 0; output = NULL; wrap_json_unpack(main_config, "{s?b s?s}", "daemon", &daemon, "output", &output); + nostdin = 0; if (output) { fd = open(output, O_WRONLY | O_APPEND | O_CREAT, 0640); @@ -199,6 +264,8 @@ static void daemonize() } if (pid != 0) _exit(0); + + nostdin = 1; } /* closes the input */ @@ -211,8 +278,8 @@ static void daemonize() close(fd); } - /* after that ctrl+C still works */ - close(0); + if (nostdin) + close(0); /* except if 'daemon', ctrl+C still works after that */ } /*--------------------------------------------------------- @@ -231,7 +298,7 @@ static int init_alias(void *closure, const char *spec) *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) @@ -273,14 +340,25 @@ 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, @@ -291,8 +369,13 @@ static struct afb_hsrv *start_http_server() } 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(); @@ -308,16 +391,38 @@ static struct afb_hsrv *start_http_server() 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; } @@ -325,6 +430,27 @@ static struct afb_hsrv *start_http_server() | 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) { @@ -332,11 +458,9 @@ static void on_sigchld(int signum, siginfo_t *info, void *uctx) 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; } } } @@ -350,9 +474,9 @@ static void on_sigchld(int signum, siginfo_t *info, void *uctx) #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 */ @@ -360,18 +484,24 @@ static char *instanciate_string(const char *arg, const char *port, const char *t 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); @@ -387,11 +517,15 @@ static char *instanciate_string(const char *arg, const char *port, const char *t 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; } @@ -419,7 +553,7 @@ static int instanciate_environ(const char *port, const char *token) 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 */ @@ -432,10 +566,9 @@ static char **instanciate_command_args(struct json_object *exec, const char *por /* 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; } @@ -447,12 +580,10 @@ static char **instanciate_command_args(struct json_object *exec, const char *por 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)) @@ -474,23 +605,21 @@ static int execute_command() /* 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; @@ -562,8 +691,7 @@ static void startup_call_current(struct startup_req *sreq) 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; @@ -603,7 +731,13 @@ static void run_startup_calls() 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; @@ -620,18 +754,14 @@ static void start(int signum, void *arg) } 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, @@ -646,7 +776,21 @@ static void start(int signum, void *arg) "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, @@ -658,9 +802,16 @@ static void start(int signum, void *arg) "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); @@ -669,16 +820,30 @@ static void start(int signum, void *arg) goto error; } if (afb_common_rootdir_set(rootdir) < 0) { - ERROR("failed to set common root directory"); + ERROR("failed to set common root directory %s", rootdir); + goto error; + } + if (addenv_realpath("AFB_WORKDIR", "." /* resolved by realpath */) + || addenv_realpath("AFB_ROOTDIR", rootdir /* relative to current directory */)) { + 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"); @@ -688,13 +853,14 @@ static void start(int signum, void *arg) 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); @@ -713,16 +879,17 @@ static void start(int signum, void *arg) 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 */ + /* 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"); - apiset_start_list("ws-server", afb_api_ws_add_server, "the afb-websocket service"); -#if defined(WITH_DBUS_TRANSPARENCY) - apiset_start_list("dbus-server", afb_api_dbus_add_server, "the afb-dbus service"); +#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"); @@ -737,14 +904,15 @@ static void start(int signum, void *arg) if (afb_apiset_start_all_services(main_apiset) < 0) goto error; + /* export started apis */ + apiset_start_list("ws-server", afb_api_ws_add_server, "the afb-websocket service"); +#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; @@ -761,11 +929,18 @@ static void start(int signum, void *arg) /* 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); @@ -778,15 +953,23 @@ error: int main(int argc, char *argv[]) { - struct json_object *name; + struct json_object *obj; afb_debug("main-entry"); // ------------- Build session handler & init config ------- - main_config = afb_config_parse_arguments(argc, argv); - if (json_object_object_get_ex(main_config, "name", &name)) { - verbose_set_name(json_object_get_string(name), 0); - process_name_set_name(json_object_get_string(name)); - process_name_replace_cmdline(argv, json_object_get_string(name)); + 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) { + ERROR("failed to initialise signal handlers"); + return 1; + } + + + if (json_object_object_get_ex(main_config, "name", &obj)) { + verbose_set_name(json_object_get_string(obj), 0); + process_name_set_name(json_object_get_string(obj)); + process_name_replace_cmdline(argv, json_object_get_string(obj)); } afb_debug("main-args"); @@ -800,7 +983,7 @@ int main(int argc, char *argv[]) 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; }