0db84bc1378f86f98a20cafdad004a85e24a3a54
[src/app-framework-binder.git] / src / main-afb-daemon.c
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  * Author José Bollo <jose.bollo@iot.bzh>
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdint.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <limits.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <sys/wait.h>
32
33 #if !defined(NO_CALL_PERSONALITY)
34 #include <sys/personality.h>
35 #endif
36
37 #include <json-c/json.h>
38
39 #include <systemd/sd-daemon.h>
40
41 #include "afb-config.h"
42 #include "afb-hswitch.h"
43 #include "afb-apiset.h"
44 #include "afb-autoset.h"
45 #include "afb-api-so.h"
46 #if defined(WITH_DBUS_TRANSPARENCY)
47 #   include "afb-api-dbus.h"
48 #endif
49 #include "afb-api-ws.h"
50 #include "afb-hsrv.h"
51 #include "afb-hreq.h"
52 #include "afb-xreq.h"
53 #include "jobs.h"
54 #include "afb-session.h"
55 #include "verbose.h"
56 #include "afb-common.h"
57 #include "afb-export.h"
58 #include "afb-monitor.h"
59 #include "afb-hook.h"
60 #include "afb-hook-flags.h"
61 #include "afb-debug.h"
62 #include "process-name.h"
63 #include "wrap-json.h"
64 #if defined(WITH_SUPERVISION)
65 #   include "afb-supervision.h"
66 #endif
67
68 /*
69    if SELF_PGROUP == 0 the launched command is the group leader
70    if SELF_PGROUP != 0 afb-daemon is the group leader
71 */
72 #define SELF_PGROUP 0
73
74 struct afb_apiset *main_apiset;
75 struct json_object *main_config;
76
77 static pid_t childpid;
78
79 /**
80  * Tiny helper around putenv: add the variable name=value
81  *
82  * @param name name of the variable to set
83  * @param value value to set to the variable
84  *
85  * @return 0 in case of success or -1 in case of error (with errno set to ENOMEM)
86  */
87 static int addenv(const char *name, const char *value)
88 {
89         char *head, *middle;
90
91         head = malloc(2 + strlen(name) + strlen(value));
92         if (head == NULL) {
93                 errno = ENOMEM;
94                 return -1;
95         }
96         middle = stpcpy(head, name);
97         middle[0] = '=';
98         strcpy(&middle[1], value);
99         return putenv(head);
100 }
101
102 /**
103  * Tiny helper around addenv that export the real path
104  *
105  * @param name name of the variable to set
106  * @param path the path value to export to the variable
107  *
108  * @return 0 in case of success or -1 in case of error (with errno set to ENOMEM)
109  */
110 static int addenv_realpath(const char *name, const char *path)
111 {
112         char buffer[PATH_MAX];
113         char *p = realpath(path, buffer);
114         return p ? addenv(name, p) : -1;
115 }
116
117 /*----------------------------------------------------------
118  |   helpers for handling list of arguments
119  +--------------------------------------------------------- */
120
121 static const char *run_for_config_array_opt(const char *name,
122                                             int (*run) (void *closure, const char *value),
123                                             void *closure)
124 {
125         int i, n, rc;
126         struct json_object *array, *value;
127
128         if (json_object_object_get_ex(main_config, name, &array)) {
129                 if (!json_object_is_type(array, json_type_array))
130                         return json_object_get_string(array);
131                 n = (int)json_object_array_length(array);
132                 for (i = 0 ; i < n ; i++) {
133                         value = json_object_array_get_idx(array, i);
134                         rc = run(closure, json_object_get_string(value));
135                         if (!rc)
136                                 return json_object_get_string(value);
137                 }
138         }
139         return NULL;
140 }
141
142 static int run_start(void *closure, const char *value)
143 {
144         int (*starter) (const char *value, struct afb_apiset *declare_set, struct afb_apiset *call_set) = closure;
145         return starter(value, main_apiset, main_apiset) >= 0;
146 }
147
148 static void apiset_start_list(const char *name,
149                         int (*starter) (const char *value, struct afb_apiset *declare_set, struct afb_apiset *call_set),
150                         const char *message)
151 {
152         const char *item = run_for_config_array_opt(name, run_start, starter);
153         if (item) {
154                 ERROR("can't start %s %s", message, item);
155                 exit(1);
156         }
157 }
158
159 /*----------------------------------------------------------
160  | exit_handler
161  |   Handles on exit specific actions
162  +--------------------------------------------------------- */
163 static void exit_handler()
164 {
165         struct sigaction siga;
166
167         memset(&siga, 0, sizeof siga);
168         siga.sa_handler = SIG_IGN;
169         sigaction(SIGTERM, &siga, NULL);
170
171         if (SELF_PGROUP)
172                 killpg(0, SIGTERM);
173         else if (childpid > 0)
174                 killpg(childpid, SIGTERM);
175 }
176
177 static void on_sigterm(int signum, siginfo_t *info, void *uctx)
178 {
179         NOTICE("Received SIGTERM");
180         exit(0);
181 }
182
183 static void on_sighup(int signum, siginfo_t *info, void *uctx)
184 {
185         NOTICE("Received SIGHUP");
186         /* TODO */
187 }
188
189 static void setup_daemon()
190 {
191         struct sigaction siga;
192
193         /* install signal handlers */
194         memset(&siga, 0, sizeof siga);
195         siga.sa_flags = SA_SIGINFO;
196
197         siga.sa_sigaction = on_sigterm;
198         sigaction(SIGTERM, &siga, NULL);
199
200         siga.sa_sigaction = on_sighup;
201         sigaction(SIGHUP, &siga, NULL);
202
203         /* handle groups */
204         atexit(exit_handler);
205
206         /* ignore any SIGPIPE */
207         signal(SIGPIPE, SIG_IGN);
208 }
209
210 /*----------------------------------------------------------
211  | daemonize
212  |   set the process in background
213  +--------------------------------------------------------- */
214 static void daemonize()
215 {
216         int fd = 0, daemon;
217         const char *output;
218         pid_t pid;
219
220         daemon = 0;
221         output = NULL;
222         wrap_json_unpack(main_config, "{s?b s?s}", "daemon", &daemon, "output", &output);
223
224         if (output) {
225                 fd = open(output, O_WRONLY | O_APPEND | O_CREAT, 0640);
226                 if (fd < 0) {
227                         ERROR("Can't open output %s", output);
228                         exit(1);
229                 }
230         }
231
232         if (daemon) {
233                 INFO("entering background mode");
234
235                 pid = fork();
236                 if (pid == -1) {
237                         ERROR("Failed to fork daemon process");
238                         exit(1);
239                 }
240                 if (pid != 0)
241                         _exit(0);
242         }
243
244         /* closes the input */
245         if (output) {
246                 NOTICE("Redirecting output to %s", output);
247                 close(2);
248                 dup(fd);
249                 close(1);
250                 dup(fd);
251                 close(fd);
252         }
253
254         /* after that ctrl+C still works */
255         close(0);
256 }
257
258 /*---------------------------------------------------------
259  | http server
260  |   Handles the HTTP server
261  +--------------------------------------------------------- */
262 static int init_alias(void *closure, const char *spec)
263 {
264         struct afb_hsrv *hsrv = closure;
265         char *path = strchr(spec, ':');
266
267         if (path == NULL) {
268                 ERROR("Missing ':' in alias %s. Alias ignored", spec);
269                 return 1;
270         }
271         *path++ = 0;
272         INFO("Alias for url=%s to path=%s", spec, path);
273         return afb_hsrv_add_alias(hsrv, spec, afb_common_rootdir_get_fd(), path,
274                                   0, 0);
275 }
276
277 static int init_http_server(struct afb_hsrv *hsrv)
278 {
279         int rc;
280         const char *rootapi, *roothttp, *rootbase;
281
282         roothttp = NULL;
283         rc = wrap_json_unpack(main_config, "{ss ss s?s}",
284                                 "rootapi", &rootapi,
285                                 "rootbase", &rootbase,
286                                 "roothttp", &roothttp);
287         if (rc < 0) {
288                 ERROR("Can't get HTTP server config");
289                 exit(1);
290         }
291
292         if (!afb_hsrv_add_handler(hsrv, rootapi,
293                         afb_hswitch_websocket_switch, main_apiset, 20))
294                 return 0;
295
296         if (!afb_hsrv_add_handler(hsrv, rootapi,
297                         afb_hswitch_apis, main_apiset, 10))
298                 return 0;
299
300         if (run_for_config_array_opt("alias", init_alias, hsrv))
301                 return 0;
302
303         if (roothttp != NULL) {
304                 if (!afb_hsrv_add_alias(hsrv, "",
305                         afb_common_rootdir_get_fd(), roothttp, -10, 1))
306                         return 0;
307         }
308
309         if (!afb_hsrv_add_handler(hsrv, rootbase,
310                         afb_hswitch_one_page_api_redirect, NULL, -20))
311                 return 0;
312
313         return 1;
314 }
315
316 static struct afb_hsrv *start_http_server()
317 {
318         int rc;
319         const char *uploaddir, *rootdir;
320         struct afb_hsrv *hsrv;
321         int cache_timeout, http_port;
322
323         rc = wrap_json_unpack(main_config, "{ss ss si si}",
324                                 "uploaddir", &uploaddir,
325                                 "rootdir", &rootdir,
326                                 "cache-eol", &cache_timeout,
327                                 "port", &http_port);
328         if (rc < 0) {
329                 ERROR("Can't get HTTP server start config");
330                 exit(1);
331         }
332
333         if (afb_hreq_init_download_path(uploaddir)) {
334                 ERROR("unable to set the upload directory %s", uploaddir);
335                 return NULL;
336         }
337
338         hsrv = afb_hsrv_create();
339         if (hsrv == NULL) {
340                 ERROR("memory allocation failure");
341                 return NULL;
342         }
343
344         if (!afb_hsrv_set_cache_timeout(hsrv, cache_timeout)
345             || !init_http_server(hsrv)) {
346                 ERROR("initialisation of httpd failed");
347                 afb_hsrv_put(hsrv);
348                 return NULL;
349         }
350
351         NOTICE("Waiting port=%d rootdir=%s", http_port, rootdir);
352         NOTICE("Browser URL= http://localhost:%d", http_port);
353
354         rc = afb_hsrv_start(hsrv, (uint16_t) http_port, 15);
355         if (!rc) {
356                 ERROR("starting of httpd failed");
357                 afb_hsrv_put(hsrv);
358                 return NULL;
359         }
360
361         return hsrv;
362 }
363
364 /*---------------------------------------------------------
365  | execute_command
366  +--------------------------------------------------------- */
367
368 static void on_sigchld(int signum, siginfo_t *info, void *uctx)
369 {
370         if (info->si_pid == childpid) {
371                 switch (info->si_code) {
372                 case CLD_EXITED:
373                 case CLD_KILLED:
374                 case CLD_DUMPED:
375                         childpid = 0;
376                         if (!SELF_PGROUP)
377                                 killpg(info->si_pid, SIGKILL);
378                         waitpid(info->si_pid, NULL, 0);
379                         exit(0);
380                 }
381         }
382 }
383
384 /*
385 # @@ @
386 # @p port
387 # @t token
388 */
389
390 #define SUBST_CHAR  '@'
391 #define SUBST_STR   "@"
392
393 static char *instanciate_string(const char *arg, const char *port, const char *token)
394 {
395         char *resu, *it, *wr;
396         int chg, dif;
397
398         /* get the changes */
399         chg = 0;
400         dif = 0;
401         it = strchrnul(arg, SUBST_CHAR);
402         while (*it) {
403                 switch(*++it) {
404                 case 'p': chg++; dif += (int)strlen(port) - 2; break;
405                 case 't': chg++; dif += (int)strlen(token) - 2; break;
406                 case SUBST_CHAR: it++; chg++; dif--; break;
407                 default: break;
408                 }
409                 it = strchrnul(it, SUBST_CHAR);
410         }
411
412         /* return arg when no change */
413         if (!chg)
414                 return strdup(arg);
415
416         /* allocates the result */
417         resu = malloc((it - arg) + dif + 1);
418         if (!resu) {
419                 ERROR("out of memory");
420                 return NULL;
421         }
422
423         /* instanciate the arguments */
424         wr = resu;
425         for (;;) {
426                 it = strchrnul(arg, SUBST_CHAR);
427                 wr = mempcpy(wr, arg, it - arg);
428                 if (!*it)
429                         break;
430                 switch(*++it) {
431                 case 'p': wr = stpcpy(wr, port); break;
432                 case 't': wr = stpcpy(wr, token); break;
433                 default: *wr++ = SUBST_CHAR; /*@fallthrough@*/
434                 case SUBST_CHAR: *wr++ = *it;
435                 }
436                 arg = ++it;
437         }
438
439         *wr = 0;
440         return resu;
441 }
442
443 static int instanciate_environ(const char *port, const char *token)
444 {
445         extern char **environ;
446         char *repl;
447         int i;
448
449         /* instantiate the environment */
450         for (i = 0 ; environ[i] ; i++) {
451                 repl = instanciate_string(environ[i], port, token);
452                 if (!repl)
453                         return -1;
454                 environ[i] = repl;
455         }
456         return 0;
457 }
458
459 static char **instanciate_command_args(struct json_object *exec, const char *port, const char *token)
460 {
461         char **result;
462         char *repl;
463         int i, n;
464
465         /* allocates the result */
466         n = (int)json_object_array_length(exec);
467         result = malloc((n + 1) * sizeof * result);
468         if (!result) {
469                 ERROR("out of memory");
470                 return NULL;
471         }
472
473         /* instanciate the arguments */
474         for (i = 0 ; i < n ; i++) {
475                 repl = instanciate_string(json_object_get_string(json_object_array_get_idx(exec, i)), port, token);
476                 if (!repl) {
477                         while(i)
478                                 free(result[--i]);
479                         free(result);
480                         return NULL;
481                 }
482                 result[i] = repl;
483         }
484         result[i] = NULL;
485         return result;
486 }
487
488 static int execute_command()
489 {
490         struct json_object *exec, *oport;
491         struct sigaction siga;
492         char port[20];
493         const char *token;
494         char **args;
495         int rc;
496
497         /* check whether a command is to execute or not */
498         if (!json_object_object_get_ex(main_config, "exec", &exec))
499                 return 0;
500
501         if (SELF_PGROUP)
502                 setpgid(0, 0);
503
504         /* install signal handler */
505         memset(&siga, 0, sizeof siga);
506         siga.sa_sigaction = on_sigchld;
507         siga.sa_flags = SA_SIGINFO;
508         sigaction(SIGCHLD, &siga, NULL);
509
510         /* fork now */
511         childpid = fork();
512         if (childpid)
513                 return 0;
514
515         /* compute the string for port */
516         if (json_object_object_get_ex(main_config, "port", &oport))
517                 rc = snprintf(port, sizeof port, "%s", json_object_get_string(oport));
518         else
519                 rc = snprintf(port, sizeof port, "%cp", SUBST_CHAR);
520         if (rc < 0 || rc >= (int)(sizeof port)) {
521                 ERROR("port->txt failed");
522         }
523         else {
524                 /* instanciate arguments and environment */
525                 token = afb_session_initial_token();
526                 args = instanciate_command_args(exec, port, token);
527                 if (args && instanciate_environ(port, token) >= 0) {
528                         /* run */
529                         if (!SELF_PGROUP)
530                                 setpgid(0, 0);
531                         execv(args[0], args);
532                         ERROR("can't launch %s: %m", args[0]);
533                 }
534         }
535         exit(1);
536         return -1;
537 }
538
539 /*---------------------------------------------------------
540  | startup calls
541  +--------------------------------------------------------- */
542
543 struct startup_req
544 {
545         struct afb_xreq xreq;
546         char *api;
547         char *verb;
548         struct json_object *calls;
549         int index;
550         int count;
551         const char *callspec;
552         struct afb_session *session;
553 };
554
555 static void startup_call_reply(struct afb_xreq *xreq, struct json_object *object, const char *error, const char *info)
556 {
557         struct startup_req *sreq = CONTAINER_OF_XREQ(struct startup_req, xreq);
558
559         info = info ?: "";
560         if (!error) {
561                 NOTICE("startup call %s returned %s (%s)", sreq->callspec, json_object_get_string(object), info);
562                 json_object_put(object);
563         } else {
564                 ERROR("startup call %s ERROR! %s (%s)", sreq->callspec, error, info);
565                 exit(1);
566         }
567 }
568
569 static void startup_call_current(struct startup_req *sreq);
570
571 static void startup_call_unref(struct afb_xreq *xreq)
572 {
573         struct startup_req *sreq = CONTAINER_OF_XREQ(struct startup_req, xreq);
574
575         free(sreq->api);
576         free(sreq->verb);
577         json_object_put(sreq->xreq.json);
578         if (++sreq->index < sreq->count)
579                 startup_call_current(sreq);
580         else {
581                 afb_session_close(sreq->session);
582                 afb_session_unref(sreq->session);
583                 free(sreq);
584         }
585 }
586
587 static struct afb_xreq_query_itf startup_xreq_itf =
588 {
589         .reply = startup_call_reply,
590         .unref = startup_call_unref
591 };
592
593 static void startup_call_current(struct startup_req *sreq)
594 {
595         const char *api, *verb, *json;
596         enum json_tokener_error jerr;
597
598         sreq->callspec = json_object_get_string(json_object_array_get_idx(sreq->calls, sreq->index)),
599         api = sreq->callspec;
600         verb = strchr(api, '/');
601         if (verb) {
602                 json = strchr(verb, ':');
603                 if (json) {
604                         afb_xreq_init(&sreq->xreq, &startup_xreq_itf);
605                         afb_context_init(&sreq->xreq.context, sreq->session, NULL);
606                         sreq->xreq.context.validated = 1;
607                         sreq->api = strndup(api, verb - api);
608                         sreq->verb = strndup(verb + 1, json - verb - 1);
609                         sreq->xreq.request.called_api = sreq->api;
610                         sreq->xreq.request.called_verb = sreq->verb;
611                         sreq->xreq.json = json_tokener_parse_verbose(json + 1, &jerr);
612                         if (sreq->api && sreq->verb && jerr == json_tokener_success) {
613                                 afb_xreq_process(&sreq->xreq, main_apiset);
614                                 return;
615                         }
616                 }
617         }
618         ERROR("Bad call specification %s", sreq->callspec);
619         exit(1);
620 }
621
622 static void run_startup_calls()
623 {
624         struct json_object *calls;
625         struct startup_req *sreq;
626         int count;
627
628         if (json_object_object_get_ex(main_config, "call", &calls)
629          && json_object_is_type(calls, json_type_array)
630          && (count = (int)json_object_array_length(calls))) {
631                 sreq = calloc(1, sizeof *sreq);
632                 sreq->session = afb_session_create(3600);
633                 sreq->calls = calls;
634                 sreq->index = 0;
635                 sreq->count = count;
636                 startup_call_current(sreq);
637         }
638 }
639
640 /*---------------------------------------------------------
641  | job for starting the daemon
642  +--------------------------------------------------------- */
643
644 static void start(int signum, void *arg)
645 {
646         const char *tracereq, *traceapi, *traceevt, *traceses, *tracesvc, *traceditf, *traceglob;
647         const char *workdir, *rootdir, *token, *rootapi;
648         struct json_object *settings;
649         struct afb_hsrv *hsrv;
650         int max_session_count, session_timeout, api_timeout;
651         int no_httpd, http_port;
652         int rc;
653
654
655         afb_debug("start-entry");
656
657         if (signum) {
658                 ERROR("start aborted: received signal %s", strsignal(signum));
659                 exit(1);
660         }
661
662         settings = NULL;
663         token = rootapi = tracesvc = traceditf = tracereq =
664                 traceapi = traceevt = traceses = traceglob = NULL;
665         no_httpd = http_port = 0;
666         rc = wrap_json_unpack(main_config, "{"
667                         "ss ss s?s"
668                         "si si si"
669                         "s?b s?i s?s"
670                         "s?o"
671 #if !defined(REMOVE_LEGACY_TRACE)
672                         "s?s s?s"
673 #endif
674                         "s?s s?s s?s s?s s?s"
675                         "}",
676
677                         "rootdir", &rootdir,
678                         "workdir", &workdir,
679                         "token", &token,
680
681                         "apitimeout", &api_timeout,
682                         "cntxtimeout", &session_timeout,
683                         "session-max", &max_session_count,
684
685                         "no-httpd", &no_httpd,
686                         "port", &http_port,
687                         "rootapi", &rootapi,
688
689                         "set", &settings,
690 #if !defined(REMOVE_LEGACY_TRACE)
691                         "tracesvc", &tracesvc,
692                         "traceditf", &traceditf,
693 #endif
694                         "tracereq", &tracereq,
695                         "traceapi", &traceapi,
696                         "traceevt", &traceevt,
697                         "traceses",  &traceses,
698                         "traceglob", &traceglob
699                         );
700         if (rc < 0) {
701                 ERROR("Unable to get start config");
702                 exit(1);
703         }
704
705         /* set the directories */
706         mkdir(workdir, S_IRWXU | S_IRGRP | S_IXGRP);
707         if (chdir(workdir) < 0) {
708                 ERROR("Can't enter working dir %s", workdir);
709                 goto error;
710         }
711         if (afb_common_rootdir_set(rootdir) < 0) {
712                 ERROR("failed to set common root directory %s", rootdir);
713                 goto error;
714         }
715         if (addenv_realpath("AFB_WORKDIR", "."     /* resolved by realpath */)
716          || addenv_realpath("AFB_ROOTDIR", rootdir /* relative to current directory */)) {
717                 ERROR("can't set environment");
718                 goto error;
719         }
720
721         /* configure the daemon */
722         afb_export_set_config(settings);
723         if (afb_session_init(max_session_count, session_timeout, token)) {
724                 ERROR("initialisation of session manager failed");
725                 goto error;
726         }
727         main_apiset = afb_apiset_create("main", api_timeout);
728         if (!main_apiset) {
729                 ERROR("can't create main api set");
730                 goto error;
731         }
732         if (afb_monitor_init(main_apiset, main_apiset) < 0) {
733                 ERROR("failed to setup monitor");
734                 goto error;
735         }
736 #if defined(WITH_SUPERVISION)
737         if (afb_supervision_init(main_apiset, main_config) < 0) {
738                 ERROR("failed to setup supervision");
739                 goto error;
740         }
741 #endif
742
743         /* install hooks */
744         if (tracereq)
745                 afb_hook_create_xreq(NULL, NULL, NULL, afb_hook_flags_xreq_from_text(tracereq), NULL, NULL);
746 #if !defined(REMOVE_LEGACY_TRACE)
747         if (traceapi || tracesvc || traceditf)
748                 afb_hook_create_api(NULL, afb_hook_flags_api_from_text(traceapi)
749                         | afb_hook_flags_legacy_ditf_from_text(traceditf)
750                         | afb_hook_flags_legacy_svc_from_text(tracesvc), NULL, NULL);
751 #else
752         if (traceapi)
753                 afb_hook_create_api(NULL, afb_hook_flags_api_from_text(traceapi), NULL, NULL);
754 #endif
755         if (traceevt)
756                 afb_hook_create_evt(NULL, afb_hook_flags_evt_from_text(traceevt), NULL, NULL);
757         if (traceses)
758                 afb_hook_create_session(NULL, afb_hook_flags_session_from_text(traceses), NULL, NULL);
759         if (traceglob)
760                 afb_hook_create_global(afb_hook_flags_global_from_text(traceglob), NULL, NULL);
761
762         /* load bindings */
763         afb_debug("start-load");
764         apiset_start_list("binding", afb_api_so_add_binding, "the binding");
765         apiset_start_list("ldpaths", afb_api_so_add_pathset_fails, "the binding path set");
766         apiset_start_list("weak-ldpaths", afb_api_so_add_pathset_nofails, "the weak binding path set");
767         apiset_start_list("auto-api", afb_autoset_add_any, "the automatic api path set");
768         apiset_start_list("ws-server", afb_api_ws_add_server, "the afb-websocket service");
769 #if defined(WITH_DBUS_TRANSPARENCY)
770         apiset_start_list("dbus-server", afb_api_dbus_add_server, "the afb-dbus service");
771         apiset_start_list("dbus-client", afb_api_dbus_add_client, "the afb-dbus client");
772 #endif
773         apiset_start_list("ws-client", afb_api_ws_add_client_weak, "the afb-websocket client");
774
775         DEBUG("Init config done");
776
777         /* start the services */
778         afb_debug("start-start");
779 #if !defined(NO_CALL_PERSONALITY)
780         personality((unsigned long)-1L);
781 #endif
782         if (afb_apiset_start_all_services(main_apiset) < 0)
783                 goto error;
784
785         /* start the HTTP server */
786         afb_debug("start-http");
787         if (!no_httpd) {
788                 if (http_port <= 0) {
789                         ERROR("no port is defined");
790                         goto error;
791                 }
792
793                 if (!afb_hreq_init_cookie(http_port, rootapi, session_timeout)) {
794                         ERROR("initialisation of HTTP cookies failed");
795                         goto error;
796                 }
797
798                 hsrv = start_http_server();
799                 if (hsrv == NULL)
800                         goto error;
801         }
802
803         /* run the startup calls */
804         afb_debug("start-call");
805         run_startup_calls();
806
807         /* run the command */
808         afb_debug("start-exec");
809         if (execute_command() < 0)
810                 goto error;
811
812         /* ready */
813         sd_notify(1, "READY=1");
814         return;
815 error:
816         exit(1);
817 }
818
819 /*---------------------------------------------------------
820  | main
821  |   Parse option and launch action
822  +--------------------------------------------------------- */
823
824 int main(int argc, char *argv[])
825 {
826         struct json_object *name;
827         afb_debug("main-entry");
828
829         // ------------- Build session handler & init config -------
830         main_config = afb_config_parse_arguments(argc, argv);
831         if (json_object_object_get_ex(main_config, "name", &name)) {
832                 verbose_set_name(json_object_get_string(name), 0);
833                 process_name_set_name(json_object_get_string(name));
834                 process_name_replace_cmdline(argv, json_object_get_string(name));
835         }
836         afb_debug("main-args");
837
838         // --------- run -----------
839         daemonize();
840         INFO("running with pid %d", getpid());
841
842         /* set the daemon environment */
843         setup_daemon();
844
845         afb_debug("main-start");
846
847         /* enter job processing */
848         jobs_start(3, 0, 50, start, NULL);
849         WARNING("hoops returned from jobs_enter! [report bug]");
850         return 1;
851 }
852