Adds 2017 to copyrights
[src/app-framework-binder.git] / src / main.c
index ba19925..4a074ee 100644 (file)
@@ -1,5 +1,5 @@
-/* 
- * Copyright (C) 2015 "IoT.bzh"
+/*
+ * Copyright (C) 2015, 2016, 2017 "IoT.bzh"
  * Author "Fulup Ar Foll"
  * Author José Bollo <jose.bollo@iot.bzh>
  *
  */
 
 #define _GNU_SOURCE
+#define NO_BINDING_VERBOSE_MACRO
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdint.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <sys/types.h>
-
 #include <getopt.h>
-#include <setjmp.h>
-#include <signal.h>
-#include <syslog.h>
 
 #include <systemd/sd-event.h>
 
 #include "afb-hswitch.h"
 #include "afb-apis.h"
 #include "afb-api-so.h"
+#include "afb-api-dbus.h"
+#include "afb-api-ws.h"
 #include "afb-hsrv.h"
 #include "afb-context.h"
 #include "afb-hreq.h"
+#include "afb-sig-handler.h"
+#include "afb-thread.h"
 #include "session.h"
 #include "verbose.h"
 #include "afb-common.h"
+#include "afb-hook.h"
 
-#include "afb-plugin.h"
+#include <afb/afb-binding.h>
 
-#if !defined(PLUGIN_INSTALL_DIR)
-#error "you should define PLUGIN_INSTALL_DIR"
+#if !defined(BINDING_INSTALL_DIR)
+#error "you should define BINDING_INSTALL_DIR"
 #endif
 
-#define AFB_VERSION    "0.4"
+#define TRACEREQ_NO     0
+#define TRACEREQ_COMMON 1
+#define TRACEREQ_EXTRA  2
+#define TRACEREQ_ALL    3
+
+#define AFB_VERSION    "0.5"
 
 // Define command line option
 #define SET_VERBOSE        1
 #define SET_MODE           18
 #define SET_READYFD        19
 
