Skip to content
Snippets Groups Projects
Select Git revision
  • ad66fa9410a5b2a37d9124de5f6a37988aace740
  • main default protected
  • tags/misc
  • tags/version-0.5r2
  • tags/version-0.6
  • tags/version-0.4
  • tags/version-0.5r1
  • tags/libxaal_v01
  • tags/generic-feedback-renderer_complexAPI
  • tags/testing-libsodium
  • tags/testing-nettle
  • tags/testing_ajax
  • tags/testing_clearsilver
  • tags/testing_jansson
  • tags/testing_jsmn
  • tags/testing_json-c
16 results

automalua.c

Blame
  • automalua.c 41.21 KiB
    /* Automa-Lua - xAAL Automata with Lua
     * (c) 2019 Christophe Lohr <christophe.lohr@imt-atlantique.fr>
     *
     * 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 <http://www.gnu.org/licenses/>.
     */
    
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <string.h>
    #include <errno.h>
    #include <signal.h>
    #include <termios.h>
    #include <sys/timerfd.h>
    
    #include <sys/queue.h>
    
    #include <json-c/json.h>
    #include <cbor.h>
    #include <uuid/uuid.h>
    
    #include <lua5.2/lua.h>
    #include <lua5.2/lualib.h>
    #include <lua5.2/lauxlib.h>
    
    #include <xaal.h>
    
    
    
    #define ALIVE_PERIOD    120
    
    
    /****************************/
    /* Chapter: data structures */
    /****************************/
    
    /**
     * Section: Lua context
     */
    
    /* List of lua states with script name */
    typedef TAILQ_HEAD(luactxshead, luactxentry) luactxs_t;
    typedef struct luactxentry {
      lua_State* L;
      char *script;
      uuid_t baseaddr;
      char *parameter;
      unsigned dev_inc;
      TAILQ_ENTRY(luactxentry) entries;
    } luactx_t;
    
    /* Retrieves a lua context by lua_State */
    luactx_t *retrieve_luactx(luactxs_t *luactxs, lua_State *L) {
      luactx_t *luactx;
      TAILQ_FOREACH(luactx, luactxs, entries)
        if (luactx->L == L)
          return luactx;
      return NULL;
    }
    
    
    /**
     * Section: embedded devices
     */
    
    /* List of embedded devices */
    typedef LIST_HEAD(deviceshead, deviceentry) devices_t;
    typedef struct deviceentry {
      xAAL_devinfo_t devinfo;
      luactx_t *luactx;
      uuid_t *wanted_sources;
      size_t wanted_sources_nb;
      char **wanted_devTypes;
      LIST_ENTRY(deviceentry) entries;
    } device_t;
    
    
    /* Retrieves a device by addr in the list */
    device_t *retrieve_device(devices_t *devices, const uuid_t *addr) {
      device_t *device;
      LIST_FOREACH(device, devices, entries)
        if (uuid_compare(device->devinfo.addr, *addr) == 0)
          return device;
      return NULL;
    }
    
    
    /*******************/
    /* Chapter: Alarms */
    /*******************/
    
    /* List of alarms */
    typedef CIRCLEQ_HEAD(alarmshead, alarmentry) alarms_t;
    typedef struct alarmentry {
      int delay;
      uuid_t addr;
      char *parameter;
      lua_State* L;
      CIRCLEQ_ENTRY(alarmentry) entries;
    } alarm_t;
    
    
    /* Synchronize timer on the next alarm to trigger */
    void adjust_timer(alarms_t *alarms, int timerfd) {
      struct itimerspec timerspec;
      int delay;
    
      if (alarms->cqh_first != (void *)alarms) /* Pickup first alarm's delay*/
        delay = alarms->cqh_first->delay;
      else /* Empty queue: unarms the timer. */
        delay = 0;
    
      timerspec.it_interval.tv_sec = 0;
      timerspec.it_interval.tv_nsec = 0;
      timerspec.it_value.tv_sec = delay;
      timerspec.it_value.tv_nsec = 0;
      if ( timerfd_settime(timerfd, 0, &timerspec, NULL) == -1 )
        fprintf(xAAL_error_log, "Could not adjust timer for alarms: %s\n", strerror(errno));
    }
    
    
    /* Retrun how many seconds remain on this timer */
    int remaining_timer(int timerfd) {
      struct itimerspec timerspec;
    
      if (timerfd_gettime(timerfd, &timerspec) == -1) {
        fprintf(xAAL_error_log, "timerfd_gettime: %s\n", strerror(errno));
        return 0;
      } else
        return timerspec.it_value.tv_sec;
    }
    
    /* Add an entry to the list of alarms */
    /* The list is sorted by 'delay'; try to maintain this */
    void alarms_queue_add(int delay, const uuid_t *addr, const char *parameter,
    		      lua_State* L, alarms_t *alarms, int timerfd) {
      alarm_t *al, *np;
      int prev_delay = -1;
    
      /* Build alarm data */
      al = (alarm_t *)malloc(sizeof(alarm_t));
      al->delay = delay;
      uuid_copy(al->addr, *addr);
      al->parameter = strdup(parameter);
      al->L = L;
    
      if (alarms->cqh_first == (void *)alarms) {
        /* The list of alarms is empty */
        CIRCLEQ_INSERT_HEAD(alarms, al, entries);
    
      } else {
        /* Synchronize the delay of the new alarm */
        al->delay -= remaining_timer(timerfd);
        if (al->delay < 0)
          al->delay = 0;
    
        /* Insert the new alarm at its place */
        CIRCLEQ_FOREACH(np, alarms, entries) {
          if ( (al->delay >= prev_delay) && (al->delay < np->delay) ) {
    	CIRCLEQ_INSERT_BEFORE(alarms, np, al, entries);
    	break;
          }
          prev_delay = np->delay;
        }
      }
    
      /* Ajust timer if the alarm was inserted at the head */
      adjust_timer(alarms, timerfd);
    }
    
    
    /* It's time to trigger an alarm */
    void alarms_queue_run(alarms_t *alarms, int timerfd, luactxs_t *luactxs) {
      alarm_t *np;
      int delay;
    
      if (alarms->cqh_first == (void *)alarms)
       return;  /* Empty queue, this should not happen */
    
      delay = alarms->cqh_first->delay;
      CIRCLEQ_FOREACH(np, alarms, entries) {
        np->delay -= delay;
        if (np->delay <= 0) {
          CIRCLEQ_REMOVE(alarms, np, entries);
          lua_getglobal(np->L, "xAAL_Lua_alarm_callback");
          if ( !lua_isfunction(np->L, -1) ) {
    	luactx_t *luactx = retrieve_luactx(luactxs, np->L);
    	fprintf(xAAL_error_log,"Error: no xAAL_Lua_alarm_callback() in %s\n",
    		luactx ? luactx->script : "unknown Lua script");
    	exit(EXIT_FAILURE);
          } else {
    	lua_pushlstring(np->L, (char*)np->addr, 16);
    	lua_pushstring(np->L, np->parameter);
    	lua_call(np->L, 2, 0);
          }
          free(np->parameter);
          free(np);
        }
      }
    
      /* In case one removed the first probe, adjust timer */
      adjust_timer(alarms, timerfd);
    }
    
    /****************/
    /* Chapter: Lua */
    /****************/
    
    /* Make it global to be used by C functions called by lua */
    /* Ugly! How to avoid this? */
    devices_t *p_embedded;
    luactxs_t *p_luactxs;
    uuid_t groupId;
    xAAL_businfo_t *p_bus;
    alarms_t *p_alarms;
    int timerfd;
    
    
    /* Converts a cbor item into a Lua data and push it on the stack.
     * The (hidden) maxdepth parameter may prevents infinit recursion on
     * self-referencing data structure... Awful but secure.
     */
    #define MAXDEPTH	30
    void lua_push_cbor_ex(lua_State *L, cbor_item_t *item, unsigned maxdepth) {
      if (!maxdepth--) {
        lua_pushnil(L);
        return;
      }
      switch ( cbor_typeof(item) ) {
        case CBOR_TYPE_UINT:
          lua_pushinteger(L, cbor_get_int(item));
          break;
        case CBOR_TYPE_NEGINT:
          lua_pushinteger(L, -cbor_get_int(item)-1);
          break;
        case CBOR_TYPE_BYTESTRING: {
          size_t len;
          unsigned char *bstr = xAAL_cbor_bytestring_dup(item, &len);
          lua_pushlstring(L, (char *)bstr, len);
          free(bstr);
          break; }
        case CBOR_TYPE_STRING: {
          size_t len;
          const char *str = xAAL_cbor_string_dup(item, &len);
          lua_pushstring(L, str);
          break; }
        case CBOR_TYPE_ARRAY: {
          size_t i, len = cbor_array_size(item);
          lua_newtable(L);
          for (i=0; i < len; i++) {
    	lua_pushnumber(L, i+1);
    	lua_push_cbor_ex(L, cbor_move(cbor_array_get(item, i)), maxdepth);
    	lua_settable(L, -3);
          }
          break; }
        case CBOR_TYPE_MAP: {
          size_t i, len = cbor_map_size(item);
          struct cbor_pair *map = cbor_map_handle(item);
          lua_newtable(L);
          for (i=0; i < len; i++) {
    	lua_push_cbor_ex(L, map[i].key, maxdepth);
    	lua_push_cbor_ex(L, map[i].value, maxdepth);
    	lua_settable(L, -3);
          }
          break; }
        case CBOR_TYPE_TAG:
          lua_push_cbor_ex(L, cbor_move(cbor_tag_item(item)), maxdepth);
          break;
        case CBOR_TYPE_FLOAT_CTRL: {
          if ( cbor_is_float(item) )
    	lua_pushnumber(L, cbor_float_get_float(item));
          else {
    	switch ( cbor_ctrl_value(item) ) {
    	case CBOR_CTRL_NONE:
    	  lua_pushnil(L); //?
    	  break;
    	case CBOR_CTRL_FALSE:
    	  lua_pushboolean(L, false);
    	  break;
    	case CBOR_CTRL_TRUE:
    	  lua_pushboolean(L, true);
    	  break;
    	case CBOR_CTRL_NULL:
    	  lua_pushnil(L);
    	  break;
    	case CBOR_CTRL_UNDEF:
    	  lua_pushnil(L); //?
    	  break;
    	default:
    	  lua_pushnil(L); //?
    	}
          }
          break; }
        default:
          lua_pushnil(L); //?
      }
    }
    
    void lua_push_cbor(lua_State *L, cbor_item_t *obj) {
      lua_push_cbor_ex(L, obj, MAXDEPTH);
    }
    
    
    /* Converts a lua data to a cbor data
     * Tables are converted as an array if all key are numbers,
     * otherwise they are converted as cbor maps
     * Note: array intexes are shifted (lua starts at 1, cbor at 0) */
    cbor_item_t *lua_to_cbor_ex(lua_State *L, int idx, unsigned maxdepth) {
      if (!maxdepth--)
        return cbor_new_undef();
      switch (lua_type(L, idx)) {
        case LUA_TBOOLEAN:
          return cbor_build_bool(lua_toboolean(L, idx));
          break;
        case LUA_TNUMBER: {
          double d = lua_tonumber(L, idx);
          if (d==(long)d)
    	return xAAL_cbor_build_int(lua_tointeger(L, idx));
          else
    	return xAAL_cbor_build_float(d);
          break; }
        case LUA_TSTRING:
          return cbor_build_string(lua_tostring(L, idx));
          break;
        case LUA_TTABLE: {
          cbor_item_t *cmap, *carray, *cval;
          double d;
          int isnum;
          cmap = cbor_new_indefinite_map();
          carray = cbor_new_indefinite_array();
          lua_pushvalue(L, idx); // Copy reference of the table on top of the stack
          lua_pushnil(L);
          while (lua_next(L, -2)) {
    	cval = lua_to_cbor_ex(L, -1, maxdepth);
    	lua_pushvalue(L, -2); // copy 'key' to safely work on it
    	d = lua_tonumberx(L, -1, &isnum);
    	if ( isnum && ( d==(long)d ) )
    	  //cbor_array_set(carray, lua_tonumber(L, -1) - 1, cval);
    	  cbor_array_push(carray, cval);
    	else
    	  cbor_map_add(cmap, (struct cbor_pair){ cbor_move(lua_to_cbor_ex(L, -1, maxdepth)), cbor_move(cval) });
    	lua_pop(L, 2); // pop value + copy of key, leaving original key
          }
          lua_pop(L, 1); // Remove copy of the table
          // If cmap is empty, the the table was a pure array, return it
          if (cbor_map_size(cmap) == 0) {
    	cbor_decref(&cmap);
    	return carray;
          } else { // Else, insert carray content into cmap and return it
    	size_t i, len = cbor_array_size(carray);
    	for (i=0; i < len; i++)
    	  cbor_map_add(cmap, (struct cbor_pair){ cbor_move(xAAL_cbor_build_int(i+1)), cbor_move(cbor_array_get(carray, i)) });
    	cbor_decref(&carray);
    	return cmap;
          }
          break;}
        default:
          return cbor_new_null();
      }
    }
    
    cbor_item_t *lua_to_cbor(lua_State *L, int idx) {
      return lua_to_cbor_ex(L, idx, MAXDEPTH);
    }
    
    
    /* Converts a lua table of string (just values)
     * to a C array of string terminated by NULL */
    char **lua_tostringarray(lua_State *L, int idx) {
      char **ret;
      unsigned i = 0;
    
      if (!lua_istable(L, idx))
        return NULL;
      ret = (char **)malloc( (lua_rawlen(L,idx)+1) * sizeof(char*));
      lua_pushvalue(L, idx);
      lua_pushnil(L);
      while (lua_next(L, -2)) {
        ret[i++] = strdup(lua_tostring(L, -1));
        lua_pop(L, 1);
      }
      lua_pop(L, 1);
      ret[i] = NULL;
      return ret;
    }
    
    
    void print_uuid(const char *prompt, uuid_t *uuid) {
      if (uuid) {
        char str[37];
        uuid_unparse(*uuid, str);
        printf("%s: %s\n", prompt, str);
      } else
        printf("%s: none\n", prompt);
    }
    
    
    /* Converts a lua table of bytestring[16] to a C array of uuid */
    uuid_t *lua_to_wanted_sources(lua_State *L, int idx, size_t *nb) {
      uuid_t *wanted_sources;
      size_t len;
      const char *str;
      size_t table_len;
    
      *nb = 0;
    
      if (!lua_istable(L, idx))
        return NULL;
    
      table_len = lua_rawlen(L,idx);
      if (table_len == 0)
        return NULL;
    
      wanted_sources = (uuid_t*)malloc(table_len*sizeof(uuid_t));
      lua_pushvalue(L, idx);
      lua_pushnil(L);
      while (lua_next(L, -2)) {
        str = lua_tolstring(L, -1, &len);
        if (len == 16)
          uuid_copy(wanted_sources[(*nb)++], (unsigned char*)str);
        lua_pop(L, 1);
      }
      lua_pop(L, 1);
      return wanted_sources;
    }
    
    
    /* Converts a lua table of uuid to cbor */
    cbor_item_t *lua_to_cbor_targets(lua_State *L, int idx) {
      cbor_item_t *ctargets;
      const char *str;
      size_t len;
    
      if (!lua_istable(L, idx))
        return NULL;
    
      ctargets = cbor_new_definite_array(lua_rawlen(L,idx));
      lua_pushvalue(L, idx);
      lua_pushnil(L);
      while (lua_next(L, -2)) {
        str = lua_tolstring(L, -1, &len);
        if (len == 16)
          cbor_array_push(ctargets, cbor_move(cbor_build_bytestring((cbor_data)str, 16)));
        lua_pop(L, 1);
      }
      lua_pop(L, 1);
      return ctargets;
    }
    
    
    /* Gets an uuid, increments it by n (positive)
     * and produces the resulting uuid */
    void uuid_inc(uuid_t uu_dst, const uuid_t uu_src, unsigned n) {
      int i;
    
      for (i=15; i >= 0; i--) {
        n += uu_src[i];
        uu_dst[i] = n & 0xFF;
        n >>= 8;
      }
    }
    
    
    
    /* Helper for Lua script: xAAL_Lua_declare_device() */
    /* Expected parameter is a table with: addr(bytestring[16]) devType(string)
     *  vendorId(string) productId(string) version(string) url(string)
     *  info(string) unsupportedAttributes(table of strings)
     *  unsupportedMethods(idem) unsupportedNotifications (idem)
     * If success, it returns the address of the registred device (bytestring[16]).
     */
    int xAAL_Lua_declare_device(lua_State* L) {
      device_t *newdev;
      uuid_t addr;
      luactx_t *luactx;
    
      if (lua_gettop(L) != 1) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_declare_device(): expected one argument\n");
        return 0;
      }
      if (!lua_istable(L,1)) {
        fprintf(xAAL_error_log,"Error xAAL_Lua_declare_device(): expected a table as argument\n");
        return 0;
      }
    
      luactx = retrieve_luactx(p_luactxs, L);
      if (luactx == NULL) {
        fprintf(xAAL_error_log,"Error xAAL_Lua_declare_device(): invalid Lua context\n");
        return 0;
      }
    
      uuid_clear(addr);
      lua_getfield(L, 1, "addr");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1)) {
        size_t len;
        const char *str = lua_tolstring(L, -1, &len);
        if (len == 16)
          uuid_copy(addr, (unsigned char*)str);
      }
      lua_pop(L, 1);
    
      if (uuid_is_null(addr)) {
        if ( luactx->dev_inc == 0 )
          uuid_copy(addr, luactx->baseaddr);
        else
          uuid_inc(addr, luactx->baseaddr, luactx->dev_inc);
        newdev = NULL;
      } else {
        newdev = retrieve_device(p_embedded, &addr);
      }
    
      if (uuid_is_null(groupId))
        uuid_generate(groupId);
    
      if (newdev == NULL) {
        newdev = (device_t *)malloc(sizeof(device_t));
        newdev->luactx = luactx;
        uuid_copy(newdev->devinfo.addr, addr);
        xAAL_add_wanted_target(&addr, p_bus);
        newdev->devinfo.groupId = &groupId;
        newdev->devinfo.hwId = NULL;
        newdev->luactx->dev_inc++;
        newdev->wanted_sources = NULL;
        newdev->wanted_sources_nb = 0;
        newdev->wanted_devTypes = NULL;
        LIST_INSERT_HEAD(p_embedded, newdev, entries);
      }
    
      lua_getfield(L, 1, "devType");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.devType = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.devType = "basic.basic";
      lua_pop(L, 1);
    
      newdev->devinfo.alivemax = ALIVE_PERIOD * 2;
    
      lua_getfield(L, 1, "vendorId");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.vendorId = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.vendorId = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "productId");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.productId = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.productId = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "version");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.version = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.version = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "url");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.url = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.url = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "info");
      if (!lua_isnil(L, -1) && lua_isstring(L, -1))
        newdev->devinfo.info = strdup(lua_tostring(L, -1));
      else
        newdev->devinfo.info = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "unsupportedAttributes");
      if (!lua_isnil(L, -1) && lua_istable(L, -1))
        newdev->devinfo.unsupportedAttributes = lua_tostringarray(L, -1);
      else
        newdev->devinfo.unsupportedAttributes = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "unsupportedMethods");
      if (!lua_isnil(L, -1) && lua_istable(L, -1))
        newdev->devinfo.unsupportedMethods = lua_tostringarray(L, -1);
      else
        newdev->devinfo.unsupportedMethods = NULL;
      lua_pop(L, 1);
    
      lua_getfield(L, 1, "unsupportedNotifications");
      if (!lua_isnil(L, -1) && lua_istable(L, -1))
        newdev->devinfo.unsupportedNotifications = lua_tostringarray(L, -1);
      else
        newdev->devinfo.unsupportedNotifications = NULL;
      lua_pop(L, 1);
    
      lua_pushlstring(L, (char*)newdev->devinfo.addr, 16);
      return 1;
    }
    
    
    /* Helper for Lua script: xAAL_Lua_write_bus() */
    /* Expected parameters are:
     *   device_addr(bytestring[16]),
     *   msgType(unsigned),
     *   action(string)
     *   body(table or bytestring (i.e. already cbor serialized content) ),
     *   targets(table of bytestring[16])
     * Returns true if it success to send the message
     */
    int xAAL_Lua_write_bus(lua_State* L) {
      device_t *device;
      const char *action;
      xAAL_msgType_t msgType;
      cbor_item_t *cbody, *ctargets;
      bool r;
    
      if (lua_gettop(L) != 5) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected 5 arguments\n");
        return 0;
      }
    
      if (!lua_isstring(L,1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a bytestring[16] as argument 1 (device addr uuid)\n");
        return 0;
      } else {
        size_t len;
        const char *str = lua_tolstring(L, 1, &len);
        if (len == 16) {
          device = retrieve_device(p_embedded, (uuid_t*)str);
          if (device == NULL) {
    	fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): unknown device\n");
    	return 0;
          }
          if (device->luactx->L != L) {
    	fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): device out of scope of the Lua script\n");
    	return 0;
          }
        } else {
          fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a bytestring[16] as argument 1 (device addr uuid)\n");
          return 0;
        }
      }
    
      if (!lua_isnumber(L,2)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a number as argument 2 (msgType[0:notify|1:request|2:reply])\n");
        return 0;
      } else {
        msgType = lua_tointeger(L, 2);
        if (msgType<0 || msgType>xAAL_MSGTYPE_LAST) {
          fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): invalid msgType:%d\n", msgType);
          return 0;
        }
      }
    
      if (!lua_isstring(L,3)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a string as argument 3 (action)\n");
        return 0;
      } else {
        action = lua_tostring(L, 3);
      }
    
      if (lua_istable(L,4)) {
        if ( lua_rawlen(L, 4) == 0 )
          cbody = NULL;
        else
          cbody = lua_to_cbor(L, 4);
      } else if (lua_isstring(L,4)) {
        size_t len;
        const char *str = lua_tolstring(L, 4, &len);
        if (len) {
          struct cbor_load_result cresult;
          cbody = cbor_load((cbor_data)str, len, &cresult);
          if ( (cbody == NULL) && xAAL_error_log)
    	fprintf(xAAL_error_log,  "CBOR error; code %d, position %lu\n", cresult.error.code, cresult.error.position);
        } else {
          cbody = NULL;
        }
      } else {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a table or a string as argument 2 (body of the message)\n");
        return 0;
      }
    
      if (!lua_istable(L,5)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_write_bus(): expected a table as argument 3 (targets)\n");
        return 0;
      } else {
        if ( lua_rawlen(L, 5) == 0 )
          ctargets = NULL;
        else
          ctargets = lua_to_cbor_targets(L, 5);
      }
    
      r = xAAL_write_bus(p_bus, &(device->devinfo), msgType, action, cbody, ctargets);
      lua_pushboolean(L, r);
    
      return 1;
    }
    
    
    /* Helper for Lua script: xAAL_Lua_filter_broadcast_by_source() */
    /* Expected parameters are:
     *   address(string) of the device concerned by the filter
     *   a table of source addresses (string)
     * Returns nothing
     */
    int xAAL_Lua_filter_broadcast_by_source(lua_State* L) {
      device_t *device;
    
      if (lua_gettop(L) != 2) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_source(): expected 2 arguments\n");
        return 0;
      }
    
      if (!lua_isstring(L,1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_source(): expected a bytestring[16] as argument 1 (device addr uuid)\n");
        return 0;
      } else {
        size_t len;
        const char *str = lua_tolstring(L, 1, &len);
        device = (len == 16)? retrieve_device(p_embedded, (uuid_t*)str) : NULL;
        if (device == NULL) {
          fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_source(): unknown device\n");
          return 0;
        }
        if (device->luactx->L != L) {
          fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_source(): device out of scope of the Lua script\n");
          return 0;
        }
      }
    
      if (!lua_istable(L,2)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_source(): expected a table as argument 2 (sources)\n");
        return 0;
      } else {
        free(device->wanted_sources);
        if ( lua_rawlen(L, 2) == 0 )
          device->wanted_sources = NULL;
        else
          device->wanted_sources = lua_to_wanted_sources(L, 2, &device->wanted_sources_nb);
      }
    
      return 0;
    }
    
    
    /* Helper for Lua script: xAAL_Lua_filter_broadcast_by_devType() */
    /* Expected parameters are:
     *   address(string) of the device concerned by the filter
     *   a table of devTypes (string)
     * Returns nothing
     */
    int xAAL_Lua_filter_broadcast_by_devType(lua_State* L) {
      device_t *device;
    
      if (lua_gettop(L) != 2) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_devType(): expected 2 arguments\n");
        return 0;
      }
    
      if (!lua_isstring(L,1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_devType(): expected a bytestring[16] as argument 1 (device addr uuid)\n");
        return 0;
      } else {
        size_t len;
        const char *str = lua_tolstring(L, 1, &len);
        device = (len == 16)? retrieve_device(p_embedded, (uuid_t*)str) : NULL;
        if (device == NULL) {
          fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_devType(): unknown device\n");
          return 0;
        }
        if (device->luactx->L != L) {
          fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_devType(): device out of scope of the Lua script\n");
          return 0;
        }
      }
    
      if (!lua_istable(L,2)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_filter_broadcast_by_devType(): expected a table as argument 2 (devTypes)\n");
        return 0;
      } else {
        free(device->wanted_devTypes);
        if ( lua_rawlen(L, 2) == 0 )
          device->wanted_devTypes = NULL;
        else
          device->wanted_devTypes = lua_tostringarray(L, 2);
      }
    
      return 0;
    }
    
    
    /* Helper for Lua script: xAAL_Lua_set_alarm() */
    /* Expected parameters are:
     *   number of seconds to trigger the alarm
     *   the address(string) of the device concerned by the alarm
     *   a parameter(string) to be passed back while triggering the alarm
     * Returns nothing
     */
    int xAAL_Lua_set_alarm(lua_State* L) {
      int delay;
      const char *parameter, *str;
      uuid_t addr;
    
      if (lua_gettop(L) != 3) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_set_alarm(): expected 3 arguments\n");
        return 0;
      }
    
      if (!lua_isnumber(L, 1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_set_alarm(): expected a number as argument 1 (seconds)\n");
        return 0;
      } else
        delay = lua_tointeger(L, 1);
    
      if (!lua_isstring(L, 2)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_set_alarm(): expected a bytestring[16] as argument 2 (device address uuid)\n");
        return 0;
      } else {
        size_t len;
        str = lua_tolstring(L, 2, &len);
        if (len == 16)
          uuid_copy(addr, (const unsigned char*)str);
        else {
          fprintf(xAAL_error_log, "Error xAAL_Lua_set_alarm(): expected a bytestring[16] as argument 2 (device address uuid)\n");
          return 0;
        }
      }
    
      if (!lua_isstring(L, 3)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_set_alarm(): expected a string as argument 2 (parameter)\n");
        return 0;
      } else
        parameter = lua_tostring(L, 3);
    
      alarms_queue_add(delay, &addr, parameter, L, p_alarms, timerfd);
    
      return 0;
    }
    
    
    /*** My own minimal libuuid1 lua binding ***/
    
    /* Push a new uuid on the stack */
    int xAAL_Lua_uuid_generate(lua_State* L) {
      uuid_t out;
      uuid_generate(out);
      lua_pushlstring(L, (const char*)out, 16);
      return 1;
    }
    
    /* Expect a string of 36 chars, push an uuid */
    int xAAL_Lua_uuid_parse(lua_State* L) {
      const char *str;
      size_t len;
      uuid_t uuid;
    
      if (lua_gettop(L) != 1) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_parse(): expected 1 argument\n");
        return 0;
      }
      if (!lua_isstring(L, 1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_parse(): expected a string[36] as argument 1\n");
        return 0;
      }
      str = lua_tolstring(L, 1, &len);
      if (len != 36) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_parse(): expected a string[36] as argument 1\n");
        return 0;
      }
      if (uuid_parse(str, uuid) == -1) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_parse(): parsing error\n");
        return 0;
      }
      lua_pop(L, 1);
      lua_pushlstring(L, (const char*)uuid, 16);
      return 1;
    }
    
    /* Expect a bytestring[16], push an string */
    int xAAL_Lua_uuid_unparse(lua_State* L) {
      const char *str;
      size_t len;
      char uuid[37];
    
      if (lua_gettop(L) != 1) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_unparse(): expected 1 argument\n");
        return 0;
      }
      if (!lua_isstring(L, 1)) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_unparse(): expected a bytestring[16] as argument 1\n");
        return 0;
      }
      str = lua_tolstring(L, 1, &len);
      if (len != 16) {
        fprintf(xAAL_error_log, "Error xAAL_Lua_uuid_unparse(): expected a bytestring[16] as argument 1\n");
        return 0;
      }
      uuid_unparse((const unsigned char*)str, uuid);
      lua_pop(L, 1);
      lua_pushstring(L, uuid);
      return 1;
    }
    
    
    
    /* Add a Lua context in the list */
    void add_lua_context(luactxs_t *luactxs, const char *script,
    		     const char *baseaddr, const char *parameter) {
      luactx_t *luactx;
    
      luactx = (luactx_t*)malloc(sizeof(luactx_t));
      luactx->script = strdup(script);
      if ( !baseaddr || (uuid_parse(baseaddr, luactx->baseaddr) == -1) )
        uuid_generate(luactx->baseaddr);
      luactx->parameter = (parameter)? strdup(parameter) : NULL;
      luactx->dev_inc = 0;
      TAILQ_INSERT_TAIL(luactxs, luactx, entries);
    }
    
    
    
    /* Activates Lua contexts */
    void activate_lua_contexts(luactxs_t *luactxs, devices_t *embedded) {
      luactx_t *luactx;
    
      TAILQ_FOREACH(luactx, luactxs, entries) {
    
        luactx->L = luaL_newstate();
        luaL_openlibs(luactx->L); // TODO: replace it by a secure sandbox...
        // Provides environment info to the Lua script
        lua_pushlstring(luactx->L, (char*)luactx->baseaddr, 16);
        lua_setglobal(luactx->L, "xAAL_Lua_baseaddr");
        lua_pushlstring(luactx->L, (char*)groupId, 16);
        lua_setglobal(luactx->L, "xAAL_Lua_groupId");
        lua_pushstring(luactx->L, luactx->parameter);
        lua_setglobal(luactx->L, "xAAL_Lua_parameter");
    
        // Registers functions to be used by the Lua script
        lua_register(luactx->L, "xAAL_Lua_declare_device", xAAL_Lua_declare_device);
        lua_register(luactx->L, "xAAL_Lua_write_bus", xAAL_Lua_write_bus);
        lua_register(luactx->L, "xAAL_Lua_filter_broadcast_by_source", xAAL_Lua_filter_broadcast_by_source);
        lua_register(luactx->L, "xAAL_Lua_filter_broadcast_by_devType", xAAL_Lua_filter_broadcast_by_devType);
        lua_register(luactx->L, "xAAL_Lua_set_alarm", xAAL_Lua_set_alarm);
        lua_register(luactx->L, "xAAL_Lua_uuid_generate", xAAL_Lua_uuid_generate);
        lua_register(luactx->L, "xAAL_Lua_uuid_parse", xAAL_Lua_uuid_parse);
        lua_register(luactx->L, "xAAL_Lua_uuid_unparse", xAAL_Lua_uuid_unparse);
    
        // Starts the Lua script
        if ( luaL_dofile(luactx->L, luactx->script ) != 0 ) {
          device_t *device;
          fprintf(stderr, "Error: %s\n", lua_tostring(luactx->L,-1));
          LIST_FOREACH(device, embedded, entries)
    	if (device->luactx == luactx) {
    	  LIST_REMOVE(device, entries);
    	  free(device->wanted_sources);
    	  free(device->wanted_devTypes);
    	  free(device);
    	}
          TAILQ_REMOVE(luactxs, luactx, entries);
          lua_close(luactx->L);
          free(luactx->script);
          free(luactx->parameter);
          free(luactx);
        }
      }
    }
    
    
    
    
    /*****************/
    /* Chapter: xAAL */
    /*****************/
    
    
    void alive_sender(int sig) {
      device_t *device;
    
      LIST_FOREACH(device, p_embedded, entries)
        if ( !xAAL_notify_alive(p_bus, &(device->devinfo)) )
          fprintf(xAAL_error_log, "Could not send spontaneous alive notification.\n");
    
      alarm(ALIVE_PERIOD);
    }
    
    
    
    /* Manage broadcast filters */
    bool pass_filters(device_t *device, cbor_item_t *ctargets,
    		  const uuid_t *source, const char *devType) {
    
      if (cbor_array_size(ctargets) != 0)
        return true;
    
      if (device->wanted_sources) {
        if ( xAAL_uuids_get_item(device->wanted_sources, device->wanted_sources_nb, source) == -1 )
          return false;
      }
    
      if (device->wanted_devTypes) {
        char **item;
        for (item=device->wanted_devTypes; *item != NULL; item++)
          if (strcmp(*item, devType) == 0)
    	break;
        if (*item == NULL)
          return false;
      }
    
      return true;
    }
    
    
    /* Manage received message */
    void manage_msg(const xAAL_businfo_t *bus, devices_t *embedded) {
      cbor_item_t *cbody, *ctargets;
      char *devType, *action;
      xAAL_msgType_t msgType;
      uuid_t *source;
      device_t *device;
      bool call_lua;
    
      if (!xAAL_read_bus(bus, &ctargets, &source, &devType, &msgType, &action, &cbody))
        return;
    
      /* Check if it is for any embedded */
      LIST_FOREACH(device, embedded, entries) {
        if (xAAL_targets_match(ctargets, &device->devinfo.addr)) {
          call_lua = false;
          /* Stadards requests. Manage directly. */
          if (msgType == xAAL_REQUEST) {
    	if ( (strcmp(action, "isAlive") == 0)
    	     && xAAL_isAliveDevType_match(cbody, device->devinfo.devType) ) {
    	  if ( !xAAL_notify_alive(bus, &(device->devinfo)) )
    	    fprintf(xAAL_error_log, "Could not reply to isAlive\n");
    	} else if ( strcmp(action, "getDescription") == 0 ) {
    	  if ( !xAAL_reply_getDescription(bus, &(device->devinfo), source) )
    	    fprintf(xAAL_error_log, "Could not reply to getDescription\n");
    	} else
    	  call_lua = true;
          } else
    	call_lua = true;
          //
          call_lua &= pass_filters(device, ctargets, source, devType);
          //
          /* Pass message to Lua and call it */
          if (call_lua) {
    	lua_getglobal(device->luactx->L, "xAAL_Lua_receive_callback");
    	if ( !lua_isfunction(device->luactx->L, -1) ) {
    	  fprintf(xAAL_error_log,"Error: no xAAL_Lua_receive_callback() in %s\n", device->luactx->script);
    	  exit(EXIT_FAILURE);
    	} else {
    	  lua_pushlstring(device->luactx->L, (char*)device->devinfo.addr, 16);
    	  lua_pushlstring(device->luactx->L, (char*)source, 16);
    	  lua_pushstring(device->luactx->L, devType);
    	  lua_pushinteger(device->luactx->L, msgType);
    	  lua_pushstring(device->luactx->L, action);
    	  lua_push_cbor(device->luactx->L, cbody);
    	  lua_call(device->luactx->L, 6, 0);
    	}
          }
        }
      }
    
      xAAL_free_msg(ctargets, source, devType, action, cbody);
    }
    
    
    
    
    
    /*************************/
    /* Chapter: User Options */
    /*************************/
    
    /* Options from cmdline or conffile */
    typedef struct {
      char *addr;
      char *port;
      int hops;
      char *passphrase;
      char *conffile;
      bool immutable;
      bool daemon;
      char *logfile;
      char *pidfile;
      luactxs_t *luactxs;
    } options_t;
    
    
    /* Parse cmdline */
    void parse_cmdline(int argc, char **argv, options_t *opts) {
      int opt;
      bool arg_error = false;
    
      while ((opt = getopt(argc, argv, "a:p:h:s:g:c:idl:P:D:U:")) != -1) {
        switch (opt) {
          case 'a':
    	opts->addr = optarg;
    	break;
          case 'p':
    	opts->port = optarg;
    	break;
          case 'h':
    	opts->hops = atoi(optarg);
    	break;
          case 's':
    	opts->passphrase = optarg;
    	break;
          case 'g':
    	if ( uuid_parse(optarg, groupId) == -1 ) {
    	  fprintf(stderr, "Warning: invalid uuid '%s'\n", optarg);
    	  uuid_clear(groupId);
    	}
    	break;
          case 'c':
    	opts->conffile = optarg;
    	break;
          case 'i':
    	opts->immutable = true;
    	break;
          case 'd':
    	opts->daemon = true;
    	break;
          case 'l':
    	opts->logfile = optarg;
    	break;
          case 'P':
    	opts->pidfile = optarg;
    	break;
          default: /* '?' */
    	arg_error = true;
        }
      }
      while (optind < argc)
        add_lua_context(opts->luactxs, argv[optind++], NULL, NULL);
      if (arg_error) {
        fprintf(stderr, "Usage: %s [-a <addr>] [-p <port>] [-h <hops>] [-s <secret>] [-g <groupId>]\n"
    		"		[-c <conffile>] [-d] [-l <logfile>] [-P <pidfile>]\n"
    		"		[-D <dir>] [-U <baseurl>] [<scripts>...]\n"
    		"-a <addr>	multicast IPv4 or IPv6 address of the xAAL bus\n"
    		"-p <port>	UDP port of the xAAL bus\n"
    		"-h <hops>	Hops limit for multicast packets\n"
    		"-s <secret>	Secret passphrase\n"
    		"-g <groupId>	UUID of the group of automatons; random by default\n"
    		"-c <conffile>	Filename of the configuration file (cson format)\n"
    		"		Use 'schemory.conf' by default\n"
    		"-i		Immutable config file (do not re-write it)\n"
    		"-d		Start as a daemon\n"
    		"-l <logfile>	Filename to write errors; stderr by default\n"
    		"-P <pidfile>	Filename to write pid; none by default\n"
    		"<scripts>	Lua scripts to load\n", argv[0]);
        exit(EXIT_FAILURE);
      }
    }
    
    
    /* Read a config file (json format) */
    void read_config(options_t *opts) {
      struct json_object *jconf, *jaddr, *jport, *jhops, *jpassphrase, *jgroupid,
    		     *jconffile, *jimmutable, *jdaemon, *jlogfile, *jpidfile,
    		     *jautomata, *jautomaton, *jscript, *jbaseaddr, *jparameter;
      int auto_len, i;
      const char *script, *baseaddr, *parameter;
    
      /* read file */
      jconf = json_object_from_file(opts->conffile);
      if (json_object_is_type(jconf, json_type_null)) {
        fprintf(stderr, "Could not parse config file %s\n", opts->conffile);
        return;
      }
    
      /* parse bus addr */
      if (json_object_object_get_ex(jconf, "addr", &jaddr)
          && json_object_is_type(jaddr, json_type_string))
        opts->addr = strdup(json_object_get_string(jaddr));
    
      /* parse bus port */
      if (json_object_object_get_ex(jconf, "port", &jport)
          && json_object_is_type(jport, json_type_string))
        opts->port = strdup(json_object_get_string(jport));
    
      /* parse bus hops */
      if (json_object_object_get_ex(jconf, "hops", &jhops)
          && json_object_is_type(jhops, json_type_int))
        opts->hops = json_object_get_int(jhops);
    
      /* parse passphrase */
      if (json_object_object_get_ex(jconf, "passphrase", &jpassphrase)
          && json_object_is_type(jpassphrase, json_type_string))
        opts->passphrase = strdup(json_object_get_string(jpassphrase));
    
      /* parse schemory xAAL address (uuid) */
      if (json_object_object_get_ex(jconf, "groupId", &jgroupid)
          && json_object_is_type(jgroupid, json_type_string))
        if ( uuid_parse(json_object_get_string(jgroupid), groupId) == -1 )
          uuid_clear(groupId);
    
      /* parse config file name  */
      if (json_object_object_get_ex(jconf, "conffile", &jconffile)
          && json_object_is_type(jconffile, json_type_string))
        opts->conffile = strdup(json_object_get_string(jconffile));
    
      /* parse immutable flag  */
      if (json_object_object_get_ex(jconf, "immutable", &jimmutable)
          && json_object_is_type(jimmutable, json_type_boolean))
        opts->immutable = json_object_get_boolean(jimmutable);
    
      /* parse daemon flag  */
      if (json_object_object_get_ex(jconf, "daemon", &jdaemon)
          && json_object_is_type(jdaemon, json_type_boolean))
        opts->daemon = json_object_get_boolean(jdaemon);
    
      /* parse pid file name  */
      if (json_object_object_get_ex(jconf, "pidfile", &jpidfile)
          && json_object_is_type(jpidfile, json_type_string))
        opts->pidfile = strdup(json_object_get_string(jpidfile));
    
      /* parse log file name  */
      if (json_object_object_get_ex(jconf, "logfile", &jlogfile)
          && json_object_is_type(jlogfile, json_type_string))
        opts->logfile = strdup(json_object_get_string(jlogfile));
    
      /* parse automata list */
      if (json_object_object_get_ex(jconf, "automata", &jautomata)
          && json_object_is_type(jautomata, json_type_array)) {
        auto_len = json_object_array_length(jautomata);
        for (i=0; i<auto_len; i++) {
          jautomaton = json_object_array_get_idx(jautomata, i);
          if (json_object_is_type(jautomaton, json_type_object)
    	  && json_object_object_get_ex(jautomaton, "script", &jscript)
    	  && json_object_is_type(jscript, json_type_string)) {
    	script = json_object_get_string(jscript);
    	if (json_object_object_get_ex(jautomaton, "baseaddr", &jbaseaddr)
    	    && json_object_is_type(jbaseaddr, json_type_string))
    	  baseaddr = json_object_get_string(jbaseaddr);
    	if (json_object_object_get_ex(jautomaton, "parameter", &jparameter)
    	    && json_object_is_type(jparameter, json_type_string))
    	  parameter = json_object_get_string(jparameter);
    	else
    	  parameter = NULL;
    	add_lua_context(opts->luactxs, script, baseaddr, parameter);
          }
        }
      }
    
      json_object_put(jconf);
    }
    
    
    /* Re-write config file (json format) */
    void write_config(options_t *opts) {
      struct json_object *jconf, *jautomata, *jautomaton;
      luactx_t *luactx;
      char uuid[37];
    
      jconf = json_object_new_object();
      jconf = json_object_new_object();
      json_object_object_add(jconf, "addr",      json_object_new_string(opts->addr));
      json_object_object_add(jconf, "port",      json_object_new_string(opts->port));
      json_object_object_add(jconf, "hops",      json_object_new_int(opts->hops));
      json_object_object_add(jconf, "passphrase",json_object_new_string(opts->passphrase));
      uuid_unparse(groupId, uuid);
      json_object_object_add(jconf, "groupId",   json_object_new_string(uuid));
      json_object_object_add(jconf, "conffile",  json_object_new_string(opts->conffile));
      json_object_object_add(jconf, "immutable", json_object_new_boolean(opts->immutable));
      json_object_object_add(jconf, "daemon",    json_object_new_boolean(opts->daemon));
      if (opts->logfile)
        json_object_object_add(jconf, "logfile", json_object_new_string(opts->logfile));
      if (opts->pidfile)
        json_object_object_add(jconf, "pidfile", json_object_new_string(opts->pidfile));
    
      jautomata = json_object_new_array();
      TAILQ_FOREACH(luactx, opts->luactxs, entries) {
        jautomaton = json_object_new_object();
        json_object_object_add(jautomaton, "script", json_object_new_string(luactx->script));
        uuid_unparse(luactx->baseaddr, uuid);
        json_object_object_add(jautomaton, "baseaddr", json_object_new_string(uuid));
        if (luactx->parameter)
          json_object_object_add(jautomaton, "parameter", json_object_new_string(luactx->parameter));
        json_object_array_add(jautomata, jautomaton);
      }
      json_object_object_add(jconf, "automata", jautomata);
    
      if (json_object_to_file_ext(opts->conffile, jconf, JSON_C_TO_STRING_PRETTY
    			      | JSON_C_TO_STRING_SPACED) == -1)
        fprintf(xAAL_error_log, "Writing config file: %s\n", strerror(errno));
    
      json_object_put(jconf);
    }
    
    
    
    
    /*****************/
    /* Chapter: main */
    /*****************/
    
    /* Global variable for the handler */
    options_t *opts;
    
    /* Called at exit */
    void terminate() {
      if (!opts->immutable)
        write_config(opts);
      if (opts->pidfile)
        unlink(opts->pidfile);
    }
    
    /* Handler for Ctrl-C &co. */
    void cancel(int s) {
      terminate();
      exit(EXIT_SUCCESS);
    }
    
    
    
    
    /* Main */
    int main(int argc, char* argv[]) {
      devices_t embedded;
      xAAL_businfo_t bus;
      luactxs_t luactxs;
      alarms_t alarms;
      fd_set rfds, rfds_;
      int fd_max;
      uint64_t exp;
      options_t options = { .addr=NULL, .port=NULL, .hops=-1, .passphrase=NULL,
    			.conffile="automalua.conf",
    			.immutable=false, .daemon=false, .logfile=NULL,
    			.pidfile=NULL, .luactxs=&luactxs };
    
      uuid_clear(groupId);
      LIST_INIT(&embedded);
      TAILQ_INIT(&luactxs);
      CIRCLEQ_INIT(&alarms);
    
      p_embedded = &embedded;
      p_bus = &bus;
      p_luactxs = &luactxs;
      p_alarms = &alarms;
      timerfd = timerfd_create(CLOCK_REALTIME, 0);
      if (timerfd == -1)
        perror("Could not create timer for alarms");
    
      /* Parse cmdline arguments */
      parse_cmdline(argc, argv, &options);
    
      /* Load config */
      read_config(&options);
    
      /* Manage logfile */
      if (options.logfile) {
        xAAL_error_log = fopen(options.logfile, "a");
        if (xAAL_error_log == NULL) {
          perror("Opening logfile");
          xAAL_error_log = stderr;
        }
      } else
        xAAL_error_log = stderr;
    
      /* Join the xAAL bus */
      if ( !options.addr || !options.port || !options.passphrase) {
        fprintf(xAAL_error_log, "Please provide the address, the port and the passphrase of the xAAL bus.\n");
        exit(EXIT_FAILURE);
      } else if ( !xAAL_join_bus(options.addr, options.port, options.hops, 1, &bus) )
        exit(EXIT_FAILURE);
    
      /* Setup security of the bus */
      bus.maxAge = 2*60; /*seconds*/;
      bus.key = xAAL_pass2key(options.passphrase);
      if (bus.key == NULL) {
        fprintf(stderr, "Could not compute key from passphrase\n");
        exit(EXIT_FAILURE);
      }
    
      /* Start as a daemon */
      if (options.daemon && (daemon(1,1) == -1) )
        fprintf(xAAL_error_log, "daemon: %s\n", strerror(errno));
    
      /* Write pidfile */
      {
        FILE *pfile;
    
        if (options.pidfile) {
          pfile = fopen(options.pidfile, "w");
          if (pfile == NULL)
    	fprintf(xAAL_error_log, "Opening pidfile: %s\n", strerror(errno));
          else {
    	fprintf(pfile, "%d\n", getpid());
    	fclose(pfile);
          }
        }
      }
    
      /* Init Lua scripts */
      activate_lua_contexts(&luactxs, &embedded);
    
      /* Manage alive notifications */
      {
        struct sigaction act_alarm;
    
        act_alarm.sa_handler = alive_sender;
        act_alarm.sa_flags = ~SA_RESETHAND;
        sigemptyset(&act_alarm.sa_mask);
        sigaction(SIGALRM, &act_alarm, NULL);
        alive_sender(0);
      }
    
      /* Manage Ctrl-C &co. */
      opts = &options;
      signal(SIGHUP,  cancel);
      signal(SIGINT,  cancel);
      signal(SIGQUIT, cancel);
      signal(SIGTERM, cancel);
      atexit(terminate);
    
      /* prepare the fd set for the following select */
      FD_ZERO(&rfds);
      FD_SET(timerfd, &rfds);
      fd_max = timerfd;
      FD_SET(bus.sfd, &rfds);
      fd_max = (fd_max > bus.sfd)? fd_max : bus.sfd;
    
      /* main loop */
      for (;;) {
    
        rfds_ = rfds;
        if ( (select(fd_max+1, &rfds_, NULL, NULL, NULL) == -1) && (errno != EINTR) )
          fprintf(xAAL_error_log, "select: %s\n", strerror(errno));
    
        /* An xAAL message from the bus */
        if (FD_ISSET(bus.sfd, &rfds_))
          manage_msg(&bus, &embedded);
    
        /* It's time to trigger an alarm */
        if (FD_ISSET(timerfd, &rfds_)) {
          if ( read(timerfd, &exp, sizeof(uint64_t)) == -1 )
    	fprintf(xAAL_error_log, "Alarm timer: %s\n", strerror(errno));
          alarms_queue_run(&alarms, timerfd, &luactxs);
        }
      }
    }