+#define DBUS_CLIENT        20
+#define DBUS_SERVICE       21
+#define SO_BINDING         22
+
+#define SET_SESSIONMAX     23
+
+#define WS_CLIENT          24
+#define WS_SERVICE         25
+
+#define SET_ROOT_HTTP      26
+
+#define SET_TRACEREQ       27
+
 // Command line structure hold cli --command + help text
 typedef struct {
   int  val;        // command number within application
@@ -89,35 +107,45 @@ typedef struct {
 
 // Supported option
 static  AFB_options cliOptions [] = {
-  {SET_VERBOSE      ,0,"verbose"         , "Verbose Mode"},
+  {SET_VERBOSE      ,0,"verbose"         , "Verbose Mode, repeat to increase verbosity"},
 
   {SET_FORGROUND    ,0,"foreground"      , "Get all in foreground mode"},
   {SET_BACKGROUND   ,0,"daemon"          , "Get all in background mode"},
 
   {SET_TCP_PORT     ,1,"port"            , "HTTP listening TCP port  [default 1234]"},
-  {SET_ROOT_DIR     ,1,"rootdir"         , "HTTP Root Directory [default $HOME/.AFB]"},
+  {SET_ROOT_DIR     ,1,"rootdir"         , "Root Directory [default $HOME/.AFB]"},
+  {SET_ROOT_HTTP    ,1,"roothttp"        , "HTTP Root Directory [default rootdir]"},
   {SET_ROOT_BASE    ,1,"rootbase"        , "Angular Base Root URL [default /opa]"},
   {SET_ROOT_API     ,1,"rootapi"         , "HTML Root API URL [default /api]"},
   {SET_ALIAS        ,1,"alias"           , "Muliple url map outside of rootdir [eg: --alias=/icons:/usr/share/icons]"},
-  
-  {SET_APITIMEOUT   ,1,"apitimeout"      , "Plugin API timeout in seconds [default 10]"},
+
+  {SET_APITIMEOUT   ,1,"apitimeout"      , "Binding API timeout in seconds [default 10]"},
   {SET_CNTXTIMEOUT  ,1,"cntxtimeout"     , "Client Session Context Timeout [default 900]"},
-  {SET_CACHE_TIMEOUT,1,"cache-eol"       , "Client cache end of live [default 3600s]"},
-  
+  {SET_CACHE_TIMEOUT,1,"cache-eol"       , "Client cache end of live [default 3600]"},
+
   {SET_SESSION_DIR  ,1,"sessiondir"      , "Sessions file path [default rootdir/sessions]"},
 
-  {SET_LDPATH       ,1,"ldpaths"         , "Load Plugins from dir1:dir2:... [default = PLUGIN_INSTALL_DIR"},
+  {SET_LDPATH       ,1,"ldpaths"         , "Load bindingss from dir1:dir2:... [default = "BINDING_INSTALL_DIR"]"},
   {SET_AUTH_TOKEN   ,1,"token"           , "Initial Secret [default=no-session, --token="" for session without authentication]"},
-  
+
   {DISPLAY_VERSION  ,0,"version"         , "Display version and copyright"},
   {DISPLAY_HELP     ,0,"help"            , "Display this help"},
 
   {SET_MODE         ,1,"mode"            , "set the mode: either local, remote or global"},
   {SET_READYFD      ,1,"readyfd"         , "set the #fd to signal when ready"},
-  {0, 0, NULL, NULL}
- };
 
+  {DBUS_CLIENT      ,1,"dbus-client"     , "bind to an afb service through dbus"},
+  {DBUS_SERVICE     ,1,"dbus-server"     , "provides an afb service through dbus"},
+  {WS_CLIENT        ,1,"ws-client"       , "bind to an afb service through websocket"},
+  {WS_SERVICE       ,1,"ws-server"       , "provides an afb service through websockets"},
+  {SO_BINDING       ,1,"binding"         , "load the binding of path"},
 
+  {SET_SESSIONMAX   ,1,"session-max"     , "max count of session simultaneously [default 10]"},
+
+  {SET_TRACEREQ     ,1,"tracereq"        , "log the requests: no, common, extra, all"},
+
+  {0, 0, NULL, NULL}
+ };
 
 /*----------------------------------------------------------
  | printversion
@@ -128,7 +156,7 @@ static void printVersion (FILE *file)
    fprintf(file, "\n----------------------------------------- \n");
    fprintf(file, "  AFB [Application Framework Binder] version=%s |\n", AFB_VERSION);
    fprintf(file, " \n");
-   fprintf(file, "  Copyright(C) 2016 /IoT.bzh [fulup -at- iot.bzh]\n");
+   fprintf(file, "  Copyright (C) 2015, 2016, 2017 \"IoT.bzh\" [fulup -at- iot.bzh]\n");
    fprintf(file, "  AFB comes with ABSOLUTELY NO WARRANTY.\n");
    fprintf(file, "  Licence Apache 2\n\n");
    exit (0);
@@ -156,7 +184,7 @@ static void printHelp(FILE *file, const char *name)
          fprintf (file, "  --%-15s %s\n", command, cliOptions[ind].help);
       }
     }
-    fprintf (file, "Example:\n  %s\\\n  --verbose --port=1234 --token='azerty' --ldpaths=build/plugins:/usr/lib64/agl/plugins\n", name);
+    fprintf (file, "Example:\n  %s\\\n  --verbose --port=1234 --token='azerty' --ldpaths=build/bindings:/usr/lib64/agl/bindings\n", name);
 }
 
 // load config from disk and merge with CLI option
@@ -165,11 +193,11 @@ static void config_set_default (struct afb_config * config)
    // default HTTP port
    if (config->httpdPort == 0)
        config->httpdPort = 1234;
-   
-   // default Plugin API timeout
+
+   // default binding API timeout
    if (config->apiTimeout == 0)
        config->apiTimeout = DEFLT_API_TIMEOUT;
-   
+
    // default AUTH_TOKEN
    if (config->token == NULL)
                config->token = DEFLT_AUTH_TOKEN;
@@ -182,6 +210,10 @@ static void config_set_default (struct afb_config * config)
    if (config->cntxTimeout == 0)
                config->cntxTimeout = DEFLT_CNTX_TIMEOUT;
 
+   // max count of sessions
+   if (config->nbSessionMax == 0)
+       config->nbSessionMax = CTX_NBCLIENTS;
+
    if (config->rootdir == NULL) {
        config->rootdir = getenv("AFBDIR");
        if (config->rootdir == NULL) {
@@ -192,16 +224,16 @@ static void config_set_default (struct afb_config * config)
        // if directory does not exist createit
        mkdir (config->rootdir,  O_RDWR | S_IRWXU | S_IRGRP);
    }
-   
+
    // if no Angular/HTML5 rootbase let's try '/' as default
    if  (config->rootbase == NULL)
        config->rootbase = "/opa";
-   
+
    if  (config->rootapi == NULL)
        config->rootapi = "/api";
 
    if  (config->ldpaths == NULL)
-       config->ldpaths = PLUGIN_INSTALL_DIR;
+       config->ldpaths = BINDING_INSTALL_DIR;
 
    // if no session dir create a default path from rootdir
    if  (config->sessiondir == NULL) {
@@ -224,6 +256,19 @@ static void config_set_default (struct afb_config * config)
  |   Parse option and launch action
  +--------------------------------------------------------- */
 
+static void add_item(struct afb_config *config, int kind, char *value)
+{
+       struct afb_config_item *item = malloc(sizeof *item);
+       if (item == NULL) {
+               ERROR("out of memory");
+               exit(1);
+       }
+       item->kind = kind;
+       item->value = value;
+       item->previous = config->items;
+       config->items = item;
+}
+
 static void parse_arguments(int argc, char *argv[], struct afb_config *config)
 {
   char*          programName = argv [0];
@@ -264,7 +309,7 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
        if (optarg == 0) goto needValueForOption;
        if (!sscanf (optarg, "%d", &config->httpdPort)) goto notAnInteger;
        break;
-       
+
     case SET_APITIMEOUT:
        if (optarg == 0) goto needValueForOption;
        if (!sscanf (optarg, "%d", &config->apiTimeout)) goto notAnInteger;
@@ -279,8 +324,14 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
        if (optarg == 0) goto needValueForOption;
        config->rootdir   = optarg;
        INFO("Forcing Rootdir=%s",config->rootdir);
-       break;       
-       
+       break;
+
+    case SET_ROOT_HTTP:
+       if (optarg == 0) goto needValueForOption;
+       config->roothttp   = optarg;
+       INFO("Forcing Root HTTP=%s",config->roothttp);
+       break;
+
     case SET_ROOT_BASE:
        if (optarg == 0) goto needValueForOption;
        config->rootbase   = optarg;
@@ -292,7 +343,7 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
        config->rootapi   = optarg;
        INFO("Forcing Rootapi=%s",config->rootapi);
        break;
-       
+
     case SET_ALIAS:
        if (optarg == 0) goto needValueForOption;
        if ((unsigned)config->aliascount < sizeof (config->aliasdir) / sizeof (config->aliasdir[0])) {
@@ -306,9 +357,9 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
             }
        } else {
            ERROR("Too many aliases [max:%d] %s ignored", MAX_ALIAS, optarg);
-       }     
+       }
        break;
-       
+
     case SET_AUTH_TOKEN:
        if (optarg == 0) goto needValueForOption;
        config->token   = optarg;
@@ -329,6 +380,11 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
        if (!sscanf (optarg, "%d", &config->cacheTimeout)) goto notAnInteger;
        break;
 
+    case  SET_SESSIONMAX:
+       if (optarg == 0) goto needValueForOption;
+       if (!sscanf (optarg, "%d", &config->nbSessionMax)) goto notAnInteger;
+       break;
+
     case SET_FORGROUND:
        if (optarg != 0) goto noValueForOption;
        config->background  = 0;
@@ -352,6 +408,24 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
        if (!sscanf (optarg, "%u", &config->readyfd)) goto notAnInteger;
        break;
 
+    case DBUS_CLIENT:
+    case DBUS_SERVICE:
+    case WS_CLIENT:
+    case WS_SERVICE:
+    case SO_BINDING:
+       if (optarg == 0) goto needValueForOption;
+       add_item(config, optc, optarg);
+       break;
+
+    case SET_TRACEREQ:
+       if (optarg == 0) goto needValueForOption;
+       if (!strcmp(optarg, "no")) config->tracereq = TRACEREQ_NO;
+       else if (!strcmp(optarg, "common")) config->tracereq = TRACEREQ_COMMON;
+       else if (!strcmp(optarg, "extra")) config->tracereq = TRACEREQ_EXTRA;
+       else if (!strcmp(optarg, "all")) config->tracereq = TRACEREQ_ALL;
+       else goto badMode;
+       break;
+
     case DISPLAY_VERSION:
        if (optarg != 0) goto noValueForOption;
        printVersion(stdout);
@@ -364,7 +438,7 @@ static void parse_arguments(int argc, char *argv[], struct afb_config *config)
     }
   }
   free(gnuOptions);
+
   config_set_default  (config);
   return;
 
@@ -398,50 +472,6 @@ static void closeSession (int status, void *data) {
        /* struct afb_config *config = data; */
 }
 
-/*----------------------------------------------------------
- | timeout signalQuit
- +--------------------------------------------------------- */
-void signalQuit (int signum)
-{
-       ERROR("Terminating signal received %s", strsignal(signum));
-       exit(1);
-}
-
-
-/*----------------------------------------------------------
- | Error signals
- |
- +--------------------------------------------------------- */
-__thread sigjmp_buf *error_handler;
-static void signalError(int signum)
-{
-       sigset_t sigset;
-
-       // unlock signal to allow a new signal to come
-       if (error_handler != NULL) {
-               sigemptyset(&sigset);
-               sigaddset(&sigset, signum);
-               sigprocmask(SIG_UNBLOCK, &sigset, 0);
-               longjmp(*error_handler, signum);
-       }
-       if (signum == SIGALRM)
-               return;
-       ERROR("Unmonitored signal received %s", strsignal(signum));
-       exit(2);
-}
-
-static void install_error_handlers()
-{
-       int i, signals[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
-
-       for (i = 0; signals[i] != 0; i++) {
-               if (signal(signals[i], signalError) == SIG_ERR) {
-                       ERROR("Signal handler error");
-                       exit(1);
-               }
-       }
-}
-
 /*----------------------------------------------------------
  | daemonize
  |   set the process in background
@@ -491,7 +521,9 @@ static void daemonize(struct afb_config *config)
  +--------------------------------------------------------- */
 static int init_http_server(struct afb_hsrv *hsrv, struct afb_config * config)
 {
-       int idx;
+       int idx, dfd;
+
+       dfd = afb_common_rootdir_get_fd();
 
        if (!afb_hsrv_add_handler(hsrv, config->rootapi, afb_hswitch_websocket_switch, NULL, 20))
                return 0;
@@ -500,11 +532,13 @@ static int init_http_server(struct afb_hsrv *hsrv, struct afb_config * config)
                return 0;
 
        for (idx = 0; idx < config->aliascount; idx++)
-               if (!afb_hsrv_add_alias (hsrv, config->aliasdir[idx].url, config->aliasdir[idx].path, 0))
+               if (!afb_hsrv_add_alias (hsrv, config->aliasdir[idx].url, dfd, config->aliasdir[idx].path, 0, 0))
                        return 0;
 
-       if (!afb_hsrv_add_alias(hsrv, "", config->rootdir, -10))
-               return 0;
+       if (config->roothttp != NULL) {
+               if (!afb_hsrv_add_alias(hsrv, "", dfd, config->roothttp, -10, 1))
+                       return 0;
+       }
 
        if (!afb_hsrv_add_handler(hsrv, config->rootbase, afb_hswitch_one_page_api_redirect, NULL, -20))
                return 0;
@@ -548,6 +582,50 @@ static struct afb_hsrv *start_http_server(struct afb_config * config)
        return hsrv;
 }
 
+static void start_items(struct afb_config_item *item)
+{
+  if (item != NULL) {
+    /* keeps the order */
+    start_items(item->previous);
+    switch(item->kind) {
+    case DBUS_CLIENT:
+      if (afb_api_dbus_add_client(item->value) < 0) {
+        ERROR("can't start the afb-dbus client of path %s",item->value);
+       exit(1);
+      }
+      break;
+    case DBUS_SERVICE:
+      if (afb_api_dbus_add_server(item->value) < 0) {
+        ERROR("can't start the afb-dbus service of path %s",item->value);
+       exit(1);
+      }
+      break;
+    case WS_CLIENT:
+      if (afb_api_ws_add_client(item->value) < 0) {
+        ERROR("can't start the afb-websocket client of path %s",item->value);
+       exit(1);
+      }
+      break;
+    case WS_SERVICE:
+      if (afb_api_ws_add_server(item->value) < 0) {
+        ERROR("can't start the afb-websocket service of path %s",item->value);
+       exit(1);
+      }
+      break;
+    case SO_BINDING:
+      if (afb_api_so_add_binding(item->value) < 0) {
+        ERROR("can't start the binding of path %s",item->value);
+       exit(1);
+      }
+      break;
+    default:
+      ERROR("unexpected internal error");
+      exit(1);
+    }
+    /* frre the item */
+    free(item);
+  }
+}
 
 /*---------------------------------------------------------
  | main
@@ -559,8 +637,7 @@ int main(int argc, char *argv[])  {
   struct afb_config *config;
   struct sd_event *eventloop;
 
-  // open syslog if ever needed
-  openlog("afb-daemon", 0, LOG_DAEMON);
+  LOGAUTH("afb-daemon");
 
   // ------------- Build session handler & init config -------
   config = calloc (1, sizeof (struct afb_config));
@@ -574,20 +651,35 @@ int main(int argc, char *argv[])  {
      exit (1);
   }
 
-  if (config->ldpaths) 
-    afb_api_so_add_pathset(config->ldpaths);
+  afb_api_so_set_timeout(config->apiTimeout);
+  if (config->ldpaths) {
+    if (afb_api_so_add_pathset(config->ldpaths) < 0) {
+      ERROR("initialisation of bindings within %s failed", config->ldpaths);
+      exit(1);
+    }
+  }
+
+  start_items(config->items);
+  config->items = NULL;
 
-  ctxStoreInit(CTX_NBCLIENTS, config->cntxTimeout, config->token, afb_apis_count());
+  ctxStoreInit(config->nbSessionMax, config->cntxTimeout, config->token, afb_apis_count());
   if (!afb_hreq_init_cookie(config->httpdPort, config->rootapi, DEFLT_CNTX_TIMEOUT)) {
      ERROR("initialisation of cookies failed");
      exit (1);
   }
 
-  install_error_handlers();
+  if (afb_sig_handler_init() < 0) {
+     ERROR("failed to initialise signal handlers");
+     return 1;
+  }
 
-  // ------------------ clean exit on CTR-C signal ------------------------
-  if (signal (SIGINT, signalQuit) == SIG_ERR || signal (SIGABRT, signalQuit) == SIG_ERR) {
-     ERROR("main fail to install Signal handler");
+  if (afb_common_rootdir_set(config->rootdir) < 0) {
+     ERROR("failed to set common root directory");
+     return 1;
+  }
+
+  if (afb_thread_init(3, 1, 20) < 0) {
+     ERROR("failed to initialise threading");
      return 1;
   }
 
@@ -610,10 +702,34 @@ int main(int argc, char *argv[])  {
       INFO("entering foreground mode");
   }
 
+  /* ignore any SIGPIPE */
+  signal(SIGPIPE, SIG_IGN);
+
+  /* install trace of requests */
+  switch(config->tracereq) {
+  default:
+  case TRACEREQ_NO:
+       break;
+  case TRACEREQ_COMMON:
+       afb_hook_req_create(NULL, NULL, NULL, afb_hook_flags_req_common, NULL, NULL);
+       break;
+  case TRACEREQ_EXTRA:
+       afb_hook_req_create(NULL, NULL, NULL, afb_hook_flags_req_extra, NULL, NULL);
+       break;
+  case TRACEREQ_ALL:
+       afb_hook_req_create(NULL, NULL, NULL, afb_hook_flags_req_all, NULL, NULL);
+       break;
+  }
+
+   /* start the HTTP server */
    hsrv = start_http_server(config);
    if (hsrv == NULL)
        exit(1);
 
+   /* start the services */
+   if (afb_apis_start_all_services(1) < 0)
+       exit(1);
+
    if (config->readyfd != 0) {
                static const char readystr[] = "READY=1";
                write(config->readyfd, readystr, sizeof(readystr) - 1);