diff --git a/src/include/66/graph.h b/src/include/66/graph.h
index 393256c427673b9e8e2a49514b95a04489aab829..93069234b062ae84846c39aeb2acf5bda7502c07 100644
--- a/src/include/66/graph.h
+++ b/src/include/66/graph.h
@@ -25,10 +25,9 @@
 #include <66/hash.h>
 
 extern void graph_build_tree(graph_t *g, struct resolve_hash_tree_s **htres, char const *base, resolve_tree_master_enum_t field) ;
-extern void graph_build_service(graph_t *g, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag) ;
+extern void graph_build_service(graph_t *g, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag) ;
 extern int graph_compute_dependencies(graph_t *g, char const *vertex, char const *edge, uint8_t requiredby) ;
-extern void graph_compute_visit(resolve_service_t *ares, unsigned int aresid, unsigned int *visit, unsigned int *list, graph_t *graph, unsigned int *nservice, uint8_t requiredby) ;
-
+extern void graph_compute_visit(struct resolve_hash_s hres, unsigned int *visit, unsigned int *list, graph_t *graph, unsigned int *nservice, uint8_t requiredby) ;
 extern int graph_build_service_bytree(graph_t *g, char const *tree, uint8_t what,  uint8_t is_supervised) ;
 extern int graph_build_service_bytree_from_src(graph_t *g, char const *src, uint8_t what) ;
 
diff --git a/src/include/66/hash.h b/src/include/66/hash.h
new file mode 100644
index 0000000000000000000000000000000000000000..3fb3d326c34ff66d2726ec887535d3f233104b2f
--- /dev/null
+++ b/src/include/66/hash.h
@@ -0,0 +1,1141 @@
+/*
+Copyright (c) 2003-2022, Troy D. Hanson  https://troydhanson.github.io/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef SS_HASH_H
+#define SS_HASH_H
+
+#define UTHASH_VERSION 2.3.0
+
+#include <string.h>   /* memcmp, memset, strlen */
+#include <stddef.h>   /* ptrdiff_t */
+#include <stdlib.h>   /* exit */
+
+#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT
+/* This codepath is provided for backward compatibility, but I plan to remove it. */
+#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead"
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT
+#else
+#include <stdint.h>   /* uint8_t, uint32_t */
+#endif
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER)   /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__MCST__)  /* Elbrus C Compiler */
+#define DECLTYPE(x) (__typeof(x))
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while (0)
+#endif
+
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
+#endif
+#ifndef uthash_bzero
+#define uthash_bzero(a,n) memset(a,'\0',n)
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+
+#ifndef HASH_FUNCTION
+#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)
+#endif
+
+#ifndef HASH_KEYCMP
+#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+#endif
+
+#ifndef HASH_NONFATAL_OOM
+#define HASH_NONFATAL_OOM 0
+#endif
+
+#if HASH_NONFATAL_OOM
+/* malloc failures can be recovered from */
+
+#ifndef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(obj) do {} while (0)    /* non-fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
+#define IF_HASH_NONFATAL_OOM(x) x
+
+#else
+/* malloc failures result in lost memory, hash tables are unusable */
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1)        /* fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
+#define IF_HASH_NONFATAL_OOM(x)
+
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_ROLLBACK_BKT(hh, head, itemptrhh)                                   \
+do {                                                                             \
+  struct UT_hash_handle *_hd_hh_item = (itemptrhh);                              \
+  unsigned _hd_bkt;                                                              \
+  HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);         \
+  (head)->hh.tbl->buckets[_hd_bkt].count++;                                      \
+  _hd_hh_item->hh_next = NULL;                                                   \
+  _hd_hh_item->hh_prev = NULL;                                                   \
+} while (0)
+
+#define HASH_VALUE(keyptr,keylen,hashv)                                          \
+do {                                                                             \
+  HASH_FUNCTION(keyptr, keylen, hashv);                                          \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
+do {                                                                             \
+  (out) = NULL;                                                                  \
+  if (head) {                                                                    \
+    unsigned _hf_bkt;                                                            \
+    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
+    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \
+      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  (out) = NULL;                                                                  \
+  if (head) {                                                                    \
+    unsigned _hf_hashv;                                                          \
+    HASH_VALUE(keyptr, keylen, _hf_hashv);                                       \
+    HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);             \
+  }                                                                              \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl,oomed)                                               \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!(tbl)->bloom_bv) {                                                        \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                           \
+    (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                     \
+  }                                                                              \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl,oomed)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head,oomed)                                           \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table));         \
+  if (!(head)->hh.tbl) {                                                         \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table));                         \
+    (head)->hh.tbl->tail = &((head)->hh);                                        \
+    (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                      \
+    (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;            \
+    (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                  \
+    (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                    \
+        HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));               \
+    (head)->hh.tbl->signature = HASH_SIGNATURE;                                  \
+    if (!(head)->hh.tbl->buckets) {                                              \
+      HASH_RECORD_OOM(oomed);                                                    \
+      uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                        \
+    } else {                                                                     \
+      uthash_bzero((head)->hh.tbl->buckets,                                      \
+          HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));             \
+      HASH_BLOOM_MAKE((head)->hh.tbl, oomed);                                    \
+      IF_HASH_NONFATAL_OOM(                                                      \
+        if (oomed) {                                                             \
+          uthash_free((head)->hh.tbl->buckets,                                   \
+              HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));           \
+          uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                    \
+        }                                                                        \
+      )                                                                          \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+    HASH_DELETE(hh, head, replaced);                                             \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do {                                                                             \
+  (replaced) = NULL;                                                             \
+  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+  if (replaced) {                                                                \
+    HASH_DELETE(hh, head, replaced);                                             \
+  }                                                                              \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
+do {                                                                             \
+  unsigned _hr_hashv;                                                            \
+  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
+  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add)                                          \
+do {                                                                             \
+  (add)->hh.next = NULL;                                                         \
+  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
+  (head)->hh.tbl->tail->next = (add);                                            \
+  (head)->hh.tbl->tail = &((add)->hh);                                           \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \
+do {                                                                             \
+  do {                                                                           \
+    if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) {                             \
+      break;                                                                     \
+    }                                                                            \
+  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \
+do {                                                                             \
+  char *_hs_saved_head = (char*)(head);                                          \
+  do {                                                                           \
+    DECLTYPE_ASSIGN(head, _hs_iter);                                             \
+    if (cmpfcn(head, add) > 0) {                                                 \
+      DECLTYPE_ASSIGN(head, _hs_saved_head);                                     \
+      break;                                                                     \
+    }                                                                            \
+    DECLTYPE_ASSIGN(head, _hs_saved_head);                                       \
+  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \
+} while (0)
+#endif
+
+#if HASH_NONFATAL_OOM
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \
+do {                                                                             \
+  if (!(oomed)) {                                                                \
+    unsigned _ha_bkt;                                                            \
+    (head)->hh.tbl->num_items++;                                                 \
+    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                  \
+    HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);    \
+    if (oomed) {                                                                 \
+      HASH_ROLLBACK_BKT(hh, head, &(add)->hh);                                   \
+      HASH_DELETE_HH(hh, head, &(add)->hh);                                      \
+      (add)->hh.tbl = NULL;                                                      \
+      uthash_nonfatal_oom(add);                                                  \
+    } else {                                                                     \
+      HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                   \
+      HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                \
+    }                                                                            \
+  } else {                                                                       \
+    (add)->hh.tbl = NULL;                                                        \
+    uthash_nonfatal_oom(add);                                                    \
+  }                                                                              \
+} while (0)
+
+#else
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \
+do {                                                                             \
+  unsigned _ha_bkt;                                                              \
+  (head)->hh.tbl->num_items++;                                                   \
+  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \
+  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);      \
+  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \
+  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
+} while (0)
+
+#endif
+
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do {                                                                             \
+  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (char*) (keyptr);                                              \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \
+    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \
+      (head) = (add);                                                            \
+    IF_HASH_NONFATAL_OOM( } )                                                    \
+  } else {                                                                       \
+    void *_hs_iter = (head);                                                     \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn);                                 \
+    if (_hs_iter) {                                                              \
+      (add)->hh.next = _hs_iter;                                                 \
+      if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) {     \
+        HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add);              \
+      } else {                                                                   \
+        (head) = (add);                                                          \
+      }                                                                          \
+      HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add);                      \
+    } else {                                                                     \
+      HASH_APPEND_LIST(hh, head, add);                                           \
+    }                                                                            \
+  }                                                                              \
+  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \
+  HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER");                    \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
+do {                                                                             \
+  unsigned _hs_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
+  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
+do {                                                                             \
+  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \
+  (add)->hh.hashv = (hashval);                                                   \
+  (add)->hh.key = (const void*) (keyptr);                                        \
+  (add)->hh.keylen = (unsigned) (keylen_in);                                     \
+  if (!(head)) {                                                                 \
+    (add)->hh.next = NULL;                                                       \
+    (add)->hh.prev = NULL;                                                       \
+    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \
+    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \
+      (head) = (add);                                                            \
+    IF_HASH_NONFATAL_OOM( } )                                                    \
+  } else {                                                                       \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_APPEND_LIST(hh, head, add);                                             \
+  }                                                                              \
+  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \
+  HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE");                            \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+  unsigned _ha_hashv;                                                            \
+  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
+  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1U));                                           \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+    HASH_DELETE_HH(hh, head, &(delptr)->hh)
+
+#define HASH_DELETE_HH(hh,head,delptrhh)                                         \
+do {                                                                             \
+  const struct UT_hash_handle *_hd_hh_del = (delptrhh);                          \
+  if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) {                \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket));    \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head) = NULL;                                                               \
+  } else {                                                                       \
+    unsigned _hd_bkt;                                                            \
+    if (_hd_hh_del == (head)->hh.tbl->tail) {                                    \
+      (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev);     \
+    }                                                                            \
+    if (_hd_hh_del->prev != NULL) {                                              \
+      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next;   \
+    } else {                                                                     \
+      DECLTYPE_ASSIGN(head, _hd_hh_del->next);                                   \
+    }                                                                            \
+    if (_hd_hh_del->next != NULL) {                                              \
+      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev;   \
+    }                                                                            \
+    HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);        \
+    HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);               \
+    (head)->hh.tbl->num_items--;                                                 \
+  }                                                                              \
+  HASH_FSCK(hh, head, "HASH_DELETE_HH");                                         \
+} while (0)
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+do {                                                                             \
+    unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr);            \
+    HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out);                     \
+} while (0)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+do {                                                                             \
+    unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield);    \
+    HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add);                  \
+} while (0)
+#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
+do {                                                                             \
+    unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield);    \
+    HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced);    \
+} while (0)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \
+    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#include <stdio.h>   /* fprintf, stderr */
+#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head,where)                                                 \
+do {                                                                             \
+  struct UT_hash_handle *_thh;                                                   \
+  if (head) {                                                                    \
+    unsigned _bkt_i;                                                             \
+    unsigned _count = 0;                                                         \
+    char *_prev;                                                                 \
+    for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) {           \
+      unsigned _bkt_count = 0;                                                   \
+      _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                            \
+      _prev = NULL;                                                              \
+      while (_thh) {                                                             \
+        if (_prev != (char*)(_thh->hh_prev)) {                                   \
+          HASH_OOPS("%s: invalid hh_prev %p, actual %p\n",                       \
+              (where), (void*)_thh->hh_prev, (void*)_prev);                      \
+        }                                                                        \
+        _bkt_count++;                                                            \
+        _prev = (char*)(_thh);                                                   \
+        _thh = _thh->hh_next;                                                    \
+      }                                                                          \
+      _count += _bkt_count;                                                      \
+      if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {                \
+        HASH_OOPS("%s: invalid bucket count %u, actual %u\n",                    \
+            (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);         \
+      }                                                                          \
+    }                                                                            \
+    if (_count != (head)->hh.tbl->num_items) {                                   \
+      HASH_OOPS("%s: invalid hh item count %u, actual %u\n",                     \
+          (where), (head)->hh.tbl->num_items, _count);                           \
+    }                                                                            \
+    _count = 0;                                                                  \
+    _prev = NULL;                                                                \
+    _thh =  &(head)->hh;                                                         \
+    while (_thh) {                                                               \
+      _count++;                                                                  \
+      if (_prev != (char*)_thh->prev) {                                          \
+        HASH_OOPS("%s: invalid prev %p, actual %p\n",                            \
+            (where), (void*)_thh->prev, (void*)_prev);                           \
+      }                                                                          \
+      _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                         \
+      _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL);     \
+    }                                                                            \
+    if (_count != (head)->hh.tbl->num_items) {                                   \
+      HASH_OOPS("%s: invalid app item count %u, actual %u\n",                    \
+          (where), (head)->hh.tbl->num_items, _count);                           \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+#else
+#define HASH_FSCK(hh,head,where)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+  unsigned _klen = fieldlen;                                                     \
+  write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                  \
+  write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                        \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hb_keylen = (unsigned)keylen;                                        \
+  const unsigned char *_hb_key = (const unsigned char*)(key);                    \
+  (hashv) = 0;                                                                   \
+  while (_hb_keylen-- != 0U) {                                                   \
+    (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                           \
+  }                                                                              \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx
+ * (archive link: https://archive.is/Ivcan )
+ */
+#define HASH_SAX(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _sx_i;                                                                \
+  const unsigned char *_hs_key = (const unsigned char*)(key);                    \
+  hashv = 0;                                                                     \
+  for (_sx_i=0; _sx_i < keylen; _sx_i++) {                                       \
+    hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                       \
+  }                                                                              \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _fn_i;                                                                \
+  const unsigned char *_hf_key = (const unsigned char*)(key);                    \
+  (hashv) = 2166136261U;                                                         \
+  for (_fn_i=0; _fn_i < keylen; _fn_i++) {                                       \
+    hashv = hashv ^ _hf_key[_fn_i];                                              \
+    hashv = hashv * 16777619U;                                                   \
+  }                                                                              \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _ho_i;                                                                \
+  const unsigned char *_ho_key=(const unsigned char*)(key);                      \
+  hashv = 0;                                                                     \
+  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
+      hashv += _ho_key[_ho_i];                                                   \
+      hashv += (hashv << 10);                                                    \
+      hashv ^= (hashv >> 6);                                                     \
+  }                                                                              \
+  hashv += (hashv << 3);                                                         \
+  hashv ^= (hashv >> 11);                                                        \
+  hashv += (hashv << 15);                                                        \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c)                                                      \
+do {                                                                             \
+  a -= b; a -= c; a ^= ( c >> 13 );                                              \
+  b -= c; b -= a; b ^= ( a << 8 );                                               \
+  c -= a; c -= b; c ^= ( b >> 13 );                                              \
+  a -= b; a -= c; a ^= ( c >> 12 );                                              \
+  b -= c; b -= a; b ^= ( a << 16 );                                              \
+  c -= a; c -= b; c ^= ( b >> 5 );                                               \
+  a -= b; a -= c; a ^= ( c >> 3 );                                               \
+  b -= c; b -= a; b ^= ( a << 10 );                                              \
+  c -= a; c -= b; c ^= ( b >> 15 );                                              \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned _hj_i,_hj_j,_hj_k;                                                    \
+  unsigned const char *_hj_key=(unsigned const char*)(key);                      \
+  hashv = 0xfeedbeefu;                                                           \
+  _hj_i = _hj_j = 0x9e3779b9u;                                                   \
+  _hj_k = (unsigned)(keylen);                                                    \
+  while (_hj_k >= 12U) {                                                         \
+    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
+        + ( (unsigned)_hj_key[2] << 16 )                                         \
+        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
+    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
+        + ( (unsigned)_hj_key[6] << 16 )                                         \
+        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
+    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
+        + ( (unsigned)_hj_key[10] << 16 )                                        \
+        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
+                                                                                 \
+     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
+                                                                                 \
+     _hj_key += 12;                                                              \
+     _hj_k -= 12U;                                                               \
+  }                                                                              \
+  hashv += (unsigned)(keylen);                                                   \
+  switch ( _hj_k ) {                                                             \
+    case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */         \
+    case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */         \
+    case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */         \
+    case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */         \
+    case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */         \
+    case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */         \
+    case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */         \
+    case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */         \
+    case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */         \
+    case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */         \
+    case 1:  _hj_i += _hj_key[0];                      /* FALLTHROUGH */         \
+    default: ;                                                                   \
+  }                                                                              \
+  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv)                                               \
+do {                                                                             \
+  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
+  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
+                                                                                 \
+  unsigned _sfh_rem = _sfh_len & 3U;                                             \
+  _sfh_len >>= 2;                                                                \
+  hashv = 0xcafebabeu;                                                           \
+                                                                                 \
+  /* Main loop */                                                                \
+  for (;_sfh_len > 0U; _sfh_len--) {                                             \
+    hashv    += get16bits (_sfh_key);                                            \
+    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \
+    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
+    _sfh_key += 2U*sizeof (uint16_t);                                            \
+    hashv    += hashv >> 11;                                                     \
+  }                                                                              \
+                                                                                 \
+  /* Handle end cases */                                                         \
+  switch (_sfh_rem) {                                                            \
+    case 3: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 16;                                                \
+            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \
+            hashv += hashv >> 11;                                                \
+            break;                                                               \
+    case 2: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 11;                                                \
+            hashv += hashv >> 17;                                                \
+            break;                                                               \
+    case 1: hashv += *_sfh_key;                                                  \
+            hashv ^= hashv << 10;                                                \
+            hashv += hashv >> 1;                                                 \
+            break;                                                               \
+    default: ;                                                                   \
+  }                                                                              \
+                                                                                 \
+  /* Force "avalanching" of final 127 bits */                                    \
+  hashv ^= hashv << 3;                                                           \
+  hashv += hashv >> 5;                                                           \
+  hashv ^= hashv << 4;                                                           \
+  hashv += hashv >> 17;                                                          \
+  hashv ^= hashv << 25;                                                          \
+  hashv += hashv >> 6;                                                           \
+} while (0)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
+do {                                                                             \
+  if ((head).hh_head != NULL) {                                                  \
+    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
+  } else {                                                                       \
+    (out) = NULL;                                                                \
+  }                                                                              \
+  while ((out) != NULL) {                                                        \
+    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
+      if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) {                  \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+    if ((out)->hh.hh_next != NULL) {                                             \
+      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
+    } else {                                                                     \
+      (out) = NULL;                                                              \
+    }                                                                            \
+  }                                                                              \
+} while (0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,hh,addhh,oomed)                                     \
+do {                                                                             \
+  UT_hash_bucket *_ha_head = &(head);                                            \
+  _ha_head->count++;                                                             \
+  (addhh)->hh_next = _ha_head->hh_head;                                          \
+  (addhh)->hh_prev = NULL;                                                       \
+  if (_ha_head->hh_head != NULL) {                                               \
+    _ha_head->hh_head->hh_prev = (addhh);                                        \
+  }                                                                              \
+  _ha_head->hh_head = (addhh);                                                   \
+  if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
+      && !(addhh)->tbl->noexpand) {                                              \
+    HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed);                              \
+    IF_HASH_NONFATAL_OOM(                                                        \
+      if (oomed) {                                                               \
+        HASH_DEL_IN_BKT(head,addhh);                                             \
+      }                                                                          \
+    )                                                                            \
+  }                                                                              \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(head,delhh)                                              \
+do {                                                                             \
+  UT_hash_bucket *_hd_head = &(head);                                            \
+  _hd_head->count--;                                                             \
+  if (_hd_head->hh_head == (delhh)) {                                            \
+    _hd_head->hh_head = (delhh)->hh_next;                                        \
+  }                                                                              \
+  if ((delhh)->hh_prev) {                                                        \
+    (delhh)->hh_prev->hh_next = (delhh)->hh_next;                                \
+  }                                                                              \
+  if ((delhh)->hh_next) {                                                        \
+    (delhh)->hh_next->hh_prev = (delhh)->hh_prev;                                \
+  }                                                                              \
+} while (0)
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(hh,tbl,oomed)                                        \
+do {                                                                             \
+  unsigned _he_bkt;                                                              \
+  unsigned _he_bkt_i;                                                            \
+  struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                   \
+  UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                  \
+  _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                              \
+           sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);             \
+  if (!_he_new_buckets) {                                                        \
+    HASH_RECORD_OOM(oomed);                                                      \
+  } else {                                                                       \
+    uthash_bzero(_he_new_buckets,                                                \
+        sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);                \
+    (tbl)->ideal_chain_maxlen =                                                  \
+       ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) +                      \
+       ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);    \
+    (tbl)->nonideal_items = 0;                                                   \
+    for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) {           \
+      _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head;                             \
+      while (_he_thh != NULL) {                                                  \
+        _he_hh_nxt = _he_thh->hh_next;                                           \
+        HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt);           \
+        _he_newbkt = &(_he_new_buckets[_he_bkt]);                                \
+        if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) {                 \
+          (tbl)->nonideal_items++;                                               \
+          if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \
+            _he_newbkt->expand_mult++;                                           \
+          }                                                                      \
+        }                                                                        \
+        _he_thh->hh_prev = NULL;                                                 \
+        _he_thh->hh_next = _he_newbkt->hh_head;                                  \
+        if (_he_newbkt->hh_head != NULL) {                                       \
+          _he_newbkt->hh_head->hh_prev = _he_thh;                                \
+        }                                                                        \
+        _he_newbkt->hh_head = _he_thh;                                           \
+        _he_thh = _he_hh_nxt;                                                    \
+      }                                                                          \
+    }                                                                            \
+    uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+    (tbl)->num_buckets *= 2U;                                                    \
+    (tbl)->log2_num_buckets++;                                                   \
+    (tbl)->buckets = _he_new_buckets;                                            \
+    (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ?   \
+        ((tbl)->ineff_expands+1U) : 0U;                                          \
+    if ((tbl)->ineff_expands > 1U) {                                             \
+      (tbl)->noexpand = 1;                                                       \
+      uthash_noexpand_fyi(tbl);                                                  \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+  }                                                                              \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head != NULL) {                                                            \
+    _hs_insize = 1;                                                              \
+    _hs_looping = 1;                                                             \
+    _hs_list = &((head)->hh);                                                    \
+    while (_hs_looping != 0U) {                                                  \
+      _hs_p = _hs_list;                                                          \
+      _hs_list = NULL;                                                           \
+      _hs_tail = NULL;                                                           \
+      _hs_nmerges = 0;                                                           \
+      while (_hs_p != NULL) {                                                    \
+        _hs_nmerges++;                                                           \
+        _hs_q = _hs_p;                                                           \
+        _hs_psize = 0;                                                           \
+        for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) {                           \
+          _hs_psize++;                                                           \
+          _hs_q = ((_hs_q->next != NULL) ?                                       \
+            HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                   \
+          if (_hs_q == NULL) {                                                   \
+            break;                                                               \
+          }                                                                      \
+        }                                                                        \
+        _hs_qsize = _hs_insize;                                                  \
+        while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) {    \
+          if (_hs_psize == 0U) {                                                 \
+            _hs_e = _hs_q;                                                       \
+            _hs_q = ((_hs_q->next != NULL) ?                                     \
+              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \
+            _hs_qsize--;                                                         \
+          } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) {                     \
+            _hs_e = _hs_p;                                                       \
+            if (_hs_p != NULL) {                                                 \
+              _hs_p = ((_hs_p->next != NULL) ?                                   \
+                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \
+            }                                                                    \
+            _hs_psize--;                                                         \
+          } else if ((cmpfcn(                                                    \
+                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)),             \
+                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q))              \
+                )) <= 0) {                                                       \
+            _hs_e = _hs_p;                                                       \
+            if (_hs_p != NULL) {                                                 \
+              _hs_p = ((_hs_p->next != NULL) ?                                   \
+                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \
+            }                                                                    \
+            _hs_psize--;                                                         \
+          } else {                                                               \
+            _hs_e = _hs_q;                                                       \
+            _hs_q = ((_hs_q->next != NULL) ?                                     \
+              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \
+            _hs_qsize--;                                                         \
+          }                                                                      \
+          if ( _hs_tail != NULL ) {                                              \
+            _hs_tail->next = ((_hs_e != NULL) ?                                  \
+              ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL);                       \
+          } else {                                                               \
+            _hs_list = _hs_e;                                                    \
+          }                                                                      \
+          if (_hs_e != NULL) {                                                   \
+            _hs_e->prev = ((_hs_tail != NULL) ?                                  \
+              ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL);                    \
+          }                                                                      \
+          _hs_tail = _hs_e;                                                      \
+        }                                                                        \
+        _hs_p = _hs_q;                                                           \
+      }                                                                          \
+      if (_hs_tail != NULL) {                                                    \
+        _hs_tail->next = NULL;                                                   \
+      }                                                                          \
+      if (_hs_nmerges <= 1U) {                                                   \
+        _hs_looping = 0;                                                         \
+        (head)->hh.tbl->tail = _hs_tail;                                         \
+        DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list));           \
+      }                                                                          \
+      _hs_insize *= 2U;                                                          \
+    }                                                                            \
+    HASH_FSCK(hh, head, "HASH_SRT");                                             \
+  }                                                                              \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt = NULL, *_elt;                                                 \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if ((src) != NULL) {                                                           \
+    for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {    \
+      for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;               \
+        _src_hh != NULL;                                                         \
+        _src_hh = _src_hh->hh_next) {                                            \
+        _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                         \
+        if (cond(_elt)) {                                                        \
+          IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; )                             \
+          _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho);          \
+          _dst_hh->key = _src_hh->key;                                           \
+          _dst_hh->keylen = _src_hh->keylen;                                     \
+          _dst_hh->hashv = _src_hh->hashv;                                       \
+          _dst_hh->prev = _last_elt;                                             \
+          _dst_hh->next = NULL;                                                  \
+          if (_last_elt_hh != NULL) {                                            \
+            _last_elt_hh->next = _elt;                                           \
+          }                                                                      \
+          if ((dst) == NULL) {                                                   \
+            DECLTYPE_ASSIGN(dst, _elt);                                          \
+            HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed);                             \
+            IF_HASH_NONFATAL_OOM(                                                \
+              if (_hs_oomed) {                                                   \
+                uthash_nonfatal_oom(_elt);                                       \
+                (dst) = NULL;                                                    \
+                continue;                                                        \
+              }                                                                  \
+            )                                                                    \
+          } else {                                                               \
+            _dst_hh->tbl = (dst)->hh_dst.tbl;                                    \
+          }                                                                      \
+          HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);      \
+          HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
+          (dst)->hh_dst.tbl->num_items++;                                        \
+          IF_HASH_NONFATAL_OOM(                                                  \
+            if (_hs_oomed) {                                                     \
+              HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh);                           \
+              HASH_DELETE_HH(hh_dst, dst, _dst_hh);                              \
+              _dst_hh->tbl = NULL;                                               \
+              uthash_nonfatal_oom(_elt);                                         \
+              continue;                                                          \
+            }                                                                    \
+          )                                                                      \
+          HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv);                          \
+          _last_elt = _elt;                                                      \
+          _last_elt_hh = _dst_hh;                                                \
+        }                                                                        \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst, dst, "HASH_SELECT");                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if ((head) != NULL) {                                                          \
+    HASH_BLOOM_FREE((head)->hh.tbl);                                             \
+    uthash_free((head)->hh.tbl->buckets,                                         \
+                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
+    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
+    (head) = NULL;                                                               \
+  }                                                                              \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head)                                                   \
+ (((head) != NULL) ? (                                                           \
+ (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
+          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
+           sizeof(UT_hash_table)                                   +             \
+           (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp)                                                \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
+  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered).
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often.
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   const void *key;                  /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+
+#endif
diff --git a/src/include/66/module.h b/src/include/66/module.h
index 1f9c7ced72696e457c93a79107464f7995038bcc..bc0e16224be2074a54d824838507fa80dbd5dbaf 100644
--- a/src/include/66/module.h
+++ b/src/include/66/module.h
@@ -35,7 +35,7 @@
 #define SS_MODULE_REQUIREDBY "/requiredby"
 #define SS_MODULE_REQUIREDBY_LEN (sizeof SS_MODULE_REQUIREDBY - 1)
 
-extern void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force) ;
+extern void parse_module(resolve_service_t *res, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force) ;
 extern void parse_module_check_dir(char const *src,char const *dir) ;
 extern void parse_module_check_name(char const *src, char const *name) ;
 
diff --git a/src/include/66/parse.h b/src/include/66/parse.h
index 83b0e8371151358f782ed2c542bf85149ae4e63a..5022e928d4d954e339f1517496214beeed4f09bd 100644
--- a/src/include/66/parse.h
+++ b/src/include/66/parse.h
@@ -47,9 +47,10 @@ extern void parse_cleanup(resolve_service_t *res, char const *tmpdir, uint8_t fo
 /** main */
 extern void start_parser(char const *sv, ssexec_t *info, uint8_t disable_module, char const *directory_forced) ;
 
-extern void parse_service(char const *sv, ssexec_t *info, uint8_t force, uint8_t conf) ;
-extern int parse_frontend(char const *sv, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree) ;
-extern int parse_interdependences(char const *service, char const *list, unsigned int listlen, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree) ;
+extern void parse_service(struct resolve_hash_s **href, char const *sv, ssexec_t *info, uint8_t force, uint8_t conf) ;
+extern int parse_frontend(char const *sv, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree) ;
+extern int parse_interdependences(char const *service, char const *list, unsigned int listlen, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree) ;
+extern void parse_append_logger(struct resolve_hash_s **hres, resolve_service_t *res, ssexec_t *info) ;
 
 /** split */
 extern int parse_section(stralloc *secname, char const *str, size_t *pos) ;
@@ -74,13 +75,19 @@ extern int parse_clean_list(stralloc *sa, char const *str) ;
 extern int parse_clean_line(char *str)  ;
 extern int parse_mandatory(resolve_service_t *res) ;
 extern void parse_error(int ierr, int idsec, int idkey) ;
-extern void parse_rename_interdependences(resolve_service_t *res, char const *prefix, resolve_service_t *ares, unsigned int *areslen) ;
+extern void parse_rename_interdependences(resolve_service_t *res, char const *prefix, struct resolve_hash_s **hres, ssexec_t *info) ;
 extern void parse_db_migrate(resolve_service_t *res, ssexec_t *info) ;
 
 /** module */
-extern void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force) ;
+extern void parse_module(resolve_service_t *res, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force) ;
 
 /** resolve */
-extern void parse_compute_resolve(unsigned int idx, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info) ;
+extern void parse_compute_resolve(resolve_service_t *res, ssexec_t *info) ;
+extern uint32_t compute_src_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info) ;
+extern uint32_t compute_live_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info) ;
+extern uint32_t compute_status(resolve_wrapper_t_ref wres, ssexec_t *info) ;
+extern uint32_t compute_scan_dir(resolve_wrapper_t_ref wres, ssexec_t *info) ;
+extern uint32_t compute_state_dir(resolve_wrapper_t_ref wres, ssexec_t *info, char const *folder) ;
+extern uint32_t compute_pipe_service(resolve_wrapper_t_ref wres, ssexec_t *info, char const *name) ;
 
 #endif
diff --git a/src/include/66/sanitize.h b/src/include/66/sanitize.h
index d5cd05ec3615f28abfb6b3be94eb655091fe815c..47d14c6df7643e2b8dba9b3bf061b31c4e182910 100644
--- a/src/include/66/sanitize.h
+++ b/src/include/66/sanitize.h
@@ -27,7 +27,7 @@ extern void sanitize_source(char const *name, ssexec_t *info, uint32_t flag) ;
 extern int sanitize_fdholder(resolve_service_t *res, ss_state_t *sta, uint32_t flag, uint8_t init) ;
 extern int sanitize_livestate(resolve_service_t *res, ss_state_t *sta) ;
 extern int sanitize_scandir(resolve_service_t *res, ss_state_t *sta) ;
-extern void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_service_t *ares, unsigned int areslen) ;
+extern void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, struct resolve_hash_s **hres) ;
 extern void sanitize_graph(ssexec_t *info) ;
 /** @Return 0 the service is already written
  * @Return 1 the service will be overwritten
diff --git a/src/include/66/service.h b/src/include/66/service.h
index 81c42137bb652f3bc0a121c4d7f1b9de960edb68..3ff38f5b97833ba0ee3d8db62220acb07d745583 100644
--- a/src/include/66/service.h
+++ b/src/include/66/service.h
@@ -25,6 +25,7 @@
 
 #include <66/ssexec.h>
 #include <66/resolve.h>
+#include <66/hash.h>
 
 typedef struct resolve_service_addon_path_s resolve_service_addon_path_t, *resolve_service_addon_path_t_ref ;
 struct resolve_service_addon_path_s
@@ -302,6 +303,15 @@ enum resolve_service_enum_e
 
 } ;
 
+struct resolve_hash_s {
+	char name[SS_MAX_SERVICE_NAME + 1] ; // name as key
+	uint8_t visit ;
+	resolve_service_t res ;
+	UT_hash_handle hh ;
+
+} ;
+#define RESOLVE_HASH_ZERO { 0, 0, RESOLVE_SERVICE_ZERO, NULL }
+
 extern resolve_field_table_t resolve_service_field_table[] ;
 
 extern int service_cmp_basedir(char const *dir) ;
@@ -310,8 +320,6 @@ extern int service_frontend_path(stralloc *sasrc,char const *sv, uid_t owner,cha
 extern int service_frontend_src(stralloc *sasrc, char const *name, char const *src, char const **exclude) ;
 extern int service_is_g(char const *name, uint32_t flag) ;
 extern int service_get_treename(char *atree, char const *name, uint32_t flag) ;
-extern void service_resolve_array_free(resolve_service_t *ares, unsigned int areslen) ;
-extern int service_resolve_array_search(resolve_service_t *ares, unsigned int areslen, char const *name) ;
 extern int service_resolve_copy(resolve_service_t *dst, resolve_service_t *res) ;
 extern int service_resolve_get_field_tosa(stralloc *sa, resolve_service_t *res, resolve_service_enum_t field) ;
 extern int service_resolve_modify_field(resolve_service_t *res, resolve_service_enum_t field, char const *data) ;
@@ -319,7 +327,7 @@ extern int service_resolve_read_cdb(cdb *c, resolve_service_t *res) ;
 extern void service_resolve_write(resolve_service_t *res) ;
 extern void service_resolve_write_remote(resolve_service_t *res, char const *dst, uint8_t force) ;
 extern int service_resolve_write_cdb(cdbmaker *c, resolve_service_t *sres) ;
-extern void service_enable_disable(graph_t *g, unsigned int idx, resolve_service_t *ares, unsigned int areslen, uint8_t action, unsigned int *visit, uint8_t propagate, ssexec_t *info) ;
+extern void service_enable_disable(graph_t *g, struct resolve_hash_s *hash, struct resolve_hash_s **hres, uint8_t action, uint8_t propagate, ssexec_t *info) ;
 extern void service_switch_tree(resolve_service_t *res, char const *base, char const *totreename, ssexec_t *info) ;
 extern void service_db_migrate(resolve_service_t *old, resolve_service_t *new, char const *base, uint8_t requiredby) ;
 /* avoid circular dependencies by prototyping the ss_state_t instead
@@ -327,9 +335,14 @@ extern void service_db_migrate(resolve_service_t *old, resolve_service_t *new, c
 typedef struct ss_state_s ss_state_t, *ss_state_t_ref ;
 
 /** Graph */
-extern void service_graph_g(char const *slist, size_t slen, graph_t *graph, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag) ;
-extern void service_graph_collect(graph_t *g, char const *alist, size_t alen, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag) ;
-extern void service_graph_build(graph_t *g, resolve_service_t *ares, unsigned int areslen, uint32_t flag) ;
-
+extern void service_graph_g(char const *slist, size_t slen, graph_t *graph, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag) ;
+extern void service_graph_collect(graph_t *g, char const *alist, size_t alen, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag) ;
+extern void service_graph_build(graph_t *g, struct resolve_hash_s **hres, uint32_t flag) ;
+
+/** Hash */
+extern int hash_add(struct resolve_hash_s **hres, char const *name, resolve_service_t res) ;
+extern struct resolve_hash_s *hash_search(struct resolve_hash_s **hres, char const *name) ;
+extern int hash_count(struct resolve_hash_s **hres) ;
+extern void hash_free(struct resolve_hash_s **hres) ;
 
 #endif
diff --git a/src/include/66/svc.h b/src/include/66/svc.h
index d378f0452a24e491efe3f0e650386dc8c51061ed..33e9bb1321eafe4f532d18fb761130e9048bb1fa 100644
--- a/src/include/66/svc.h
+++ b/src/include/66/svc.h
@@ -40,7 +40,7 @@ struct pidservice_s
 {
     int pipe[2] ;
     pid_t pid ;
-    int aresid ; // id at array ares
+    resolve_service_t *res ; // resolve of service through hash
     unsigned int vertex ; // id at graph_hash_t struct
     uint8_t state ;
     int nedge ;
@@ -54,7 +54,7 @@ struct pidservice_s
 #define PIDSERVICE_ZERO { \
     .pipe[0] = -1, \
     .pipe[1] = -1, \
-    .aresid = -1, \
+    .res = NULL, \
     .vertex = -1, \
     .state = 0, \
     .nedge =  0, \
@@ -63,13 +63,13 @@ struct pidservice_s
     .notif = { 0 } \
 }
 
-extern void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, uint8_t requiredby, uint32_t flag) ;
-extern int svc_launch(pidservice_t *apids, unsigned int napid, uint8_t what, graph_t *graph, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate) ;
-extern int svc_compute_ns(resolve_service_t *ares, unsigned int areslen, unsigned int aresid, uint8_t what, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate, pidservice_t *apids, unsigned int napids) ;
+extern void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, struct resolve_hash_s **hres, ssexec_t *info, uint8_t requiredby, uint32_t flag) ;
+extern int svc_launch(pidservice_t *apids, unsigned int napid, uint8_t what, graph_t *graph, struct resolve_hash_s **hres, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate) ;
+extern int svc_compute_ns(resolve_service_t *res, uint8_t what, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate, pidservice_t *apids, unsigned int napids) ;
 extern int svc_scandir_ok (char const *dir) ;
 extern int svc_scandir_send(char const *scandir,char const *signal) ;
 extern int svc_send_wait(char const *const *list, unsigned int nservice, char **sig, unsigned int siglen, ssexec_t *info) ;
-extern void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info) ;
+extern void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, struct resolve_hash_s **hres, ssexec_t *info) ;
 extern void svc_send_fdholder(char const *socket, char const *signal) ;
 
 #endif
diff --git a/src/lib66/exec/ssexec_disable.c b/src/lib66/exec/ssexec_disable.c
index a1cbbfc9c8bb132303088bd16651aa69dea7c986..5a57cfe3802bd7e4ccd9fae246f3d3f11ec1590d 100644
--- a/src/lib66/exec/ssexec_disable.c
+++ b/src/lib66/exec/ssexec_disable.c
@@ -18,10 +18,8 @@
 #include <oblibs/log.h>
 #include <oblibs/types.h>
 #include <oblibs/string.h>
-#include <oblibs/sastr.h>
 
 #include <skalibs/sgetopt.h>
-#include <skalibs/stralloc.h>
 
 #include <66/constants.h>
 #include <66/ssexec.h>
@@ -40,15 +38,12 @@ int ssexec_disable(int argc, char const *const *argv, ssexec_t *info)
     int n = 0, e = 1 ;
     size_t pos = 0 ;
     graph_t graph = GRAPH_ZERO ;
-    stralloc sa = STRALLOC_ZERO ;
+    struct resolve_hash_s *hres = NULL ;
+    struct resolve_hash_s tostop[argc] ;
 
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
-    unsigned int visit[SS_MAX_SERVICE + 1] ;
+    memset(tostop, 0, sizeof(struct resolve_hash_s) * argc) ;
 
-    memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
-
     {
         subgetopt l = SUBGETOPT_ZERO ;
 
@@ -95,27 +90,28 @@ int ssexec_disable(int argc, char const *const *argv, ssexec_t *info)
         log_usage(info->usage, "\n", info->help) ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- try to parse it first") ;
 
     for (; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", argv[n], " not available -- did you parse it?") ;
 
-        service_enable_disable(&graph, aresid, ares, areslen, 0, visit, propagate, info) ;
+        service_enable_disable(&graph, hash, &hres, 0, propagate, info) ;
 
-        if (!sastr_add_string(&sa, argv[n]))
-            log_dieu(LOG_EXIT_SYS, "add string") ;
+        tostop[n] = *hash ;
     }
 
-    if (stop && sa.len) {
+    graph_free_all(&graph) ;
+    e = 0 ;
+
+    if (stop && n) {
 
-        size_t len = sastr_nelement(&sa) ;
-        int nargc = 3 + len ;
+        int nargc = 3 + n ;
         char const *prog = PROG ;
         char const *newargv[nargc] ;
         unsigned int m = 0 ;
@@ -128,8 +124,8 @@ int ssexec_disable(int argc, char const *const *argv, ssexec_t *info)
 
         newargv[m++] = "stop" ;
         newargv[m++] = "-u" ;
-        FOREACH_SASTR(&sa, pos)
-            newargv[m++] = sa.s + pos ;
+        for (; pos < n ; pos++)
+            newargv[m++] = tostop[pos].name ;
         newargv[m] = 0 ;
 
         PROG = "stop" ;
@@ -138,15 +134,9 @@ int ssexec_disable(int argc, char const *const *argv, ssexec_t *info)
 
         info->help = help ;
         info->usage = usage ;
-
-        goto end ;
     }
-    e = 0 ;
 
-    end:
-        stralloc_free(&sa) ;
-        service_resolve_array_free(ares, areslen) ;
-        graph_free_all(&graph) ;
+    hash_free(&hres) ;
 
     return e ;
 }
diff --git a/src/lib66/exec/ssexec_enable.c b/src/lib66/exec/ssexec_enable.c
index 837ba083a2604af5f30e122e1c421944f18e4402..9fda2a90365e8ec908228adfbff4d62869f1b87a 100644
--- a/src/lib66/exec/ssexec_enable.c
+++ b/src/lib66/exec/ssexec_enable.c
@@ -16,7 +16,6 @@
 
 #include <oblibs/log.h>
 #include <oblibs/types.h> // FLAGS
-#include <oblibs/stack.h>
 
 #include <skalibs/sgetopt.h>
 
@@ -37,14 +36,12 @@ int ssexec_enable(int argc, char const *const *argv, ssexec_t *info)
     int n = 0, e = 1 ;
     size_t pos = 0 ;
     graph_t graph = GRAPH_ZERO ;
+    struct resolve_hash_s *hres = NULL ;
+    struct resolve_hash_s tostart[argc] ;
 
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
-    unsigned int visit[SS_MAX_SERVICE + 1] ;
+    memset(tostart, 0, sizeof(struct resolve_hash_s) * argc) ;
 
-    memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
-
     {
         subgetopt l = SUBGETOPT_ZERO ;
 
@@ -80,52 +77,46 @@ int ssexec_enable(int argc, char const *const *argv, ssexec_t *info)
     if (argc < 1)
         log_usage(info->usage, "\n", info->help) ;
 
-    _init_stack_(stk, argc * SS_MAX_TREENAME) ;
-
     for(; n < argc ; n++)
         sanitize_source(argv[n], info, flag) ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
 
     for (n = 0 ; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", argv[n], " not available -- did you parse it?") ;
 
-        service_enable_disable(&graph, aresid, ares, areslen, 1, visit, propagate, info) ;
+        service_enable_disable(&graph, hash, &hres, 1, propagate, info) ;
 
         if (info->opt_tree) {
 
-            service_switch_tree(&ares[aresid], info->base.s, info->treename.s, info) ;
+            service_switch_tree(&hash->res, info->base.s, info->treename.s, info) ;
 
-            if (ares[aresid].logger.want && ares[aresid].type == TYPE_CLASSIC) {
+            if (hash->res.logger.want && hash->res.type == TYPE_CLASSIC) {
 
-                int logid = service_resolve_array_search(ares, areslen, ares[aresid].sa.s + ares[aresid].logger.name) ;
-                if (logid < 0)
-                    log_die(LOG_EXIT_USER, "service: ", ares[aresid].sa.s + ares[aresid].logger.name, " not available -- please make a bug report") ;
+                struct resolve_hash_s *log = hash_search(&hres, hash->res.sa.s + hash->res.logger.name) ;
+                if (log == NULL)
+                    log_die(LOG_EXIT_USER, "service: ", hash->res.sa.s + hash->res.logger.name, " not available -- please make a bug report") ;
 
-                service_switch_tree(&ares[logid], info->base.s, info->treename.s, info) ;
+                service_switch_tree(&log->res, info->base.s, info->treename.s, info) ;
             }
         }
 
-        if (!stack_add_g(&stk, argv[n]))
-            log_dieu(LOG_EXIT_SYS, "add string") ;
+        tostart[n] = *hash ;
     }
 
-    service_resolve_array_free(ares, areslen) ;
     graph_free_all(&graph) ;
-
     e = 0 ;
 
-    if (start && stk.len) {
+    if (start && n) {
 
-        size_t len = stack_count_element(&stk) ;
-        int nargc = 2 + len ;
+        int nargc = 2 + n ;
         char const *prog = PROG ;
         char const *newargv[nargc] ;
         unsigned int m = 0 ;
@@ -137,8 +128,8 @@ int ssexec_enable(int argc, char const *const *argv, ssexec_t *info)
         info->usage = usage_start ;
 
         newargv[m++] = "start" ;
-        FOREACH_STK(&stk, pos)
-            newargv[m++] = stk.s + pos ;
+        for (; pos < n ; pos++)
+            newargv[m++] = tostart[pos].name ;
         newargv[m] = 0 ;
 
         PROG = "start" ;
@@ -149,5 +140,7 @@ int ssexec_enable(int argc, char const *const *argv, ssexec_t *info)
         info->usage = usage ;
     }
 
+    hash_free(&hres) ;
+
     return e ;
 }
diff --git a/src/lib66/exec/ssexec_parse.c b/src/lib66/exec/ssexec_parse.c
index 316268e9d3ad3f73427cf6b7a9aa4384186d4d4b..b5d5311b246dc5f4eaa53b79cfc6312b46a8e034 100644
--- a/src/lib66/exec/ssexec_parse.c
+++ b/src/lib66/exec/ssexec_parse.c
@@ -113,13 +113,15 @@ int ssexec_parse(int argc, char const *const *argv, ssexec_t *info)
          * service can be a directory name. In this case
          * we parse all services inside. */
         size_t pos = 0 ;
+        struct resolve_hash_s *hres = NULL ;
         FOREACH_SASTR(&sa, pos)
-            parse_service(sa.s + pos, info, force, conf) ;
-    }
+            parse_service(&hres, sa.s + pos, info, force, conf) ;
 
-    sanitize_graph(info) ;
+        hash_free(&hres) ;
+    }
 
     stralloc_free(&sa) ;
+    sanitize_graph(info) ;
 
     return 0 ;
 }
diff --git a/src/lib66/exec/ssexec_reconfigure.c b/src/lib66/exec/ssexec_reconfigure.c
index dee2e491a682f70ba6f2114955ffdf4e3a306c2d..2cd26764402e217ea528e5a311f6d5cc5c6049f6 100644
--- a/src/lib66/exec/ssexec_reconfigure.c
+++ b/src/lib66/exec/ssexec_reconfigure.c
@@ -17,7 +17,6 @@
 #include <oblibs/log.h>
 #include <oblibs/types.h>
 #include <oblibs/graph.h>
-#include <oblibs/stack.h>
 #include <oblibs/string.h>
 
 #include <skalibs/sgetopt.h>
@@ -32,7 +31,6 @@
 #include <66/service.h>
 #include <66/constants.h>
 #include <66/tree.h>
-#include <skalibs/djbunix.h>
 
 int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
 {
@@ -42,17 +40,17 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
     uint32_t flag = 0 ;
     uint8_t siglen = 0, issupervised = 0 ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], tostate[SS_MAX_SERVICE + 1], ntostate = 0, nservice = 0, n = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
+    resolve_service_t_ref pres = 0 ;
+    struct resolve_hash_s tostate[argc] ;
+    struct resolve_hash_s toenable[argc] ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], ntostate = 0, ntoenable = 0, nservice = 0, n = 0 ;
     ss_state_t sta = STATE_ZERO ;
-    resolve_service_t res = RESOLVE_SERVICE_ZERO ;
-    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, &res) ;
 
-    _init_stack_(stk, argc * SS_MAX_SERVICE_NAME) ;
     memset(list, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
-    memset(tostate, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
+    memset(tostate, 0, argc * sizeof(struct resolve_hash_s)) ;
+    memset(toenable, 0, argc * sizeof(struct resolve_hash_s)) ;
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_TOPARSE|STATE_FLAGS_WANTUP) ;
 
     {
@@ -87,38 +85,38 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
         log_usage(info->usage, "\n", info->help) ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- have you already parsed a service?") ;
 
     for (; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", argv[n], " not available -- did you parse it?") ;
 
-        char status[strlen(ares[aresid].sa.s + ares[aresid].path.servicedir) + SS_STATE_LEN + 1] ;
+        pres = &hash->res ;
+        char status[strlen(pres->sa.s + pres->path.servicedir) + SS_STATE_LEN + 1] ;
 
-        auto_strings(status, ares[aresid].sa.s + ares[aresid].path.servicedir, SS_STATE) ;
+        auto_strings(status, pres->sa.s + pres->path.servicedir, SS_STATE) ;
 
-        if (!state_read(&sta, &ares[aresid]))
+        if (!state_read(&sta, pres))
             log_dieu(LOG_EXIT_SYS, "read state file of: ", argv[n]) ;
 
         sta.toparse = STATE_FLAGS_TRUE ;
 
-        if (!state_write(&sta, &ares[aresid]))
+        if (!state_write(&sta, pres))
             log_dieusys(LOG_EXIT_SYS, "write status file of: ", argv[n]) ;
 
         /** need to reverse the previous state change to
          * for current live service.*/
-        tostate[ntostate++] = aresid ;
+        tostate[ntostate++] = *hash ;
 
         issupervised = sta.issupervised == STATE_FLAGS_TRUE ? 1 : 0 ;
 
-        if (ares[aresid].enabled && !ares[aresid].inns)
-            if (!stack_add_g(&stk, argv[n]))
-                log_dieu(LOG_EXIT_SYS, "add string") ;
+        if (pres->enabled && !pres->inns)
+            toenable[ntoenable++] = *hash ;
 
         if (!issupervised) {
             /* only force to parse it again */
@@ -131,7 +129,7 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
 
             /** services of group boot cannot be restarted, the changes will appear only at
              * next reboot.*/
-            r = tree_ongroups(ares[aresid].sa.s + ares[aresid].path.home, ares[aresid].sa.s + ares[aresid].treename, TREE_GROUPS_BOOT) ;
+            r = tree_ongroups(pres->sa.s + pres->path.home, pres->sa.s + pres->treename, TREE_GROUPS_BOOT) ;
 
             if (r < 0)
                 log_dieu(LOG_EXIT_SYS, "get groups of service: ", argv[n]) ;
@@ -139,7 +137,7 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
             if (r)
                 continue ;
 
-            graph_compute_visit(ares, aresid, visit, list, &graph, &nservice, 1) ;
+            graph_compute_visit(*hash, visit, list, &graph, &nservice, 1) ;
         }
     }
 
@@ -208,12 +206,12 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
 
         /** live of the service still exist.
          * Reverse to the previous state of the toparse flag. */
-        if (state_read_remote(&sta, ares[tostate[n]].sa.s + ares[tostate[n]].live.statedir)) {
+        if (state_read_remote(&sta, tostate[n].res.sa.s + tostate[n].res.live.statedir)) {
 
             sta.toparse = STATE_FLAGS_FALSE ;
 
-            if (!state_write_remote(&sta, ares[tostate[n]].sa.s + ares[tostate[n]].live.statedir))
-                log_warnusys("write status file of: ", ares[tostate[n]].sa.s + ares[tostate[n]].live.statedir) ;
+            if (!state_write_remote(&sta, tostate[n].res.sa.s + tostate[n].res.live.statedir))
+                log_warnusys("write status file of: ", tostate[n].res.sa.s + tostate[n].res.live.statedir) ;
         }
     }
 
@@ -251,11 +249,11 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
         info->usage = usage ;
     }
 
-    if (stk.len) {
+    if (ntoenable) {
 
         /** enable again the service if it was enabled */
         unsigned int m = 0 ;
-        int nargc = 2 + stk.count ;
+        int nargc = 2 + ntoenable ;
         char const *prog = PROG ;
         char const *newargv[nargc] ;
 
@@ -268,10 +266,10 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
         newargv[m++] = "enable" ;
 
         n = 0 ;
-        FOREACH_STK(&stk, n) {
+        for (; n < ntoenable ; n++) {
 
-            char *name = stk.s + n ;
-            if (get_rstrlen_until(name,SS_LOG_SUFFIX) < 0)
+            char *name = toenable[n].name ;
+             if (get_rstrlen_until(name,SS_LOG_SUFFIX) < 0)
                 newargv[m++] = name ;
         }
 
@@ -286,10 +284,8 @@ int ssexec_reconfigure(int argc, char const *const *argv, ssexec_t *info)
     }
 
     freed:
-        resolve_free(wres) ;
-        service_resolve_array_free(ares, areslen) ;
+        hash_free(&hres) ;
         graph_free_all(&graph) ;
 
     return e ;
-
 }
diff --git a/src/lib66/exec/ssexec_reload.c b/src/lib66/exec/ssexec_reload.c
index bda2d1ac2bf9b182da4367ac86cf5170f86ae423..3ebfbb1d2127b4790fcd1d0bd4335483426bb20b 100644
--- a/src/lib66/exec/ssexec_reload.c
+++ b/src/lib66/exec/ssexec_reload.c
@@ -37,9 +37,8 @@ int ssexec_reload(int argc, char const *const *argv, ssexec_t *info)
     uint32_t flag = 0 ;
     uint8_t siglen = 2 ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0, m = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
+    unsigned int m = 0 ;
 
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
 
@@ -105,20 +104,20 @@ int ssexec_reload(int argc, char const *const *argv, ssexec_t *info)
     }
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
 
     for (n = 0 ; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", *argv, " not available -- did you pars it?") ;
 
-        if (ares[aresid].type == TYPE_ONESHOT) {
+        if (hash->res.type == TYPE_ONESHOT) {
             nargc++ ;
-            nargv[m++] = ares[aresid].sa.s + ares[aresid].name ;
+            nargv[m++] = hash->res.sa.s + hash->res.name ;
         }
     }
 
@@ -171,7 +170,7 @@ int ssexec_reload(int argc, char const *const *argv, ssexec_t *info)
     }
 
     err:
-        service_resolve_array_free(ares, areslen) ;
+        hash_free(&hres) ;
         graph_free_all(&graph) ;
 
         return r ;
diff --git a/src/lib66/exec/ssexec_remove.c b/src/lib66/exec/ssexec_remove.c
index d84de2cebf24b4d55b7156ca1286c1ad4ff897d4..27fa39919de97f43c3ecfc9360c117acc5f57a23 100644
--- a/src/lib66/exec/ssexec_remove.c
+++ b/src/lib66/exec/ssexec_remove.c
@@ -38,6 +38,7 @@
 #include <66/constants.h>
 #include <66/svc.h>
 #include <66/utils.h>
+#include <66/hash.h>
 
 static void auto_remove(char const *path)
 {
@@ -46,7 +47,7 @@ static void auto_remove(char const *path)
         log_dieusys(LOG_EXIT_SYS, "remove directory: ", path) ;
 }
 
-static void remove_deps(resolve_service_t *res, resolve_service_t *ares, unsigned int *areslen, stralloc *sa, ssexec_t *info)
+static void remove_deps(resolve_service_t *res, struct resolve_hash_s **hres, stralloc *sa, ssexec_t *info)
 {
 
     if (!res->dependencies.nrequiredby)
@@ -82,10 +83,12 @@ static void remove_deps(resolve_service_t *res, resolve_service_t *ares, unsigne
             if (!sastr_add_string(sa, stk.s + pos))
                 log_dieusys(LOG_EXIT_SYS, "add service: ", stk.s + pos, " to selection") ;
 
-        ares[(*areslen)++] = dres ;
+        log_trace("add service: ", stk.s + pos, " to the service selection") ;
+        if (!hash_add(hres, stk.s + pos, dres))
+            log_dieu(LOG_EXIT_SYS, "append service selection with: ", stk.s + pos) ;
 
         if (dres.dependencies.nrequiredby)
-            remove_deps(&dres, ares, areslen, sa, info) ;
+            remove_deps(&dres, hres, sa, info) ;
     }
 
     free(wres) ;
@@ -109,9 +112,10 @@ static void remove_service(resolve_service_t *res, ssexec_t *info)
         resolve_wrapper_t_ref lwres = resolve_set_struct(DATA_SERVICE, &lres) ;
 
         r = resolve_read_g(lwres, info->base.s, res->sa.s + res->logger.name) ;
-        if (r <= 0)
-            log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", res->sa.s + res->logger.name) ;
-
+        if (r <= 0) {
+            log_warnusys("read resolve file of: ", res->sa.s + res->logger.name, " -- ignoring it") ;
+            goto end ;
+        }
         auto_remove(lres.sa.s + lres.path.servicedir) ;
 
         auto_remove(lres.sa.s + lres.logger.destination) ;
@@ -130,7 +134,7 @@ static void remove_service(resolve_service_t *res, ssexec_t *info)
 
         resolve_free(lwres) ;
     }
-
+    end:
     auto_strings(sym, res->sa.s + res->path.home, SS_SYSTEM, SS_RESOLVE, SS_SERVICE, "/", res->sa.s + res->name) ;
 
     log_trace("remove symlink: ", sym) ;
@@ -150,10 +154,10 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
     size_t pos = 0 ;
     uint32_t flag = 0 ;
     uint8_t siglen = 0 ;
-    unsigned int areslen = 0 ;
     ss_state_t ste = STATE_ZERO ;
     stralloc sa = STRALLOC_ZERO ;
     resolve_wrapper_t_ref wres = 0 ;
+    struct resolve_hash_s *hres = NULL, *c, *tmp ;
 
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_ISSUPERVISED|STATE_FLAGS_TOUNSUPERVISE|STATE_FLAGS_WANTDOWN) ;
 
@@ -189,9 +193,6 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
     if (argc < 1)
         log_usage(info->usage, "\n", info->help) ;
 
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
-    memset(ares, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
-
     for(; pos < argc ; pos++) {
 
         resolve_service_t res = RESOLVE_SERVICE_ZERO ;
@@ -220,10 +221,12 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
                     log_dieusys(LOG_EXIT_SYS, "add service: ", argv[pos], " to selection") ;
         }
 
-        ares[areslen++] = res ;
+        log_trace("add service: ", argv[pos], " to the service selection") ;
+        if (!hash_add(&hres, argv[pos], res))
+            log_dieu(LOG_EXIT_SYS, "append service selection with: ", argv[pos]) ;
 
         if (!siglen)
-            remove_deps(&res, ares, &areslen, &sa, info) ;
+            remove_deps(&res, &hres, &sa, info) ;
     }
 
     r = svc_scandir_ok(info->scandir.s) ;
@@ -263,18 +266,18 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
         info->usage = usage ;
     }
 
-    for (pos = 0 ; pos < areslen ; pos++) {
+    HASH_ITER(hh, hres, c, tmp) {
 
-        remove_service(&ares[pos], info) ;
+        remove_service(&c->res, info) ;
 
-        if (ares[pos].type == TYPE_MODULE) {
+        if (c->res.type == TYPE_MODULE) {
 
-            if (ares[pos].dependencies.ncontents) {
+            if (c->res.dependencies.ncontents) {
 
                 size_t pos = 0 ;
                 resolve_service_t mres = RESOLVE_SERVICE_ZERO ;
                 resolve_wrapper_t_ref mwres = resolve_set_struct(DATA_SERVICE, &mres) ;
-                _init_stack_(stk, strlen(ares[pos].sa.s + ares[pos].dependencies.contents)) ;
+                _init_stack_(stk, strlen(c->res.sa.s + c->res.dependencies.contents)) ;
 
                 if (!stack_clean_string_g(&stk, c->res.sa.s + c->res.dependencies.contents))
                     log_dieu(LOG_EXIT_SYS, "convert string") ;
@@ -282,8 +285,10 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
                 FOREACH_STK(&stk, pos) {
 
                     r = resolve_read_g(mwres, info->base.s, stk.s + pos) ;
-                    if (r <= 0)
-                        log_dieusys(LOG_EXIT_SYS, "read resolve file of: ", stk.s + pos) ;
+                    if (r <= 0) {
+                        log_warnusys("read resolve file of: ", stk.s + pos) ;
+                        continue ;
+                    }
 
                     remove_service(&mres, info) ;
                 }
@@ -296,7 +301,7 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
 
             if (!info->owner) {
 
-                auto_strings(dir, SS_SERVICE_ADMDIR, ares[pos].sa.s + ares[pos].name) ;
+                auto_strings(dir, SS_SERVICE_ADMDIR, c->res.sa.s + c->res.name) ;
 
             } else {
 
@@ -305,7 +310,7 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
 
                 size_t dirlen = strlen(dir) ;
 
-                auto_strings(dir + dirlen, SS_SERVICE_USERDIR, ares[pos].sa.s + ares[pos].name) ;
+                auto_strings(dir + dirlen, SS_SERVICE_USERDIR, c->res.sa.s + c->res.name) ;
             }
 
             auto_remove(dir) ;
@@ -313,7 +318,7 @@ int ssexec_remove(int argc, char const *const *argv, ssexec_t *info)
     }
 
     stralloc_free(&sa) ;
-    service_resolve_array_free(ares, areslen) ;
+    hash_free(&hres) ;
     free(wres) ;
 
     return 0 ;
diff --git a/src/lib66/exec/ssexec_restart.c b/src/lib66/exec/ssexec_restart.c
index 8f4ec70e3cbf9d0a418eb8becd533175b9374617..9dab2753c35a110feb7f2734aeac607cf8d42a07 100644
--- a/src/lib66/exec/ssexec_restart.c
+++ b/src/lib66/exec/ssexec_restart.c
@@ -38,9 +38,7 @@ int ssexec_restart(int argc, char const *const *argv, ssexec_t *info)
     uint32_t flag = 0 ;
     uint8_t siglen = 3 ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
     ss_state_t sta = STATE_ZERO ;
 
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_TORESTART|STATE_FLAGS_WANTUP) ;
@@ -81,18 +79,18 @@ int ssexec_restart(int argc, char const *const *argv, ssexec_t *info)
         log_diesys(LOG_EXIT_SYS,"scandir: ", info->scandir.s, " is not running") ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- have you already parsed a service?") ;
 
     for (n = 0 ; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", *argv, " not available -- did you parse it?") ;
 
-        if (!state_read(&sta, &ares[aresid]))
+        if (!state_read(&sta, &hash->res))
             log_dieu(LOG_EXIT_SYS, "read state file of: ", argv[n]) ;
 
         if (sta.issupervised == STATE_FLAGS_FALSE)
@@ -117,7 +115,7 @@ int ssexec_restart(int argc, char const *const *argv, ssexec_t *info)
 
     r = svc_send_wait(argv, argc, sig, siglen, info) ;
 
-    service_resolve_array_free(ares, areslen) ;
+    hash_free(&hres) ;
     graph_free_all(&graph) ;
 
     return r ;
diff --git a/src/lib66/exec/ssexec_signal.c b/src/lib66/exec/ssexec_signal.c
index a43c0eb673924ab6bac6e9b25ba02da96432e88e..ae11fb42ed2d023f41eecb6c149971a4ddad4f74 100644
--- a/src/lib66/exec/ssexec_signal.c
+++ b/src/lib66/exec/ssexec_signal.c
@@ -54,17 +54,14 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info)
     int r ;
     uint8_t what = 1, requiredby = 0, propagate = 1 ;
     graph_t graph = GRAPH_ZERO ;
-
     unsigned int napid = 0 ;
-
     char updown[4] = "-w \0" ;
     uint8_t opt_updown = 0 ;
     char data[DATASIZE + 1] = "-" ;
     unsigned int datalen = 1 ;
     uint8_t reloadmsg = 0 ;
-
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1] ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1] ;
 
     /*
      * STATE_FLAGS_TOPROPAGATE = 0
@@ -175,20 +172,21 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info)
         log_diesys(LOG_EXIT_SYS,"scandir: ", info->scandir.s," is not running") ;
 
     /** build the graph of the entire system.*/
-    graph_build_service(&graph, ares, &areslen, info, gflag) ;
+    graph_build_service(&graph, &hres, info, gflag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not supervised -- initiate its first") ;
 
     for (; *argv ; argv++) {
-        int aresid = service_resolve_array_search(ares, areslen, *argv) ;
+
+        struct resolve_hash_s *hash = hash_search(&hres, *argv) ;
         /** The service may not be supervised, for example serviceB depends on
          * serviceA and serviceB was unsupervised by the user. So it will be ignored
          * by the function graph_build_service. In this case, the service does not
          * exist at array.
          *
          * At stop process, just ignore it as it already down anyway */
-        if (aresid < 0) {
+        if (hash == NULL) {
             if (what && data[1] != 'r' || data[1] != 'h') {
                 log_warn("service: ", *argv, " is already stopped or unsupervised -- ignoring it") ;
                 continue ;
@@ -196,17 +194,17 @@ int ssexec_signal(int argc, char const *const *argv, ssexec_t *info)
                 log_die(LOG_EXIT_USER, "service: ", *argv, " not available -- did you parse it?") ;
             }
         }
-        graph_compute_visit(ares, aresid, visit, list, &graph, &napid, requiredby) ;
+        graph_compute_visit(*hash, visit, list, &graph, &napid, requiredby) ;
     }
 
     pidservice_t apids[napid] ;
 
-    svc_init_array(list, napid, apids, &graph, ares, areslen, info, requiredby, gflag) ;
+    svc_init_array(list, napid, apids, &graph, &hres, info, requiredby, gflag) ;
 
-    r = svc_launch(apids, napid, what, &graph, ares, areslen, info, updown, opt_updown, reloadmsg, data, propagate) ;
+    r = svc_launch(apids, napid, what, &graph, &hres, info, updown, opt_updown, reloadmsg, data, propagate) ;
 
     graph_free_all(&graph) ;
-    service_resolve_array_free(ares, areslen) ;
+    hash_free(&hres) ;
 
     return r ;
 }
diff --git a/src/lib66/exec/ssexec_start.c b/src/lib66/exec/ssexec_start.c
index 2f8ebe385356eedc02f0d6e8e58699f6c4bbd683..0b3249ec57dc2ec2a0aacf06b124e8ef9ada3534 100644
--- a/src/lib66/exec/ssexec_start.c
+++ b/src/lib66/exec/ssexec_start.c
@@ -20,6 +20,7 @@
 #include <oblibs/sastr.h>
 
 #include <skalibs/sgetopt.h>
+#include <skalibs/genalloc.h>
 
 #include <66/ssexec.h>
 #include <66/config.h>
@@ -29,18 +30,19 @@
 #include <66/sanitize.h>
 #include <66/service.h>
 #include <66/enum.h>
+#include <66/hash.h>
 
 int ssexec_start(int argc, char const *const *argv, ssexec_t *info)
 {
     log_flow() ;
 
+    int n = 0 ;
     uint32_t flag = 0 ;
     graph_t graph = GRAPH_ZERO ;
     uint8_t siglen = 3 ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0 ;
 
-    int n = 0 ;
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
 
     memset(list, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
@@ -90,25 +92,24 @@ int ssexec_start(int argc, char const *const *argv, ssexec_t *info)
         sanitize_source(argv[n], info, flag) ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
 
     for (n = 0 ; n < argc ; n++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[n]) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, argv[n]) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", argv[n], " not available -- did you parse it?") ;
 
-        graph_compute_visit(ares, aresid, visit, list, &graph, &nservice, 0) ;
+        graph_compute_visit(*hash, visit, list, &graph, &nservice, 0) ;
     }
 
     /** initiate services at the corresponding scandir */
-    sanitize_init(list, nservice, &graph, ares, areslen) ;
-
-    service_resolve_array_free(ares, areslen) ;
+    sanitize_init(list, nservice, &graph, &hres) ;
 
+    hash_free(&hres) ;
     graph_free_all(&graph) ;
 
     char *sig[siglen] ;
diff --git a/src/lib66/exec/ssexec_status.c b/src/lib66/exec/ssexec_status.c
index 18cf4c37bd9c05ead69702e9550f841eeed0055c..f9d02ce31458377ba1ef05a1068ad3d632f9f752 100644
--- a/src/lib66/exec/ssexec_status.c
+++ b/src/lib66/exec/ssexec_status.c
@@ -322,16 +322,13 @@ static void info_display_requiredby(char const *field, resolve_service_t *res)
     size_t padding = 1 ;
     int r ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
-
+    struct resolve_hash_s *hres = NULL ;
     stralloc deps = STRALLOC_ZERO ;
 
     if (NOFIELD) padding = info_display_field_name(field) ;
     else { field = 0 ; padding = 0 ; }
 
-    graph_build_service(&graph, ares, &areslen, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
+    graph_build_service(&graph, &hres, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
 
    if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
@@ -391,6 +388,7 @@ static void info_display_requiredby(char const *field, resolve_service_t *res)
         }
     freed:
         graph_free_all(&graph) ;
+        hash_free(&hres) ;
         stralloc_free(&deps) ;
 }
 
@@ -399,16 +397,14 @@ static void info_display_deps(char const *field, resolve_service_t *res)
     int r ;
     size_t padding = 1 ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
 
     stralloc deps = STRALLOC_ZERO ;
 
     if (NOFIELD) padding = info_display_field_name(field) ;
     else { field = 0 ; padding = 0 ; }
 
-    graph_build_service(&graph, ares, &areslen, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
+    graph_build_service(&graph, &hres, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
@@ -468,6 +464,7 @@ static void info_display_deps(char const *field, resolve_service_t *res)
 
     freed:
         graph_free_all(&graph) ;
+        hash_free(&hres) ;
         stralloc_free(&deps) ;
 }
 
@@ -504,9 +501,7 @@ static void info_display_contents(char const *field, resolve_service_t *res)
     size_t padding = 1 ;
     graph_t graph = GRAPH_ZERO ;
     stralloc sa = STRALLOC_ZERO ;
-
-    unsigned int areslen = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
 
     if (NOFIELD) padding = info_display_field_name(field) ;
     else { field = 0 ; padding = 0 ; }
@@ -517,7 +512,7 @@ static void info_display_contents(char const *field, resolve_service_t *res)
     if (!sastr_clean_string(&sa, res->sa.s + res->dependencies.contents))
         log_dieu(LOG_EXIT_SYS, "clean string") ;
 
-    service_graph_g(sa.s, sa.len, &graph, ares, &areslen, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
+    service_graph_g(sa.s, sa.len, &graph, &hres, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- please make a bug report") ;
@@ -564,7 +559,7 @@ static void info_display_contents(char const *field, resolve_service_t *res)
 
     freed:
         graph_free_all(&graph) ;
-        service_resolve_array_free(ares, areslen) ;
+        hash_free(&hres) ;
         stralloc_free(&sa) ;
 }
 
diff --git a/src/lib66/exec/ssexec_stop.c b/src/lib66/exec/ssexec_stop.c
index f49100ea7445c53132eef00e54a98508dcf1215f..b3bacc395901a413f3ecf26ec154270f6e1db1e0 100644
--- a/src/lib66/exec/ssexec_stop.c
+++ b/src/lib66/exec/ssexec_stop.c
@@ -41,9 +41,8 @@ int ssexec_stop(int argc, char const *const *argv, ssexec_t *info)
     graph_t graph = GRAPH_ZERO ;
     uint8_t siglen = 3 ;
     int e = 0 ;
-
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0, pos = 0, idx = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0, pos = 0, idx = 0 ;
 
     memset(list, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
@@ -99,25 +98,24 @@ int ssexec_stop(int argc, char const *const *argv, ssexec_t *info)
         log_diesys(LOG_EXIT_SYS,"scandir: ", info->scandir.s," is not running") ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- did you start it first?") ;
 
     for (; pos < argc ; pos++) {
 
-        int aresid = service_resolve_array_search(ares, areslen, argv[pos]) ;
-
         /** The service may not be supervised, so it will be ignored by the
          * function graph_build_service. In this case, the service does not
          * exist at array.
          *
          * This the stop process, just ignore it as it already down anyway */
-        if (aresid < 0) {
+        struct resolve_hash_s *hash = hash_search(&hres, argv[pos]) ;
+        if (hash == NULL) {
             log_warn("service: ", argv[pos], " is already stopped or unsupervised -- ignoring it") ;
             continue ;
         }
-        graph_compute_visit(ares, aresid, visit, list, &graph, &nservice, 1) ;
+        graph_compute_visit(*hash, visit, list, &graph, &nservice, 1) ;
     }
 
     char *sig[siglen] ;
@@ -155,17 +153,17 @@ int ssexec_stop(int argc, char const *const *argv, ssexec_t *info)
             fvisit[idx] = 1 ;
         }
 
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(&hres, name) ;
+        if (hash == NULL)
             log_die(LOG_EXIT_USER, "service: ", name, " not available -- please make a bug report") ;
 
         /** the logger need to be stopped in case of unsupervise request */
         if (FLAGS_ISSET(flag, STATE_FLAGS_TOUNSUPERVISE)) {
 
-            if (get_rstrlen_until(name, SS_LOG_SUFFIX) < 0 && ares[aresid].logger.want) {
+            if (get_rstrlen_until(name, SS_LOG_SUFFIX) < 0 && hash->res.logger.want) {
 
-                nargv[nargc++] = ares[aresid].sa.s + ares[aresid].logger.name ;
-                idx = graph_hash_vertex_get_id(&graph, ares[aresid].sa.s + ares[aresid].logger.name) ;
+                nargv[nargc++] = hash->res.sa.s + hash->res.logger.name ;
+                idx = graph_hash_vertex_get_id(&graph, hash->res.sa.s + hash->res.logger.name) ;
                 if (!fvisit[idx]) {
                     flist[fnservice++] = idx ;
                     fvisit[idx] = 1 ;
@@ -179,9 +177,9 @@ int ssexec_stop(int argc, char const *const *argv, ssexec_t *info)
     e = svc_send_wait(nargv, nargc, sig, siglen, info) ;
 
     if (FLAGS_ISSET(flag, STATE_FLAGS_TOUNSUPERVISE))
-        svc_unsupervise(flist, fnservice, &graph, ares, areslen, info) ;
+        svc_unsupervise(flist, fnservice, &graph, &hres, info) ;
 
-    service_resolve_array_free(ares, areslen) ;
+    hash_free(&hres) ;
     graph_free_all(&graph) ;
 
     return e ;
diff --git a/src/lib66/exec/ssexec_tree_init.c b/src/lib66/exec/ssexec_tree_init.c
index fd18baaa92ec24fa8f27635d29fa1995a5b4fef0..8fd125f15b398a6a756870220cb1c8b57586be40 100644
--- a/src/lib66/exec/ssexec_tree_init.c
+++ b/src/lib66/exec/ssexec_tree_init.c
@@ -40,9 +40,8 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
 
     uint32_t flag = 0 ;
     graph_t graph = GRAPH_ZERO ;
-
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0, n = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1], nservice = 0, n = 0 ;
 
     memset(list, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
@@ -52,15 +51,16 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
         FLAGS_SET(flag, STATE_FLAGS_ISEARLIER) ;
 
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not available -- have you already parsed a service?") ;
 
     FOREACH_SASTR(sa, n) {
 
-        int aresid = service_resolve_array_search(ares, areslen, sa->s + n) ;
-        if (aresid < 0) {
+        struct resolve_hash_s *hash ;
+        hash = hash_search(&hres, sa->s + n) ;
+        if (hash == NULL) {
 
             if (earlier) {
                 log_trace("ignoring none earlier service: ", sa->s + n) ;
@@ -77,7 +77,7 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
 
             if (earlier) {
 
-                if (ares[aresid].earlier) {
+                if (hash->res.earlier) {
 
                     list[nservice++] = idx ;
                     visit[idx] = 1 ;
@@ -85,14 +85,14 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
 
             } else {
 
-                if (ares[aresid].enabled) {
+                if (hash->res.enabled) {
 
                     list[nservice++] = idx ;
                     visit[idx] = 1 ;
 
                 } else {
 
-                    log_trace("ignoring not enabled service: ", ares[aresid].sa.s + ares[aresid].name) ;
+                    log_trace("ignoring not enabled service: ", hash->res.sa.s + hash->res.name) ;
 
                 }
             }
@@ -109,13 +109,14 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
 
                 char *name = graph.data.s + genalloc_s(graph_hash_t,&graph.hash)[l[pos]].vertex ;
 
-                aresid = service_resolve_array_search(ares, areslen, name) ;
-                if (aresid < 0)
+                struct resolve_hash_s *h ;
+                h = hash_search(&hres, name) ;
+                if (hash == NULL)
                     log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parse it?") ;
 
                 if (earlier) {
 
-                    if (ares[aresid].earlier) {
+                    if (h->res.earlier) {
 
                         list[nservice++] = l[pos] ;
                         visit[l[pos]] = 1 ;
@@ -123,7 +124,7 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
 
                 } else {
 
-                    if (ares[aresid].enabled) {
+                    if (h->res.enabled) {
 
                         list[nservice++] = l[pos] ;
                         visit[l[pos]] = 1 ;
@@ -134,9 +135,9 @@ static void doit(stralloc *sa, ssexec_t *info, uint8_t earlier)
         }
     }
 
-    sanitize_init(list, nservice, &graph, ares, areslen) ;
+    sanitize_init(list, nservice, &graph, &hres) ;
 
-    service_resolve_array_free(ares, areslen) ;
+    hash_free(&hres) ;
     graph_free_all(&graph) ;
 }
 
diff --git a/src/lib66/exec/ssexec_tree_signal.c b/src/lib66/exec/ssexec_tree_signal.c
index d982fe380d464b755f6fdf202424b41c2e1fa214..33f753f961af69ca328d1fe6a506002cf9125e0a 100644
--- a/src/lib66/exec/ssexec_tree_signal.c
+++ b/src/lib66/exec/ssexec_tree_signal.c
@@ -643,10 +643,6 @@ static int async_deps(struct resolve_hash_tree_s **htres, pidtree_t *apidt, unsi
 
                 if (!visit[pos]) {
 
-                    id = pidtree_get_id(apidt, id) ;
-                    if (id < 0)
-                        log_dieu(LOG_EXIT_SYS, "get apidtree id -- please make a bug report") ;
-
                     id = check_action(apidt, id, c, what) ;
                     if (id < 0)
                         log_die(LOG_EXIT_SYS, "tree dependency: ", apidt[id].tres->sa.s + apidt[id].tres->name, " of: ", apidt[id].tres->sa.s + apidt[id].tres->name," crashed") ;
diff --git a/src/lib66/exec/ssexec_tree_status.c b/src/lib66/exec/ssexec_tree_status.c
index 7689c9ccee6192cfd87f377286d92814eddd718c..e21647681dcfa9078e88c6575fca562f46bd6ff0 100644
--- a/src/lib66/exec/ssexec_tree_status.c
+++ b/src/lib66/exec/ssexec_tree_status.c
@@ -370,7 +370,7 @@ static void info_display_contents(char const *field, char const *treename)
     if (!sa.len)
         goto empty ;
 
-    service_graph_g_hash(sa.s, sa.len, &graph, &hres, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
+    service_graph_g(sa.s, sa.len, &graph, &hres, pinfo, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP) ;
 
     if (!HASH_COUNT(hres))
         goto empty ;
diff --git a/src/lib66/graph/graph_build_service.c b/src/lib66/graph/graph_build_service.c
index fe2a0249f10e7f42cc62b9e6f026531679321f7c..fceacb3ea4baf9517891ac71e07a12d4b3619c88 100644
--- a/src/lib66/graph/graph_build_service.c
+++ b/src/lib66/graph/graph_build_service.c
@@ -27,8 +27,9 @@
 #include <66/graph.h>
 #include <66/state.h>
 #include <66/config.h>
+#include <66/hash.h>
 
-void graph_build_service(graph_t *graph, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag)
+void graph_build_service(graph_t *graph, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag)
 {
     log_flow() ;
 
@@ -41,9 +42,7 @@ void graph_build_service(graph_t *graph, resolve_service_t *ares, unsigned int *
     if (!sastr_dir_get_recursive(&sa, solve, exclude, S_IFLNK, 0))
         log_dieu(LOG_EXIT_SYS, "get resolve files") ;
 
-    memset(ares, 0, (SS_MAX_SERVICE + 1) * sizeof(resolve_service_t)) ;
-
-    service_graph_g(sa.s, sa.len, graph, ares, areslen, info, flag) ;
+    service_graph_g(sa.s, sa.len, graph, hres, info, flag) ;
 
     stralloc_free(&sa) ;
 }
diff --git a/src/lib66/graph/graph_build_tree.c b/src/lib66/graph/graph_build_tree.c
index b1b5fd26cbc828bcaed7cfeda220efd693ced868..0ca246c2b9292428f3a35fe7a8eb46adbe6eea14 100644
--- a/src/lib66/graph/graph_build_tree.c
+++ b/src/lib66/graph/graph_build_tree.c
@@ -78,6 +78,5 @@ void graph_build_tree(graph_t *g, struct resolve_hash_tree_s **htres, char const
     if (!graph_matrix_sort(g))
         log_dieu(LOG_EXIT_SYS, "sort the graph") ;
 
-    resolve_free(wres) ;
     stralloc_free(&sa) ;
 }
diff --git a/src/lib66/graph/graph_compute_visit.c b/src/lib66/graph/graph_compute_visit.c
index 27eab5cb58dc8b71dd11c23dbbcb5ae9c5762f3f..84b9ac7a98e70855a825855959847dd1329f8170 100644
--- a/src/lib66/graph/graph_compute_visit.c
+++ b/src/lib66/graph/graph_compute_visit.c
@@ -21,13 +21,13 @@
 #include <66/service.h>
 #include <66/enum.h>
 
-void graph_compute_visit(resolve_service_t *ares, unsigned int aresid, unsigned int *visit, unsigned int *list, graph_t *graph, unsigned int *nservice, uint8_t requiredby)
+void graph_compute_visit(struct resolve_hash_s hash, unsigned int *visit, unsigned int *list, graph_t *graph, unsigned int *nservice, uint8_t requiredby)
 {
     log_flow() ;
 
     unsigned int l[graph->mlen], c = 0, pos = 0, idx = 0 ;
 
-    idx = graph_hash_vertex_get_id(graph, ares[aresid].sa.s + ares[aresid].name) ;
+    idx = graph_hash_vertex_get_id(graph, hash.res.sa.s + hash.res.name) ;
 
     if (!visit[idx]) {
         list[(*nservice)++] = idx ;
@@ -35,7 +35,7 @@ void graph_compute_visit(resolve_service_t *ares, unsigned int aresid, unsigned
     }
 
     /** find dependencies of the service from the graph, do it recursively */
-    c = graph_matrix_get_edge_g_list(l, graph, ares[aresid].sa.s + ares[aresid].name, requiredby, 1) ;
+    c = graph_matrix_get_edge_g_list(l, graph, hash.res.sa.s + hash.res.name, requiredby, 1) ;
 
     /** append to the list to deal with */
     for (pos = 0 ; pos < c ; pos++) {
diff --git a/src/lib66/module/parse_module.c b/src/lib66/module/parse_module.c
index 0b168220db5ecc85d5933cbfad54357613aa1be1..8431a3032a317b19943151e2364b7c83fbfd6810 100644
--- a/src/lib66/module/parse_module.c
+++ b/src/lib66/module/parse_module.c
@@ -39,7 +39,7 @@
 #include <66/sanitize.h>
 #include <66/state.h>
 
-static void parse_module_dependencies(stralloc *list, resolve_service_t *res, uint8_t requiredby, resolve_service_t *ares, unsigned int *areslen, uint8_t force, uint8_t conf, ssexec_t *info)
+static void parse_module_dependencies(stralloc *list, resolve_service_t *res, uint8_t requiredby, struct resolve_hash_s **hres, uint8_t force, uint8_t conf, ssexec_t *info)
 {
     log_flow() ;
 
@@ -79,7 +79,7 @@ static void parse_module_dependencies(stralloc *list, resolve_service_t *res, ui
 
         (*nfield)++ ;
 
-        parse_frontend(sa.s, ares, areslen, info, force, conf, 0, fname, 0, 0) ;
+        parse_frontend(sa.s, hres, info, force, conf, 0, fname, 0, 0) ;
 
     }
 
@@ -142,7 +142,7 @@ static void parse_module_regex(resolve_service_t *res, char *copy, size_t copyle
     regex_configure(res, info, copy, name) ;
 }
 
-void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force)
+void parse_module(resolve_service_t *res, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force)
 {
     log_flow() ;
 
@@ -236,12 +236,12 @@ void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int
         auto_strings(copy + copylen, SS_MODULE_ACTIVATED SS_MODULE_DEPENDS) ;
         get_list(&list, copy, name, S_IFREG, exclude) ;
 
-        parse_module_dependencies(&list, res, 0, ares, areslen, force, conf, info) ;
+        parse_module_dependencies(&list, res, 0, hres, force, conf, info) ;
 
         auto_strings(copy + copylen, SS_MODULE_ACTIVATED SS_MODULE_REQUIREDBY) ;
         get_list(&list, copy, name, S_IFREG, exclude) ;
 
-        parse_module_dependencies(&list, res, 1, ares, areslen, force, conf, info) ;
+        parse_module_dependencies(&list, res, 1, hres, force, conf, info) ;
     }
 
     auto_strings(copy + copylen, SS_MODULE_ACTIVATED) ;
@@ -307,7 +307,7 @@ void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int
             if (!auto_stra(&info->treename, res->sa.s + res->treename))
                 log_die_nomem("stralloc") ;
 
-            parse_frontend(list.s, ares, areslen, info, force, conf, copy, fname, name, res->intree ? res->sa.s + res->intree : 0) ;
+            parse_frontend(list.s, hres, info, force, conf, copy, fname, name, res->intree ? res->sa.s + res->intree : 0) ;
 
             info->opt_tree = opt_tree ;
         }
@@ -316,7 +316,7 @@ void parse_module(resolve_service_t *res, resolve_service_t *ares, unsigned int
 
     /** append the module name at each inner depends/requiredby dependencies service name
      * and define contents field.*/
-    parse_rename_interdependences(res, name, ares, areslen) ;
+    parse_rename_interdependences(res, name, hres, info) ;
 
     /** Remove the module name from requiredby field
      * of the dependencies if the service disappears with the
diff --git a/src/lib66/parse/deps-lib/deps b/src/lib66/parse/deps-lib/deps
index d9041bc0c7a9105f7f7087f791cd6e995c846a6a..b72e0338da31808ca4e91a8598db1fa48cb6e23d 100644
--- a/src/lib66/parse/deps-lib/deps
+++ b/src/lib66/parse/deps-lib/deps
@@ -1,3 +1,4 @@
+parse_append_logger.o
 parse_clean_line.o
 parse_clean_list.o
 parse_clean_quotes.o
diff --git a/src/lib66/parse/parse_append_logger.c b/src/lib66/parse/parse_append_logger.c
new file mode 100644
index 0000000000000000000000000000000000000000..c44e34d5106d53cb03bab2b08dccb8d2ea24c531
--- /dev/null
+++ b/src/lib66/parse/parse_append_logger.c
@@ -0,0 +1,300 @@
+/*
+ * parse_append_logger.c
+ *
+ * Copyright (c) 2018-2023 Eric Vidal <eric@obarun.org>
+ *
+ * All rights reserved.
+ *
+ * This file is part of Obarun. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution.
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file./
+ */
+
+#include <string.h>
+
+#include <oblibs/string.h>
+#include <oblibs/log.h>
+
+#include <66/resolve.h>
+#include <66/service.h>
+#include <66/enum.h>
+#include <66/constants.h>
+#include <66/config.h>
+#include <66/utils.h>
+#include <66/config.h>
+#include <66/parse.h>
+
+#include <s6/config.h>
+
+#ifndef FAKELEN
+#define FAKELEN strlen(run)
+#endif
+
+static uint32_t compute_log_dir(resolve_wrapper_t_ref wres, resolve_service_t *res)
+{
+    log_flow() ;
+
+    size_t namelen = strlen(res->sa.s + res->name) ;
+    size_t syslen = res->owner ? strlen(res->sa.s + res->path.home) + strlen(SS_LOGGER_USERDIR) : strlen(SS_LOGGER_SYSDIR) ;
+    size_t dstlen = res->logger.destination ? strlen(res->sa.s + res->logger.destination) : strlen(SS_LOGGER_SYSDIR) ;
+
+    char dstlog[syslen + dstlen + namelen + 1] ;
+
+    if (!res->logger.destination) {
+
+        if (res->owner) {
+
+            char home[syslen + 1 + strlen(SS_LOGGER_USERDIR) + 1] ;
+
+            if (!set_ownerhome_stack(home))
+                log_dieusys(LOG_EXIT_SYS,"set home directory") ;
+
+            auto_strings(dstlog, home, SS_LOGGER_USERDIR, res->sa.s + res->name) ;
+
+        } else
+            auto_strings(dstlog, SS_LOGGER_SYSDIR, res->sa.s + res->name) ;
+
+    } else {
+
+        auto_strings(dstlog, res->sa.s + res->logger.destination) ;
+    }
+
+    return resolve_add_string(wres, dstlog) ;
+}
+
+
+static void compute_log_script(resolve_service_t *res, resolve_service_t *log)
+{
+
+    log_flow() ;
+
+    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, log) ;
+
+    int build = !strcmp(res->sa.s + res->logger.execute.run.build, "custom") ? BUILD_CUSTOM : BUILD_AUTO ;
+
+    char *pmax = 0 ;
+    char *pback = 0 ;
+    char max[UINT32_FMT] ;
+    char back[UINT32_FMT] ;
+    char *timestamp = 0 ;
+    int itimestamp = SS_LOGGER_TIMESTAMP ;
+    char *logrunner = res->logger.execute.run.runas ? res->sa.s + res->logger.execute.run.runas : SS_LOGGER_RUNNER ;
+
+    log->execute.run.runas = resolve_add_string(wres, logrunner) ;
+
+    /** timestamp */
+    if (res->logger.timestamp != 3)
+        timestamp = res->logger.timestamp == TIME_NONE ? "" : res->logger.timestamp == TIME_ISO ? "T" : "t" ;
+    else
+        timestamp = itimestamp == TIME_NONE ? "" : itimestamp == TIME_ISO ? "T" : "t" ;
+
+    /** backup */
+    if (res->logger.backup) {
+
+        back[uint32_fmt(back,res->logger.backup)] = 0 ;
+        pback = back ;
+
+    }
+
+    /** file size */
+    if (res->logger.maxsize) {
+
+        max[uint32_fmt(max,res->logger.maxsize)] = 0 ;
+        pmax = max ;
+
+    }
+
+    char *shebang = "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+
+    {
+        /** run scripts */
+        char run[SS_MAX_PATH_LEN + 1] ;
+
+        auto_strings(run, \
+                    shebang, \
+                    "s6-fdholder-retrieve ", \
+                    res->sa.s + res->live.fdholderdir, "/s ", \
+                    "\"" SS_FDHOLDER_PIPENAME "r-", \
+                    res->sa.s + res->logger.name, "\"\n", \
+                    "./run.user\n") ;
+
+        log->execute.run.run = resolve_add_string(wres, run) ;
+
+    }
+
+    {
+        if (!build) {
+            /** run.user script */
+            char run[SS_MAX_PATH_LEN + 1] ;
+
+            auto_strings(run, shebang) ;
+
+            auto_strings(run + FAKELEN, "fdmove -c 2 1\n") ;
+
+            /** runas */
+            if (!res->owner)
+                auto_strings(run + FAKELEN, S6_BINPREFIX "s6-setuidgid ", logrunner, "\n") ;
+
+            auto_strings(run + FAKELEN, "s6-log ") ;
+
+            if (SS_LOGGER_NOTIFY)
+                auto_strings(run + FAKELEN, "-d3 ") ;
+
+            auto_strings(run + FAKELEN, "n", pback, " ") ;
+
+            if (res->logger.timestamp < TIME_NONE)
+                auto_strings(run + FAKELEN, timestamp, " ") ;
+
+            auto_strings(run + FAKELEN, "s", pmax, " ", res->sa.s + res->logger.destination, "\n") ;
+
+            log->execute.run.run_user = resolve_add_string(wres, run) ;
+
+        } else {
+
+            if (res->logger.execute.run.shebang)
+                log_warn("@shebang field is deprecated -- please define it at start of your @execute field instead") ;
+
+            char *shebang = res->logger.execute.run.shebang ? res->sa.s + res->logger.execute.run.shebang : "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+            size_t shebanglen = strlen(shebang) ;
+
+            char run[shebanglen + strlen(res->sa.s + res->logger.execute.run.run_user) + 2] ;
+            auto_strings(run, shebang, res->sa.s + res->logger.execute.run.run_user, "\n") ;
+
+            log->execute.run.run_user = resolve_add_string(wres, run) ;
+        }
+    }
+
+    free(wres) ;
+}
+
+static void compute_logger(resolve_service_t *res, resolve_service_t *log, ssexec_t *info)
+{
+    log_flow() ;
+
+    if (!res->logger.name)
+        return ;
+
+    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, log) ;
+
+    resolve_init(wres) ;
+
+    char *str = res->sa.s ;
+    size_t namelen = strlen(str + res->logger.name) ;
+    char name[namelen + 1] ;
+    char description[namelen + 7 + 1] ;
+
+    auto_strings(name, str + res->logger.name) ;
+
+    auto_strings(description, str + res->name, " logger") ;
+
+    log->name = resolve_add_string(wres, name) ;
+    log->description = resolve_add_string(wres, description) ;
+    log->version = resolve_add_string(wres, str + res->version) ;
+    log->type = res->type ;
+    log->notify = 3 ;
+    log->maxdeath = res->maxdeath ;
+    log->earlier = res->earlier ;
+    if (res->intree)
+        log->intree = resolve_add_string(wres, str + res->intree) ;
+
+    log->ownerstr = resolve_add_string(wres, str + res->ownerstr) ;
+    log->owner = res->owner ;
+    log->treename = resolve_add_string(wres, str + res->treename) ;
+    log->user = resolve_add_string(wres, str + res->user) ;
+    if (res->inns)
+        log->inns = resolve_add_string(wres, str + res->inns) ;
+
+    log->path.home = resolve_add_string(wres, str + res->path.home) ;
+    log->path.frontend = resolve_add_string(wres, str + res->path.frontend) ;
+    log->path.servicedir = compute_src_servicedir(wres, info) ;
+    log->dependencies.requiredby = resolve_add_string(wres, str + res->name) ;
+    log->dependencies.nrequiredby = 1 ;
+
+    log->execute.run.build = resolve_add_string(wres, str + res->logger.execute.run.build) ;
+    log->execute.run.shebang = res->logger.execute.run.shebang ? resolve_add_string(wres, str + res->logger.execute.run.shebang) : 0 ;
+    log->execute.run.runas = resolve_add_string(wres, str + res->logger.execute.run.runas) ;
+    log->execute.timeout.kill = res->logger.execute.timeout.kill ;
+    log->execute.timeout.finish = res->logger.execute.timeout.finish ;
+    log->execute.down = res->logger.execute.down ;
+    log->execute.downsignal = res->logger.execute.downsignal ;
+
+    log->live.livedir = resolve_add_string(wres, info->live.s) ;
+    log->live.status = compute_status(wres, info) ;
+    log->live.servicedir = compute_live_servicedir(wres, info) ;
+    log->live.scandir = compute_scan_dir(wres, info) ;
+    log->live.statedir = compute_state_dir(wres, info, SS_STATE + 1) ;
+    log->live.eventdir = compute_state_dir(wres, info, SS_EVENTDIR + 1) ;
+    log->live.notifdir = compute_state_dir(wres, info, "notif") ;
+    log->live.supervisedir = compute_state_dir(wres, info, SS_SUPERVISEDIR + 1) ;
+    log->live.fdholderdir = compute_pipe_service(wres, info, SS_FDHOLDER) ;
+    log->live.oneshotddir = compute_pipe_service(wres, info, SS_ONESHOTD) ;
+
+    log->logger.destination = resolve_add_string(wres, str + res->logger.destination) ;
+    log->logger.backup = res->logger.backup ;
+    log->logger.maxsize = res->logger.maxsize ;
+    log->logger.timestamp = res->logger.timestamp ;
+    log->logger.want = 0 ;
+
+    // oneshot do not use fdholder daemon
+    if (res->type == TYPE_CLASSIC)
+        compute_log_script(res, log) ;
+
+    free(wres) ;
+
+}
+
+void parse_append_logger(struct resolve_hash_s **hres, resolve_service_t *res, ssexec_t *info)
+{
+    log_flow() ;
+
+    char *name = res->sa.s + res->name ;
+    struct resolve_hash_s *hash ;
+    resolve_service_t lres = RESOLVE_SERVICE_ZERO ;
+    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
+    size_t namelen = strlen(name) ;
+    char logname[namelen + SS_LOG_SUFFIX_LEN + 1] ;
+
+    auto_strings(logname, name, SS_LOG_SUFFIX) ;
+
+    res->logger.name = resolve_add_string(wres, logname) ;
+
+    res->logger.destination = compute_log_dir(wres, res) ;
+
+    res->logger.execute.run.runas = res->logger.execute.run.runas ? resolve_add_string(wres, res->sa.s + res->logger.execute.run.runas) : resolve_add_string(wres, SS_LOGGER_RUNNER) ;
+
+    hash = hash_search(hres, logname) ;
+    if (hash == NULL && res->type == TYPE_CLASSIC) {
+        /** the logger is not a service with oneshot type */
+
+        if (res->dependencies.ndepends) {
+
+            char buf[strlen(res->sa.s + res->dependencies.depends) + 1 + strlen(res->sa.s + res->logger.name) + 1] ;
+            auto_strings(buf, res->sa.s + res->dependencies.depends, " ", res->sa.s + res->logger.name) ;
+
+            res->dependencies.depends = resolve_add_string(wres, buf) ;
+
+        } else {
+
+            res->dependencies.depends = resolve_add_string(wres, res->sa.s + res->logger.name) ;
+        }
+
+        res->dependencies.ndepends++ ;
+
+        compute_logger(res, &lres, info) ;
+
+        /** sanitize_init use this field */
+        res->logger.execute.run.run = resolve_add_string(wres, lres.sa.s + lres.execute.run.run) ;
+        res->logger.execute.run.run_user = resolve_add_string(wres, lres.sa.s + lres.execute.run.run_user) ;
+
+        if (hash_count(hres) > SS_MAX_SERVICE)
+            log_die(LOG_EXIT_SYS, "too many services to parse -- compile again 66 changing the --max-service options") ;
+
+        log_trace("add service: ", logname, " to the service selection") ;
+        if (!hash_add(hres, logname, lres))
+            log_dieu(LOG_EXIT_SYS, "append service selection with: ", logname) ;
+    }
+
+    free(wres) ;
+}
\ No newline at end of file
diff --git a/src/lib66/parse/parse_compute_resolve.c b/src/lib66/parse/parse_compute_resolve.c
index 195ec77878572ea084a0ddb84b305afc6db9ca29..df7d1ee23c765888746dfe9b000b2ccd955f4597 100644
--- a/src/lib66/parse/parse_compute_resolve.c
+++ b/src/lib66/parse/parse_compute_resolve.c
@@ -37,7 +37,7 @@
 #define FAKELEN strlen(run)
 #endif
 
-static uint32_t compute_src_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info)
+uint32_t compute_src_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info)
 {
     log_flow() ;
 
@@ -52,7 +52,7 @@ static uint32_t compute_src_servicedir(resolve_wrapper_t_ref wres, ssexec_t *inf
     return resolve_add_string(wres, dir) ;
 }
 
-static uint32_t compute_live_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info)
+uint32_t compute_live_servicedir(resolve_wrapper_t_ref wres, ssexec_t *info)
 {
     log_flow() ;
 
@@ -68,7 +68,7 @@ static uint32_t compute_live_servicedir(resolve_wrapper_t_ref wres, ssexec_t *in
 }
 
 
-static uint32_t compute_status(resolve_wrapper_t_ref wres, ssexec_t *info)
+uint32_t compute_status(resolve_wrapper_t_ref wres, ssexec_t *info)
 {
     log_flow() ;
 
@@ -84,7 +84,7 @@ static uint32_t compute_status(resolve_wrapper_t_ref wres, ssexec_t *info)
 
 }
 
-static uint32_t compute_scan_dir(resolve_wrapper_t_ref wres, ssexec_t *info)
+uint32_t compute_scan_dir(resolve_wrapper_t_ref wres, ssexec_t *info)
 {
     log_flow() ;
 
@@ -99,7 +99,7 @@ static uint32_t compute_scan_dir(resolve_wrapper_t_ref wres, ssexec_t *info)
     return resolve_add_string(wres, dir) ;
 }
 
-static uint32_t compute_state_dir(resolve_wrapper_t_ref wres, ssexec_t *info, char const *folder)
+uint32_t compute_state_dir(resolve_wrapper_t_ref wres, ssexec_t *info, char const *folder)
 {
     log_flow() ;
 
@@ -115,7 +115,7 @@ static uint32_t compute_state_dir(resolve_wrapper_t_ref wres, ssexec_t *info, ch
     return resolve_add_string(wres, dir) ;
 }
 
-static uint32_t compute_pipe_service(resolve_wrapper_t_ref wres, ssexec_t *info, char const *service, char const *name)
+uint32_t compute_pipe_service(resolve_wrapper_t_ref wres, ssexec_t *info, char const *name)
 {
     log_flow() ;
 
@@ -128,39 +128,6 @@ static uint32_t compute_pipe_service(resolve_wrapper_t_ref wres, ssexec_t *info,
 
 }
 
-static uint32_t compute_log_dir(resolve_wrapper_t_ref wres, resolve_service_t *res)
-{
-    log_flow() ;
-
-    size_t namelen = strlen(res->sa.s + res->name) ;
-    size_t syslen = res->owner ? strlen(res->sa.s + res->path.home) + strlen(SS_LOGGER_USERDIR) : strlen(SS_LOGGER_SYSDIR) ;
-    size_t dstlen = res->logger.destination ? strlen(res->sa.s + res->logger.destination) : strlen(SS_LOGGER_SYSDIR) ;
-
-    char dstlog[syslen + dstlen + namelen + 1] ;
-
-    if (!res->logger.destination) {
-
-        if (res->owner) {
-
-            char home[SS_MAX_PATH_LEN + 1 + strlen(SS_LOGGER_USERDIR) + 1] ;
-
-            if (!set_ownerhome_stack(home))
-                log_dieusys(LOG_EXIT_SYS,"set home directory") ;
-
-            auto_strings(dstlog, home, SS_LOGGER_USERDIR, res->sa.s + res->name) ;
-
-        } else
-
-            auto_strings(dstlog, SS_LOGGER_SYSDIR, res->sa.s + res->name) ;
-
-    } else {
-
-        auto_strings(dstlog, res->sa.s + res->logger.destination) ;
-    }
-
-    return resolve_add_string(wres, dstlog) ;
-}
-
 /**
  * @!runorfinish -> finish, @runorfinish -> run
  * */
@@ -170,9 +137,8 @@ static void compute_wrapper_scripts(resolve_service_t *res, resolve_service_addo
 
     resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
     char *shebang = "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -" ;
-    size_t shebanglen = strlen(shebang) ;
 
-    char run[shebanglen + strlen(res->sa.s + res->live.fdholderdir) + SS_FDHOLDER_PIPENAME_LEN + strlen(res->sa.s + res->name) + SS_LOG_SUFFIX_LEN + strlen(S6_BINPREFIX) + (SS_MAX_PATH*2) + SS_MAX_PATH + strlen(file) + 132 + 1] ;
+    char run[SS_MAX_PATH_LEN + 1] ;
 
     auto_strings(run, shebang, !runorfinish ? (res->type != TYPE_ONESHOT ? "S0\n" : "P\n") : "P\n") ;
 
@@ -196,7 +162,7 @@ static void compute_wrapper_scripts(resolve_service_t *res, resolve_service_addo
 }
 
 /**
- * @!runorfinish -> finish, @runofinish -> run
+ * @!runorfinish -> finish.user, @runofinish -> run.user
  * */
 static void compute_wrapper_scripts_user(resolve_service_t *res, resolve_service_addon_scripts_t *scripts, uint8_t runorfinish)
 {
@@ -251,209 +217,10 @@ static void compute_wrapper_scripts_user(resolve_service_t *res, resolve_service
     free(wres) ;
 }
 
-static void compute_log_script(resolve_service_t *res)
-{
-
-    log_flow() ;
-
-    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
-
-    int build = !strcmp(res->sa.s + res->logger.execute.run.build, "custom") ? BUILD_CUSTOM : BUILD_AUTO ;
-
-    char *pmax = 0 ;
-    char *pback = 0 ;
-    char max[UINT32_FMT] ;
-    char back[UINT32_FMT] ;
-    char *timestamp = 0 ;
-    int itimestamp = SS_LOGGER_TIMESTAMP ;
-    char *logrunner = res->logger.execute.run.runas ? res->sa.s + res->logger.execute.run.runas : SS_LOGGER_RUNNER ;
-
-    res->logger.execute.run.runas = resolve_add_string(wres, logrunner) ;
-
-    /** timestamp */
-    if (res->logger.timestamp != 3)
-        timestamp = res->logger.timestamp == TIME_NONE ? "" : res->logger.timestamp == TIME_ISO ? "T" : "t" ;
-    else
-        timestamp = itimestamp == TIME_NONE ? "" : itimestamp == TIME_ISO ? "T" : "t" ;
-
-    /** backup */
-    if (res->logger.backup) {
-
-        back[uint32_fmt(back,res->logger.backup)] = 0 ;
-        pback = back ;
-
-    }
-
-    /** file size */
-    if (res->logger.maxsize) {
-
-        max[uint32_fmt(max,res->logger.maxsize)] = 0 ;
-        pmax = max ;
-
-    }
-
-    char *shebang = "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
-    size_t shebanglen = strlen(shebang) ;
-
-    {
-        /** run scripts */
-        char run[strlen(shebang) + strlen(res->sa.s + res->live.fdholderdir) + SS_FDHOLDER_PIPENAME_LEN +  strlen(res->sa.s + res->logger.name) + strlen(logrunner) + strlen(S6_BINPREFIX) + strlen(res->sa.s + res->logger.execute.run.runas) + 67 + 1] ;
-
-        auto_strings(run, \
-                    shebang, \
-                    "s6-fdholder-retrieve ", \
-                    res->sa.s + res->live.fdholderdir, "/s ", \
-                    "\"" SS_FDHOLDER_PIPENAME "r-", \
-                    res->sa.s + res->logger.name, "\"\n") ;
-
-        auto_strings(run + FAKELEN, "./run.user\n") ;
-
-        res->logger.execute.run.run = resolve_add_string(wres, run) ;
-
-    }
-
-    {
-        if (!build) {
-            /** run.user script */
-            char run[shebanglen + strlen(pback) + strlen(timestamp) + strlen(pmax) + strlen(res->sa.s + res->logger.destination) + 17 + 1] ;
-
-            auto_strings(run, shebang) ;
-
-            auto_strings(run + FAKELEN, "fdmove -c 2 1\n") ;
-
-            /** runas */
-            if (!res->owner)
-                auto_strings(run + FAKELEN, S6_BINPREFIX "s6-setuidgid ", logrunner, "\n") ;
-
-
-            auto_strings(run + FAKELEN, "s6-log ") ;
-
-            if (SS_LOGGER_NOTIFY)
-                auto_strings(run + FAKELEN, "-d3 ") ;
-
-            auto_strings(run + FAKELEN, "n", pback, " ") ;
-
-            if (res->logger.timestamp < TIME_NONE)
-                auto_strings(run + FAKELEN, timestamp, " ") ;
-
-            auto_strings(run + FAKELEN, "s", pmax, " ", res->sa.s + res->logger.destination, "\n") ;
-
-            res->logger.execute.run.run_user = resolve_add_string(wres, run) ;
-
-        } else {
-
-            if (res->logger.execute.run.shebang)
-                log_warn("@shebang field is deprecated -- please define it at start of your @execute field instead") ;
-
-            char *shebang = res->logger.execute.run.shebang ? res->sa.s + res->logger.execute.run.shebang : "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
-            size_t shebanglen = strlen(shebang) ;
-
-            char run[shebanglen + strlen(res->sa.s + res->logger.execute.run.run_user) + 2] ;
-            auto_strings(run, shebang, res->sa.s + res->logger.execute.run.run_user, "\n") ;
-
-            res->logger.execute.run.run_user = resolve_add_string(wres, run) ;
-        }
-    }
-
-    free(wres) ;
-}
-
-static void compute_log(resolve_service_t *res, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info)
+void parse_compute_resolve(resolve_service_t *res, ssexec_t *info)
 {
     log_flow() ;
 
-    if (!res->logger.name)
-        return ;
-
-    resolve_service_t lres = RESOLVE_SERVICE_ZERO ;
-    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, &lres) ;
-
-    resolve_init(wres) ;
-
-    char *str = res->sa.s ;
-    size_t namelen = strlen(str + res->logger.name) ;
-    char name[namelen + 1] ;
-    char description[namelen + 7 + 1] ;
-
-    auto_strings(name, str + res->logger.name) ;
-
-    auto_strings(description, str + res->name, " logger") ;
-
-    lres.name = resolve_add_string(wres, name) ;
-    lres.description = resolve_add_string(wres, description) ;
-    lres.version = resolve_add_string(wres, str + res->version) ;
-    lres.type = res->type ;
-    lres.notify = 3 ;
-    lres.maxdeath = res->maxdeath ;
-    lres.earlier = res->earlier ;
-    if (res->intree)
-        lres.intree = resolve_add_string(wres, str + res->intree) ;
-
-    lres.ownerstr = resolve_add_string(wres, str + res->ownerstr) ;
-    lres.owner = res->owner ;
-    lres.treename = resolve_add_string(wres, str + res->treename) ;
-    lres.user = resolve_add_string(wres, str + res->user) ;
-    if (res->inns)
-        lres.inns = resolve_add_string(wres, str + res->inns) ;
-
-    lres.path.home = resolve_add_string(wres, str + res->path.home) ;
-    lres.path.frontend = resolve_add_string(wres, str + res->path.frontend) ;
-    lres.path.servicedir = compute_src_servicedir(wres, info) ;
-
-    lres.dependencies.requiredby = resolve_add_string(wres, str + res->name) ;
-    lres.dependencies.nrequiredby = 1 ;
-
-    lres.execute.run.build = resolve_add_string(wres, str + res->logger.execute.run.build) ;
-    lres.execute.run.shebang = res->logger.execute.run.shebang ? resolve_add_string(wres, str + res->logger.execute.run.shebang) : 0 ;
-    lres.execute.run.runas = res->logger.execute.run.runas ? resolve_add_string(wres, str + res->logger.execute.run.runas) : resolve_add_string(wres, SS_LOGGER_RUNNER) ;
-    lres.execute.timeout.kill = res->logger.execute.timeout.kill ;
-    lres.execute.timeout.finish = res->logger.execute.timeout.finish ;
-    lres.execute.down = res->logger.execute.down ;
-    lres.execute.downsignal = res->logger.execute.downsignal ;
-
-    lres.live.livedir = resolve_add_string(wres, info->live.s) ;
-    lres.live.status = compute_status(wres, info) ;
-    lres.live.servicedir = compute_live_servicedir(wres, info) ;
-    lres.live.scandir = compute_scan_dir(wres, info) ;
-    lres.live.statedir = compute_state_dir(wres, info, SS_STATE + 1) ;
-    lres.live.eventdir = compute_state_dir(wres, info, SS_EVENTDIR + 1) ;
-    lres.live.notifdir = compute_state_dir(wres, info, "notif") ;
-    lres.live.supervisedir = compute_state_dir(wres, info, SS_SUPERVISEDIR + 1) ;
-    lres.live.fdholderdir = compute_pipe_service(wres, info, name, SS_FDHOLDER) ;
-    lres.live.oneshotddir = compute_pipe_service(wres, info, name, SS_ONESHOTD) ;
-
-    lres.logger.destination = resolve_add_string(wres, str + res->logger.destination) ;
-    lres.logger.backup = res->logger.backup ;
-    lres.logger.maxsize = res->logger.maxsize ;
-    lres.logger.timestamp = res->logger.timestamp ;
-    lres.logger.want = 0 ;
-
-    // oneshot do not use fdholder daemon
-    if (res->type == TYPE_CLASSIC) {
-
-        compute_log_script(res) ;
-        lres.execute.run.run = resolve_add_string(wres, res->sa.s + res->logger.execute.run.run) ;
-        lres.execute.run.run_user = resolve_add_string(wres, res->sa.s + res->logger.execute.run.run_user) ;
-        lres.execute.run.runas = resolve_add_string(wres, res->sa.s + res->logger.execute.run.runas) ;
-    }
-
-    if (service_resolve_array_search(ares, *areslen, name) < 0) {
-
-        log_trace("add service ", name, " to the selection") ;
-        if (*areslen > SS_MAX_SERVICE)
-            log_die(LOG_EXIT_SYS, "too many services to parse -- compile again 66 changing the --max-service options") ;
-
-        ares[(*areslen)++] = lres ;
-    }
-
-    free(wres) ;
-}
-
-void parse_compute_resolve(unsigned int idx, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info)
-{
-    log_flow() ;
-
-    resolve_service_t_ref res = &ares[idx] ;
     resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
     char name[strlen(res->sa.s + res->name) + 1] ;
 
@@ -486,43 +253,10 @@ void parse_compute_resolve(unsigned int idx, resolve_service_t *ares, unsigned i
     res->live.supervisedir = compute_state_dir(wres, info, SS_SUPERVISEDIR + 1) ;
 
     /* fdholder */
-    res->live.fdholderdir = compute_pipe_service(wres, info, name, SS_FDHOLDER) ;
+    res->live.fdholderdir = compute_pipe_service(wres, info, SS_FDHOLDER) ;
 
     /* oneshotd */
-    res->live.oneshotddir = compute_pipe_service(wres, info, name, SS_ONESHOTD) ;
-
-    if (res->logger.want && (res->type == TYPE_CLASSIC || res->type == TYPE_ONESHOT)) {
-
-        size_t namelen = strlen(name) ;
-
-        char logname[namelen + SS_LOG_SUFFIX_LEN + 1] ;
-
-        auto_strings(logname, name, SS_LOG_SUFFIX) ;
-
-        res->logger.name = resolve_add_string(wres, logname) ;
-
-        res->logger.destination = compute_log_dir(wres, res) ;
-
-        if (res->type == TYPE_CLASSIC) {
-
-            /** the logger is not a service with oneshot type */
-            if (res->dependencies.ndepends) {
-
-                char buf[strlen(res->sa.s + res->dependencies.depends) + 1 + strlen(res->sa.s + res->logger.name) + 1] ;
-                auto_strings(buf, res->sa.s + res->dependencies.depends, " ", res->sa.s + res->logger.name) ;
-
-                res->dependencies.depends = resolve_add_string(wres, buf) ;
-
-            } else {
-
-                res->dependencies.depends = resolve_add_string(wres, res->sa.s + res->logger.name) ;
-            }
-
-            res->dependencies.ndepends++ ;
-
-            compute_log(res, ares, areslen, info) ;
-        }
-    }
+    res->live.oneshotddir = compute_pipe_service(wres, info, SS_ONESHOTD) ;
 
     if (res->type == TYPE_ONESHOT || res->type == TYPE_CLASSIC) {
 
diff --git a/src/lib66/parse/parse_frontend.c b/src/lib66/parse/parse_frontend.c
index 02dc6ba59d44587b0a9761393466c8ee1850731e..80c7c7aaa736595187d2c2bff22d2fba348e9c7f 100644
--- a/src/lib66/parse/parse_frontend.c
+++ b/src/lib66/parse/parse_frontend.c
@@ -86,7 +86,7 @@ static void parse_service_instance(stralloc *frontend, char const *svsrc, char c
  * @Die on fail
  * @Return 1 on success
  * @Return 2 -> already parsed */
-int parse_frontend(char const *sv, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree)
+int parse_frontend(char const *sv, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree)
 {
     log_flow() ;
 
@@ -95,6 +95,7 @@ int parse_frontend(char const *sv, resolve_service_t *ares, unsigned int *aresle
     size_t svlen = strlen(sv) ;
     char svname[svlen + 1], svsrc[svlen + 1] ;
     stralloc sa = STRALLOC_ZERO ;
+    struct resolve_hash_s *hash ;
 
     if (!ob_basename(svname, sv))
         log_dieu(LOG_EXIT_SYS, "get basename of: ", sv) ;
@@ -104,18 +105,21 @@ int parse_frontend(char const *sv, resolve_service_t *ares, unsigned int *aresle
 
     if (inns) {
 
-        if (service_resolve_array_search(ares, *areslen, svname) >= 0)
+        hash = hash_search(hres, svname) ;
+        if (hash != NULL)
             log_warn_return(2, "ignoring: ", svname, " service -- already appended to the selection") ;
 
         char n[strlen(inns) + 1 + strlen(svname) + 1] ;
         auto_strings(n, inns, ":", svname) ;
 
-        if (service_resolve_array_search(ares, *areslen, n) >= 0)
+        hash = hash_search(hres, n) ;
+        if (hash != NULL)
             log_warn_return(2, "ignoring: ", n, " service -- already appended to the selection") ;
 
     } else {
 
-        if (service_resolve_array_search(ares, *areslen, svname) >= 0)
+        hash = hash_search(hres, svname) ;
+        if (hash != NULL)
             log_warn_return(2, "ignoring: ", svname, " service -- already appended to the selection") ;
     }
 
@@ -262,19 +266,28 @@ int parse_frontend(char const *sv, resolve_service_t *ares, unsigned int *aresle
     /** parse interdependences if the service was never parsed */
     if (isparsed == STATE_FLAGS_FALSE) {
 
-        if (!parse_interdependences(svname, res.sa.s + res.dependencies.depends, res.dependencies.ndepends, ares, areslen, info, force, conf, forced_directory, main, inns, intree))
+        if (!parse_interdependences(svname, res.sa.s + res.dependencies.depends, res.dependencies.ndepends, hres, info, force, conf, forced_directory, main, inns, intree))
             log_dieu(LOG_EXIT_SYS, "parse dependencies of service: ", svname) ;
     }
 
     if (res.type == TYPE_MODULE)
-        parse_module(&res, ares, areslen, info, force) ;
+        parse_module(&res, hres, info, force) ;
 
-    if (service_resolve_array_search(ares, *areslen, res.sa.s + res.name) < 0) {
+    parse_compute_resolve(&res, info) ;
 
-        log_trace("add service ", res.sa.s + res.name, " to the selection") ;
-        if (*areslen > SS_MAX_SERVICE)
+    if (res.logger.want && res.type != TYPE_MODULE && !res.inns)
+        parse_append_logger(hres, &res, info) ;
+
+    hash = hash_search(hres, res.sa.s + res.name) ;
+    if (hash == NULL) {
+
+        if (hash_count(hres) > SS_MAX_SERVICE)
             log_die(LOG_EXIT_SYS, "too many services to parse -- compile again 66 changing the --max-service options") ;
-        ares[(*areslen)++] = res ;
+
+        log_trace("add service: ", res.sa.s + res.name, " to the service selection") ;
+        char *name = res.sa.s + res.name ; // hash_add + log_dieu doesn't accept res.sa.s + res.name
+        if (!hash_add(hres, name, res))
+            log_dieu(LOG_EXIT_SYS, "append service selection with: ", name) ;
     }
 
     stralloc_free(&sa) ;
diff --git a/src/lib66/parse/parse_interdependences.c b/src/lib66/parse/parse_interdependences.c
index 5e70e87adf68edf5138b465c76cfa289754a44cc..417a408580d53d420db24ce4ecfc13f1eb4d664f 100644
--- a/src/lib66/parse/parse_interdependences.c
+++ b/src/lib66/parse/parse_interdependences.c
@@ -30,7 +30,7 @@
 #include <66/instance.h>
 #include <66/module.h>
 
-int parse_interdependences(char const *service, char const *list, unsigned int listlen, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree)
+int parse_interdependences(char const *service, char const *list, unsigned int listlen, struct resolve_hash_s **hres, ssexec_t *info, uint8_t force, uint8_t conf, char const *forced_directory, char const *main, char const *inns, char const *intree)
 {
     log_flow() ;
 
@@ -89,7 +89,7 @@ int parse_interdependences(char const *service, char const *list, unsigned int l
              * forced_directory == 0 means that the service
              * comes from an external directory of the module.
              * In this case don't associated it at the module. */
-            parse_frontend(sa.s, ares, areslen, info, force, conf, forced_directory, main, !forced_directory ? 0 : inns, !forced_directory ? 0 : intree) ;
+            parse_frontend(sa.s, hres, info, force, conf, forced_directory, main, !forced_directory ? 0 : inns, !forced_directory ? 0 : intree) ;
         }
 
     } else
diff --git a/src/lib66/parse/parse_rename_interdependences.c b/src/lib66/parse/parse_rename_interdependences.c
index a150b8cedbf986c975a243378988710c60353d51..56a09dc601167fe1f026efc479c75020c4da4809 100644
--- a/src/lib66/parse/parse_rename_interdependences.c
+++ b/src/lib66/parse/parse_rename_interdependences.c
@@ -27,124 +27,123 @@
 #include <66/resolve.h>
 #include <66/enum.h>
 #include <66/constants.h>
+#include <66/hash.h>
 
-static void parse_prefix(char *result, stack *stk, resolve_service_t *ares, unsigned int areslen, char const *prefix)
+static void parse_prefix(char *result, stack *stk, struct resolve_hash_s **hres, char const *prefix)
 {
     log_flow() ;
 
-    int aresid = -1 ;
     size_t pos = 0, mlen = strlen(prefix) ;
+    struct resolve_hash_s *hash ;
 
     FOREACH_STK(stk, pos) {
 
-        aresid = service_resolve_array_search(ares, areslen, stk->s + pos) ;
+        hash = hash_search(hres, stk->s + pos) ;
+        if (hash == NULL) {
 
-        if (aresid < 0) {
             /** try with the name of the prefix as prefix */
             char tmp[mlen + 1 + strlen(stk->s + pos) + 1] ;
 
             auto_strings(tmp, prefix, ":", stk->s + pos) ;
 
-            aresid = service_resolve_array_search(ares, areslen, tmp) ;
-            if (aresid < 0)
+            hash = hash_search(hres, tmp) ;
+            if (hash == NULL)
                 log_die(LOG_EXIT_USER, "service: ", stk->s + pos, " not available -- please make a bug report") ;
         }
 
         /** check if the dependencies is a external one. In this
-         * case, the service is not considered as part of the prefix */
-        if (ares[aresid].inns && (!strcmp(ares[aresid].sa.s + ares[aresid].inns, prefix)))
+         * case, the service is not considered as part of the ns */
+        if (hash->res.inns && (!strcmp(hash->res.sa.s + hash->res.inns, prefix)) && str_start_with(hash->res.sa.s + hash->res.name, prefix))
             auto_strings(result + strlen(result), prefix, ":", stk->s + pos, " ") ;
         else
-            auto_strings(result + strlen(result), stk->s + pos, " ") ;
+            auto_strings(result + strlen(result), hash->res.sa.s + hash->res.name, " ") ;
     }
 
     result[strlen(result) - 1] = 0 ;
 }
 
-static void parse_prefix_name(unsigned int idx, resolve_service_t *ares, unsigned int areslen, char const *prefix)
+static void parse_prefix_name(resolve_service_t *res, struct resolve_hash_s **hres, char const *prefix)
 {
     log_flow() ;
 
     size_t mlen = strlen(prefix) ;
-    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, &ares[idx]) ;
+    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
 
-    if (ares[idx].dependencies.ndepends) {
+    if (res->dependencies.ndepends) {
 
-        size_t depslen = strlen(ares[idx].sa.s + ares[idx].dependencies.depends) ;
+        size_t depslen = strlen(res->sa.s + res->dependencies.depends) ;
         _init_stack_(stk, depslen + 1) ;
 
         if (!stack_clean_string(&stk, res->sa.s + res->dependencies.depends, depslen))
             log_dieusys(LOG_EXIT_SYS, "convert string to stack") ;
 
-        size_t len = (mlen + 1 + SS_MAX_TREENAME + 2) * ares[idx].dependencies.ndepends ;
+        size_t len = (mlen + 1 + SS_MAX_TREENAME + 2) * res->dependencies.ndepends ;
         char n[len] ;
 
         memset(n, 0, len * sizeof(char)); ;
 
-        parse_prefix(n, &stk, ares, areslen, prefix) ;
+        parse_prefix(n, &stk, hres, prefix) ;
 
-        ares[idx].dependencies.depends = resolve_add_string(wres, n) ;
+        res->dependencies.depends = resolve_add_string(wres, n) ;
 
     }
 
-    if (ares[idx].dependencies.nrequiredby) {
+    if (res->dependencies.nrequiredby) {
 
-        size_t depslen = strlen(ares[idx].sa.s + ares[idx].dependencies.requiredby) ;
+        size_t depslen = strlen(res->sa.s + res->dependencies.requiredby) ;
         _init_stack_(stk, depslen + 1) ;
 
         if (!stack_clean_string(&stk, res->sa.s + res->dependencies.requiredby, depslen))
             log_dieusys(LOG_EXIT_SYS, "convert string to stack") ;
 
-        size_t len = (mlen + 1 + SS_MAX_TREENAME + 2) * ares[idx].dependencies.nrequiredby ;
+        size_t len = (mlen + 1 + SS_MAX_TREENAME + 2) * res->dependencies.nrequiredby ;
         char n[len] ;
 
         memset(n, 0, len * sizeof(char)) ;
 
-        parse_prefix(n, &stk, ares, areslen, prefix) ;
+        parse_prefix(n, &stk, hres, prefix) ;
 
-        ares[idx].dependencies.requiredby = resolve_add_string(wres, n) ;
+        res->dependencies.requiredby = resolve_add_string(wres, n) ;
 
     }
 
     free(wres) ;
 }
 
-void parse_rename_interdependences(resolve_service_t *res, char const *prefix, resolve_service_t *ares, unsigned int *areslen)
+void parse_rename_interdependences(resolve_service_t *res, char const *prefix, struct resolve_hash_s **hres, ssexec_t *info)
 {
     log_flow() ;
 
-    unsigned int aresid = 0 ;
-    size_t plen = strlen(prefix) ;
-    unsigned int visit[SS_MAX_SERVICE + 1] ;
-    resolve_wrapper_t_ref awres = 0 ;
+    struct resolve_hash_s *c, *tmp ;
     stralloc sa = STRALLOC_ZERO ;
     resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
 
-    memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
-
-    for (; aresid < *areslen ; aresid++) {
+    HASH_ITER(hh, *hres, c, tmp) {
 
-        if (!strcmp(ares[aresid].sa.s + ares[aresid].inns, prefix)) {
+        if (!strcmp(c->res.sa.s + c->res.inns, prefix)) {
 
-            if (ares[aresid].dependencies.ndepends || ares[aresid].dependencies.nrequiredby)
-                parse_prefix_name(aresid, ares, *areslen, prefix) ;
+            if (c->res.dependencies.ndepends || c->res.dependencies.nrequiredby)
+                parse_prefix_name(&c->res, hres, prefix) ;
 
-            if (ares[aresid].logger.want && ares[aresid].type == TYPE_CLASSIC) {
+            if (c->res.logger.want && (c->res.type == TYPE_CLASSIC || c->res.type == TYPE_ONESHOT)) {
 
-                char n[strlen(ares[aresid].sa.s + ares[aresid].name) + SS_LOG_SUFFIX_LEN + 1] ;
+                size_t namelen = strlen(c->res.sa.s + c->res.name) ;
+                char logname[namelen + SS_LOG_SUFFIX_LEN + 1] ;
 
-                auto_strings(n, ares[aresid].sa.s + ares[aresid].name, SS_LOG_SUFFIX) ;
+                auto_strings(logname, c->res.sa.s + c->res.name, SS_LOG_SUFFIX) ;
 
-                if (!sastr_add_string(&sa, n))
-                    log_die_nomem("stralloc") ;
-            }
+                parse_append_logger(hres, &c->res, info) ;
 
-            char tmp[plen + 1 + strlen(ares[aresid].sa.s + ares[aresid].name) + 1] ;
+                if (c->res.type == TYPE_CLASSIC) {
+                    if (!sastr_add_string(&sa, logname))
+                        log_die_nomem("stralloc") ;
+                }
 
-            auto_strings(tmp, prefix, ":", ares[aresid].sa.s + ares[aresid].name) ;
+            }
 
-            if (!sastr_add_string(&sa, ares[aresid].sa.s + ares[aresid].name))
-                log_die_nomem("stralloc") ;
+            if (sastr_cmp(&sa, c->res.sa.s + c->res.name) < 0 )
+                if (!sastr_add_string(&sa, c->res.sa.s + c->res.name))
+                    log_die_nomem("stralloc") ;
         }
     }
 
@@ -152,5 +151,4 @@ void parse_rename_interdependences(resolve_service_t *res, char const *prefix, r
 
     stralloc_free(&sa) ;
     free(wres) ;
-    free(awres) ;
 }
diff --git a/src/lib66/parse/parse_service.c b/src/lib66/parse/parse_service.c
index 195cb1d000ad1c9595274bf5d57f4f999dd8e63c..d7957836f6d784dedde40a3b098aec64739c3f53 100644
--- a/src/lib66/parse/parse_service.c
+++ b/src/lib66/parse/parse_service.c
@@ -38,6 +38,7 @@
 #include <66/graph.h>
 #include <66/sanitize.h>
 #include <66/symlink.h>
+#include <66/hash.h>
 
 parse_mill_t MILL_GET_SECTION_NAME = \
 { \
@@ -163,77 +164,69 @@ static void parse_write_state(resolve_service_t *res, char const *dst, uint8_t f
     }
 }
 
-void parse_service(char const *sv, ssexec_t *info, uint8_t force, uint8_t conf)
+void parse_service(struct resolve_hash_s **hres, char const *sv, ssexec_t *info, uint8_t force, uint8_t conf)
 {
     log_flow();
 
     int r ;
-    unsigned int areslen = 0, count = 0, pos = 0 ;
     uint8_t rforce = 0 ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
     stralloc sa = STRALLOC_ZERO ;
+    struct resolve_hash_s *c, *tmp ;
 
     char main[strlen(sv) + 1] ;
     if (!ob_basename(main, sv))
         log_dieu(LOG_EXIT_SYS, "get basename of: ", sv) ;
 
-    r = parse_frontend(sv, ares, &areslen, info, force, conf, 0, main, 0, 0) ;
+    r = parse_frontend(sv, hres, info, force, conf, 0, main, 0, 0) ;
     if (r == 2)
         /** already parsed */
         return ;
 
-    /** parse_compute_resolve add the logger
-     * to the ares array */
-    count = areslen ;
-    for (; pos < count ; pos++)
-        parse_compute_resolve(pos, ares, &areslen, info) ;
-
-    for (pos = 0 ; pos < areslen ; pos++) {
+    HASH_ITER(hh, *hres, c, tmp) {
 
         sa.len = 0 ;
         /** be paranoid */
         rforce = 0 ;
-        size_t namelen = strlen(ares[pos].sa.s + ares[pos].name), homelen = strlen(ares[pos].sa.s + ares[pos].path.home) ;
+        size_t namelen = strlen(c->res.sa.s + c->res.name), homelen = strlen(c->res.sa.s + c->res.path.home) ;
 
         char servicedir[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 1] ;
 
-        auto_strings(servicedir, ares[pos].sa.s + ares[pos].path.home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", ares[pos].sa.s + ares[pos].name) ;
+        auto_strings(servicedir, c->res.sa.s + c->res.path.home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", c->res.sa.s + c->res.name) ;
 
-        if (sanitize_write(&ares[pos], force))
+        if (sanitize_write(&c->res, force))
             rforce = 1 ;
 
-        if (!auto_stra(&sa, "/tmp/", ares[pos].sa.s + ares[pos].name, ":XXXXXX"))
+        if (!auto_stra(&sa, "/tmp/", c->res.sa.s + c->res.name, ":XXXXXX"))
             log_die_nomem("stralloc") ;
 
         if (!mkdtemp(sa.s))
             log_dieusys(LOG_EXIT_SYS, "create temporary directory") ;
 
-        write_services(&ares[pos], sa.s, rforce) ;
+        write_services(&c->res, sa.s, rforce) ;
 
-        parse_write_state(&ares[pos], sa.s, rforce) ;
+        parse_write_state(&c->res, sa.s, rforce) ;
 
-        service_resolve_write_remote(&ares[pos], sa.s, rforce) ;
+        service_resolve_write_remote(&c->res, sa.s, rforce) ;
 
-        parse_copy_to_source(servicedir, sa.s, &ares[pos], rforce) ;
+        parse_copy_to_source(servicedir, sa.s, &c->res, rforce) ;
 
         /** do not die here, just warn the user */
         log_trace("remove temporary directory: ", sa.s) ;
         if (!dir_rm_rf(sa.s))
             log_warnu("remove temporary directory: ", sa.s) ;
 
-        tree_service_add(ares[pos].sa.s + ares[pos].treename, ares[pos].sa.s + ares[pos].name, info) ;
+        tree_service_add(c->res.sa.s + c->res.treename, c->res.sa.s + c->res.name, info) ;
 
-        if (!symlink_make(&ares[pos]))
+        if (!symlink_make(&c->res))
             log_dieusys(LOG_EXIT_SYS, "make service symlink") ;
 
         /** symlink may exist already, be sure to point to the correct location */
 
-        if (!symlink_switch(&ares[pos], SYMLINK_SOURCE))
+        if (!symlink_switch(&c->res, SYMLINK_SOURCE))
             log_dieusys(LOG_EXIT_SYS, "sanitize_symlink") ;
 
-        log_info("Parsed successfully: ", ares[pos].sa.s + ares[pos].name, " at tree: ", ares[pos].sa.s + ares[pos].treename) ;
+        log_info("Parsed successfully: ", c->res.sa.s + c->res.name, " at tree: ", c->res.sa.s + c->res.treename) ;
     }
 
-    service_resolve_array_free(ares, areslen) ;
     stralloc_free(&sa) ;
 }
diff --git a/src/lib66/sanitize/sanitize_graph.c b/src/lib66/sanitize/sanitize_graph.c
index 67f6bb8eba4ff0577be264a174bfe6e93aef5425..2e6b5f3caab9d9ac2ab27f69f0eb0c0a40f10e43 100644
--- a/src/lib66/sanitize/sanitize_graph.c
+++ b/src/lib66/sanitize/sanitize_graph.c
@@ -30,6 +30,7 @@
 #include <66/graph.h>
 #include <66/constants.h>
 #include <66/enum.h>
+#include <66/hash.h>
 
 /** rewrite depends/requiredby of each service
  * found on the system */
@@ -37,53 +38,53 @@ void sanitize_graph(ssexec_t *info)
 {
     log_flow() ;
 
-    int n = 0 ;
     uint32_t flag = 0 ;
-    unsigned int areslen = 0 ;
     stralloc sa = STRALLOC_ZERO ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hres = NULL, *c, *tmp ;
     graph_t graph = GRAPH_ZERO ;
+    resolve_wrapper_t_ref wres = 0 ;
 
     FLAGS_SET(flag, STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_TOPARSE|STATE_FLAGS_WANTUP|STATE_FLAGS_WANTDOWN) ;
 
+    log_trace("sanitize system graph") ;
     /** build the graph of the entire system */
-    graph_build_service(&graph, ares, &areslen, info, flag) ;
+    graph_build_service(&graph, &hres, info, flag) ;
 
-    for (; n < areslen ; n++) {
+    HASH_ITER(hh, hres, c, tmp) {
 
         sa.len = 0 ;
-        resolve_service_t_ref res = &ares[n] ;
-        resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
 
-        char *name = res->sa.s + res->name ;
+        wres = resolve_set_struct(DATA_SERVICE, &c->res) ;
+
+        char *name = c->res.sa.s + c->res.name ;
 
         if (graph_matrix_get_edge_g_sa(&sa, &graph, name, 0, 0) < 0)
             log_dieu(LOG_EXIT_SYS, "get dependencies of service: ", name) ;
 
-        res->dependencies.ndepends = 0 ;
-        res->dependencies.depends = 0 ;
+        c->res.dependencies.ndepends = 0 ;
+        c->res.dependencies.depends = 0 ;
 
         if (sa.len)
-            res->dependencies.depends = parse_compute_list(wres, &sa, &res->dependencies.ndepends, 0) ;
+            c->res.dependencies.depends = parse_compute_list(wres, &sa, &c->res.dependencies.ndepends, 0) ;
 
         sa.len = 0 ;
 
         if (graph_matrix_get_edge_g_sa(&sa, &graph, name, 1, 0) < 0)
             log_dieu(LOG_EXIT_SYS, "get requiredby of service: ", name) ;
 
-        res->dependencies.nrequiredby = 0 ;
-        res->dependencies.requiredby = 0 ;
+        c->res.dependencies.nrequiredby = 0 ;
+        c->res.dependencies.requiredby = 0 ;
 
         if (sa.len)
-            res->dependencies.requiredby = parse_compute_list(wres, &sa, &res->dependencies.nrequiredby, 0) ;
+            c->res.dependencies.requiredby = parse_compute_list(wres, &sa, &c->res.dependencies.nrequiredby, 0) ;
 
         if (!resolve_write_g(wres, info->base.s, name))
             log_dieu(LOG_EXIT_SYS, "write resolve file of service: ", name) ;
 
-        free(wres) ;
+        resolve_free(wres) ;
     }
 
-    service_resolve_array_free(ares, areslen) ;
-    graph_free_all(&graph) ;
     stralloc_free(&sa) ;
+    hash_free(&hres) ;
+    graph_free_all(&graph) ;
 }
diff --git a/src/lib66/sanitize/sanitize_init.c b/src/lib66/sanitize/sanitize_init.c
index bc52362b7cb7fed22875024966a91886fa14c43b..ad93ec2a6cb9344a329d86a82b3c4353e514057e 100644
--- a/src/lib66/sanitize/sanitize_init.c
+++ b/src/lib66/sanitize/sanitize_init.c
@@ -42,105 +42,109 @@
 #include <66/sanitize.h>
 #include <66/symlink.h>
 
-static unsigned int toclean[SS_MAX_SERVICE + 1] ;
-
-void cleanup(resolve_service_t *ares, unsigned int areslen)
+void cleanup(struct resolve_hash_s *hash, unsigned int alen)
 {
     unsigned int pos = 0 ;
     int e = errno ;
     ss_state_t sta = STATE_ZERO ;
+    resolve_service_t_ref pres = 0 ;
 
-    for (; pos < areslen ; pos++) {
+    for (; pos < alen ; pos++) {
 
-        if (toclean[pos]) {
+        pres = &hash[pos].res ;
 
-            if (!sanitize_fdholder(&ares[pos], &sta, STATE_FLAGS_FALSE, 0))
-                log_warnusys("sanitize fdholder directory: ", ares[pos].sa.s + ares[pos].live.fdholderdir);
+        if (!sanitize_fdholder(pres, &sta, STATE_FLAGS_FALSE, 0))
+            log_warnusys("sanitize fdholder directory: ", pres->sa.s + pres->live.fdholderdir);
 
-            log_trace("remove directory: ", ares[pos].sa.s + ares[pos].live.servicedir) ;
-            if (!dir_rm_rf(ares[pos].sa.s + ares[pos].live.servicedir))
-                log_warnusys("remove live directory: ", ares[pos].sa.s + ares[pos].live.servicedir) ;
+        log_trace("remove directory: ", pres->sa.s + pres->live.servicedir) ;
+        if (!dir_rm_rf(pres->sa.s + pres->live.servicedir))
+            log_warnusys("remove live directory: ", pres->sa.s + pres->live.servicedir) ;
+
+        log_trace("remove symlink: ", pres->sa.s + pres->live.scandir) ;
+        unlink(pres->sa.s + pres->live.scandir) ;
 
-            log_trace("remove symlink: ", ares[pos].sa.s + ares[pos].live.scandir) ;
-            unlink(ares[pos].sa.s + ares[pos].live.scandir) ;
-        }
     }
     errno = e ;
 }
 
-void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_service_t *ares, unsigned int areslen)
+void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, struct resolve_hash_s **hres)
 {
     log_flow() ;
 
+    /* nothing to do */
+    if (!alen)
+        return ;
+
     ftrigr_t fifo = FTRIGR_ZERO ;
     uint32_t earlier ;
     gid_t gid = getgid() ;
     int is_supervised = 0 ;
-    unsigned int pos = 0, nsv = 0 ;
-    unsigned int real[alen], msg[areslen] ;
+    unsigned int pos = 0, nsv = 0, msg[alen] ;
     ss_state_t sta = STATE_ZERO ;
+    resolve_service_t_ref pres = 0 ;
+    struct resolve_hash_s toclean[alen] ;
+    struct resolve_hash_s real[alen] ;
+    unsigned int ntoclean = 0 ;
 
-    memset(msg, 0, areslen * sizeof(unsigned int)) ;
-    memset(toclean, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
-
-    /* nothing to do */
-    if (!alen)
-        return ;
+    memset(msg, 0, alen * sizeof(unsigned int)) ;
+    memset(toclean, 0, alen * sizeof(struct resolve_hash_s)) ;
+    memset(real, 0, alen * sizeof(struct resolve_hash_s)) ;
 
     for (; pos < alen ; pos++) {
 
         char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[alist[pos]].vertex ;
 
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0)
+        struct resolve_hash_s *hash = hash_search(hres,name) ;
+        if (hash == NULL)
             log_dieu(LOG_EXIT_SYS,"find ares id -- please make a bug reports") ;
 
-        toclean[aresid] = 1 ;
-        earlier = ares[aresid].earlier ;
-        char *scandir = ares[aresid].sa.s + ares[aresid].live.scandir ;
+        pres = &hash->res ;
+
+        toclean[ntoclean++] = *hash ;
+        earlier = pres->earlier ;
+        char *scandir = pres->sa.s + pres->live.scandir ;
         size_t scandirlen = strlen(scandir) ;
 
-        int r = state_read(&sta, &ares[aresid]) ;
+        int r = state_read(&sta, pres) ;
 
         if (!r)
             log_dieu(LOG_EXIT_SYS, "read state file of: ", name, " -- please make a bug reports") ;
 
-        if (!sanitize_livestate(&ares[aresid], &sta)) {
-            cleanup(ares, areslen) ;
-            log_dieu(LOG_EXIT_SYS, "sanitize state directory: ", ares[aresid].sa.s + ares[aresid].name) ;
+        if (!sanitize_livestate(pres, &sta)) {
+            cleanup(toclean, ntoclean) ;
+            log_dieu(LOG_EXIT_SYS, "sanitize state directory: ", pres->sa.s + pres->name) ;
         }
 
         /**
          * Module type are not a daemons. We don't need to supervise it.
          * Special case for Oneshot, we only deal with the scandir symlink. */
-        if (ares[aresid].type == TYPE_MODULE)
+        if (pres->type == TYPE_MODULE)
             continue ;
 
         is_supervised = access(scandir, F_OK) ;
 
         if (!earlier && !is_supervised) {
             log_trace(name," already initialized -- ignore it") ;
-            msg[aresid] = 1 ;
+            msg[pos] = 1 ;
             continue ;
         }
 
         if (is_supervised == -1) {
 
-            if (!sanitize_scandir(&ares[aresid], &sta)) {
-                cleanup(ares, areslen) ;
-                log_dieusys(LOG_EXIT_SYS, "sanitize_scandir directory: ", ares[aresid].sa.s + ares[aresid].live.scandir) ;
+            if (!sanitize_scandir(pres, &sta)) {
+                cleanup(toclean, pos) ;
+                log_dieusys(LOG_EXIT_SYS, "sanitize_scandir directory: ", pres->sa.s + pres->live.scandir) ;
             }
 
-            if (ares[aresid].type == TYPE_ONESHOT) {
+            if (pres->type == TYPE_ONESHOT) {
 
-                if (!state_write(&sta, &ares[aresid])) {
-                    cleanup(ares, areslen) ;
-                    log_dieusys(LOG_EXIT_SYS, "write status file of: ", ares[aresid].sa.s + ares[aresid].name) ;
+                if (!state_write(&sta, pres)) {
+                    cleanup(toclean, pos) ;
+                    log_dieusys(LOG_EXIT_SYS, "write status file of: ", pres->sa.s + pres->name) ;
                 }
 
                 continue ;
             }
-
         }
 
         /* down file */
@@ -151,7 +155,7 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
             log_trace("create file: ", downfile) ;
             int fd = open_trunc(downfile) ;
             if (fd < 0) {
-                cleanup(ares, areslen) ;
+                cleanup(toclean, pos) ;
                 log_dieusys(LOG_EXIT_SYS, "create file: ", downfile) ;
             }
             fd_close(fd) ;
@@ -159,24 +163,24 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
 
         if (!earlier && is_supervised) {
 
-            if (!sanitize_fdholder(&ares[aresid], &sta, STATE_FLAGS_TRUE, 1)) {
-                cleanup(ares, areslen) ;
-                log_dieusys(LOG_EXIT_SYS, "sanitize fdholder directory: ", ares[aresid].sa.s + ares[aresid].live.fdholderdir) ;
+            if (!sanitize_fdholder(pres, &sta, STATE_FLAGS_TRUE, 1)) {
+                cleanup(toclean, pos) ;
+                log_dieusys(LOG_EXIT_SYS, "sanitize fdholder directory: ", pres->sa.s + pres->live.fdholderdir) ;
             }
 
-            log_trace("create fifo: ", ares[aresid].sa.s + ares[aresid].live.eventdir) ;
-            if (!ftrigw_fifodir_make(ares[aresid].sa.s + ares[aresid].live.eventdir, gid, 0)) {
-                cleanup(ares, areslen) ;
-                log_dieusys(LOG_EXIT_SYS, "create fifo: ", ares[aresid].sa.s + ares[aresid].live.eventdir) ;
+            log_trace("create fifo: ", pres->sa.s + pres->live.eventdir) ;
+            if (!ftrigw_fifodir_make(pres->sa.s + pres->live.eventdir, gid, 0)) {
+                cleanup(toclean, pos) ;
+                log_dieusys(LOG_EXIT_SYS, "create fifo: ", pres->sa.s + pres->live.eventdir) ;
             }
         }
 
-        if (!state_write(&sta, &ares[aresid])) {
-            cleanup(ares, areslen) ;
+        if (!state_write(&sta, pres)) {
+            cleanup(toclean, pos) ;
             log_dieu(LOG_EXIT_SYS, "write state file of: ", name) ;
         }
 
-        real[nsv++] = (unsigned int)aresid ;
+        real[nsv++] = *hash ;
     }
 
     /**
@@ -198,24 +202,24 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
         tain_addsec(&deadline, &STAMP, 3) ;
 
         if (!ftrigr_startf_g(&fifo, &deadline)) {
-            cleanup(ares, areslen) ;
+            cleanup(toclean, ntoclean) ;
             log_dieusys(LOG_EXIT_SYS, "ftrigr") ;
         }
 
         for (pos = 0 ; pos < nsv ; pos++) {
 
-            if (ares[real[pos]].type == TYPE_CLASSIC && !ares[real[pos]].earlier) {
+            if (real[pos].res.type == TYPE_CLASSIC && !real[pos].res.earlier) {
 
                 fake = pos ;
-                char *sa = ares[real[pos]].sa.s ;
-                char *eventdir = sa + ares[real[pos]].live.eventdir ;
+                char *sa = real[pos].res.sa.s ;
+                char *eventdir = sa + real[pos].res.live.eventdir ;
 
                 log_trace("subcribe to fifo: ", eventdir) ;
                 /** unsubscribe automatically, options is 0 */
                 ids[nids] = ftrigr_subscribe_g(&fifo, eventdir, "s", 0, &deadline) ;
 
                 if (!ids[nids++]) {
-                    cleanup(ares, areslen) ;
+                    cleanup(toclean, ntoclean) ;
                     log_dieusys(LOG_EXIT_SYS, "subcribe to fifo: ", eventdir) ;
                 }
             }
@@ -225,14 +229,14 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
 
             state_set_flag(&sta, STATE_FLAGS_TORELOAD, STATE_FLAGS_TRUE) ;
 
-            if (!sanitize_scandir(&ares[real[fake]], &sta)) {
-                cleanup(ares, areslen) ;
-                log_dieusys(LOG_EXIT_SYS, "sanitize scandir directory: ", ares[real[fake]].sa.s + ares[real[fake]].live.scandir) ;
+            if (!sanitize_scandir(&real[fake].res, &sta)) {
+                cleanup(toclean, ntoclean) ;
+                log_dieusys(LOG_EXIT_SYS, "sanitize scandir directory: ", real[fake].res.sa.s + real[fake].res.live.scandir) ;
             }
 
             log_trace("waiting for events on fifo...") ;
             if (ftrigr_wait_and_g(&fifo, ids, nids, &deadline) < 0) {
-                cleanup(ares, areslen) ;
+                cleanup(toclean, ntoclean) ;
                 log_dieusys(LOG_EXIT_SYS, "wait for events") ;
             }
         }
@@ -249,55 +253,56 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
 
         ss_state_t sta = STATE_ZERO ;
         char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[alist[pos]].vertex ;
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0) {
-            cleanup(ares, areslen) ;
-            log_dieu(LOG_EXIT_SYS, "find ares id of: ", name, " -- please make a bug reports") ;
+
+        struct resolve_hash_s *hash = hash_search(hres, name) ;
+        if (hash == NULL) {
+            cleanup(toclean, ntoclean) ;
+            log_dieu(LOG_EXIT_SYS, "find hash id of: ", name, " -- please make a bug reports") ;
         }
 
-        char *sa = ares[aresid].sa.s ;
+        pres = &hash->res ;
+        char *sa = pres->sa.s ;
 
-        if (!state_read(&sta, &ares[aresid])) {
-            cleanup(ares, areslen) ;
-            log_dieusys(LOG_EXIT_SYS, "read status file of: ", sa + ares[aresid].name) ;
+        if (!state_read(&sta, pres)) {
+            cleanup(toclean, ntoclean) ;
+            log_dieusys(LOG_EXIT_SYS, "read status file of: ", sa + pres->name) ;
         }
 
-        if (ares[aresid].type == TYPE_CLASSIC) {
+        if (pres->type == TYPE_CLASSIC) {
 
             if (!earlier) {
 
-                log_trace("clean event directory: ", sa + ares[aresid].live.eventdir) ;
-                if (!ftrigw_clean(sa + ares[aresid].live.eventdir))
-                    log_warnu("clean event directory: ", sa + ares[aresid].live.eventdir) ;
+                log_trace("clean event directory: ", sa + pres->live.eventdir) ;
+                if (!ftrigw_clean(sa + pres->live.eventdir))
+                    log_warnu("clean event directory: ", sa + pres->live.eventdir) ;
             }
         }
-        if (ares[aresid].type == TYPE_CLASSIC || ares[aresid].type == TYPE_ONESHOT) {
-
-            if (ares[aresid].logger.want) {
-                /** Creation of the logger destination. This is made here to avoid
-                 * issues on tmpfs logger directory destination */
-                uid_t log_uid ;
-                gid_t log_gid ;
-                char *logrunner = ares[aresid].logger.execute.run.runas ? ares[aresid].sa.s + ares[aresid].logger.execute.run.runas : SS_LOGGER_RUNNER ;
-                char *dst = ares[aresid].sa.s + ares[aresid].logger.destination ;
-
-                if (!youruid(&log_uid, logrunner) || !yourgid(&log_gid, log_uid)) {
-                    cleanup(ares, areslen) ;
-                    log_dieusys(LOG_EXIT_SYS, "get uid and gid of: ", logrunner) ;
-                }
 
-                log_trace("create directory: ", dst) ;
-                if (!dir_create_parent(dst, 0755)) {
-                    cleanup(ares, areslen) ;
-                    log_dieusys(LOG_EXIT_SYS, "create directory: ", ares[aresid].sa.s + ares[aresid].logger.destination) ;
-                }
+        if ((pres->type == TYPE_CLASSIC || pres->type == TYPE_ONESHOT) && pres->logger.want) {
+
+            /** Creation of the logger destination. This is made here to avoid
+             * issues on tmpfs logger directory destination */
+            uid_t log_uid ;
+            gid_t log_gid ;
+            char *logrunner = pres->sa.s + pres->logger.execute.run.runas ;
+            char *dst = pres->sa.s + pres->logger.destination ;
+
+            if (!youruid(&log_uid, logrunner) || !yourgid(&log_gid, log_uid)) {
+                cleanup(toclean, ntoclean) ;
+                log_dieusys(LOG_EXIT_SYS, "get uid and gid of: ", logrunner) ;
+            }
+
+            log_trace("create directory: ", dst) ;
+            if (!dir_create_parent(dst, 0755)) {
+                cleanup(toclean, ntoclean) ;
+                log_dieusys(LOG_EXIT_SYS, "create directory: ", dst) ;
+            }
 
-                if (!ares[aresid].owner && (strcmp(ares[aresid].sa.s + ares[aresid].logger.execute.run.build, "custom"))) {
+            if (!pres->owner && (strcmp(pres->sa.s + pres->logger.execute.run.build, "custom"))) {
 
-                    if (chown(dst, log_uid, log_gid) == -1) {
-                        cleanup(ares, areslen) ;
-                        log_dieusys(LOG_EXIT_SYS, "chown: ", dst) ;
-                    }
+                if (chown(dst, log_uid, log_gid) == -1) {
+                    cleanup(toclean, ntoclean) ;
+                    log_dieusys(LOG_EXIT_SYS, "chown: ", dst) ;
                 }
             }
         }
@@ -306,12 +311,12 @@ void sanitize_init(unsigned int *alist, unsigned int alen, graph_t *g, resolve_s
         state_set_flag(&sta, STATE_FLAGS_TOINIT, STATE_FLAGS_FALSE) ;
         state_set_flag(&sta, STATE_FLAGS_ISSUPERVISED, STATE_FLAGS_TRUE) ;
 
-        if (!state_write(&sta, &ares[aresid])) {
-            cleanup(ares, areslen) ;
-            log_dieusys(LOG_EXIT_SYS, "write status file of: ", sa + ares[aresid].name) ;
+        if (!state_write(&sta, pres)) {
+            cleanup(toclean, ntoclean) ;
+            log_dieusys(LOG_EXIT_SYS, "write status file of: ", sa + pres->name) ;
         }
 
-        if (!msg[aresid])
+        if (!msg[pos])
             log_info("Initialized successfully: ", name) ;
     }
 }
diff --git a/src/lib66/service/deps-lib/deps b/src/lib66/service/deps-lib/deps
index 6df5dced719c4209b6f856c54c5e4cfd99ae53a1..d12958fd884fff7adc49b0fba8e892548380c732 100644
--- a/src/lib66/service/deps-lib/deps
+++ b/src/lib66/service/deps-lib/deps
@@ -7,9 +7,8 @@ service_frontend_src.o
 service_graph_build.o
 service_graph_collect.o
 service_graph_g.o
+service_hash.o
 service_is_g.o
-service_resolve_array_free.o
-service_resolve_array_search.o
 service_resolve_copy.o
 service_resolve_get_field_tosa.o
 service_resolve_modify_field.o
diff --git a/src/lib66/service/service_enable_disable.c b/src/lib66/service/service_enable_disable.c
index 94ab869558a8d267b43a9ed1a7a3a87ee49d01f6..61965ffcc619a06c5e7979d5039c54af9e544b8c 100644
--- a/src/lib66/service/service_enable_disable.c
+++ b/src/lib66/service/service_enable_disable.c
@@ -17,6 +17,7 @@
 
 #include <oblibs/log.h>
 #include <oblibs/graph.h>
+#include <oblibs/stack.h>
 #include <oblibs/sastr.h>
 
 #include <skalibs/stralloc.h>
@@ -27,13 +28,13 @@
 #include <66/enum.h>
 #include <66/ssexec.h>
 
-static void service_enable_disable_deps(graph_t *g, unsigned int idx, resolve_service_t *ares, unsigned int areslen, uint8_t action, unsigned int *visit, uint8_t propagate, ssexec_t *info)
+static void service_enable_disable_deps(graph_t *g, struct resolve_hash_s *hash, struct resolve_hash_s **hres, uint8_t action, uint8_t propagate, ssexec_t *info)
 {
     log_flow() ;
 
     size_t pos = 0 ;
     stralloc sa = STRALLOC_ZERO ;
-    resolve_service_t_ref res = &ares[idx] ;
+    resolve_service_t_ref res = &hash->res ;
 
     if (graph_matrix_get_edge_g_sa(&sa, g, res->sa.s + res->name, action ? 0 : 1, 0) < 0)
         log_dieu(LOG_EXIT_SYS, "get ", action ? "dependencies" : "required by" ," of: ", res->sa.s + res->name) ;
@@ -43,12 +44,15 @@ static void service_enable_disable_deps(graph_t *g, unsigned int idx, resolve_se
         FOREACH_SASTR(&sa, pos) {
 
             char *name = sa.s + pos ;
-            int aresid = service_resolve_array_search(ares, areslen, name) ;
-            if (aresid < 0)
+
+            struct resolve_hash_s *h = hash_search(hres, name) ;
+            if (h == NULL)
                 log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parse it?") ;
 
-            if (!visit[aresid])
-                service_enable_disable(g, aresid, ares, areslen, action, visit, propagate, info) ;
+            if (!h->visit) {
+                service_enable_disable(g, h, hres, action, propagate, info) ;
+                h->visit = 1 ;
+            }
         }
     }
 
@@ -57,27 +61,29 @@ static void service_enable_disable_deps(graph_t *g, unsigned int idx, resolve_se
 
 /** @action -> 0 disable
  * @action -> 1 enable */
-void service_enable_disable(graph_t *g, unsigned int idx, resolve_service_t *ares, unsigned int areslen, uint8_t action, unsigned int *visit, uint8_t propagate, ssexec_t *info)
+void service_enable_disable(graph_t *g, struct resolve_hash_s *hash, struct resolve_hash_s **hres, uint8_t action, uint8_t propagate, ssexec_t *info)
 {
     log_flow() ;
 
-    if (!visit[idx]) {
+    if (!hash->visit) {
 
-        resolve_service_t_ref res = &ares[idx] ;
+        resolve_service_t_ref res = &hash->res ;
         resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;
+        char const *treename = res->sa.s + (res->intree ? res->intree : res->treename) ;
 
         /** resolve file may already exist. Be sure to add it to the contents field of the tree.*/
         if (action)
-            tree_service_add(res->sa.s + (res->intree ? res->intree : res->treename), res->sa.s + res->name, info) ;
+            tree_service_add(treename, res->sa.s + res->name, info) ;
 
-        if (!service_resolve_modify_field(res, E_RESOLVE_SERVICE_ENABLED, !action ? "0" : "1"))
-            log_dieu(LOG_EXIT_SYS, "modify resolve file of: ", res->sa.s + res->name) ;
+        res->enabled = action ;
 
         if (!resolve_write_g(wres, res->sa.s + res->path.home, res->sa.s + res->name))
             log_dieu(LOG_EXIT_SYS, "write  resolve file of: ", res->sa.s + res->name) ;
 
         if (propagate)
-            service_enable_disable_deps(g, idx, ares, areslen, action, visit, propagate, info) ;
+            service_enable_disable_deps(g, hash, hres, action, propagate, info) ;
+
+        free(wres) ;
 
         /** the logger must be disabled to avoid to start it
          * with the 66 tree start <tree> command */
@@ -85,26 +91,27 @@ void service_enable_disable(graph_t *g, unsigned int idx, resolve_service_t *are
 
             char *name = res->sa.s + res->logger.name ;
 
-            int aresid = service_resolve_array_search(ares, areslen, name) ;
-            if (aresid < 0)
+            struct resolve_hash_s *h = hash_search(hres, name) ;
+            if (h == NULL)
                 log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parse it?") ;
 
-            if (!visit[aresid]) {
+            if (!h->visit) {
 
-                wres = resolve_set_struct(DATA_SERVICE, &ares[aresid]) ;
+                wres = resolve_set_struct(DATA_SERVICE,  &h->res) ;
 
                 if (action)
-                    tree_service_add(ares[aresid].sa.s + (ares[aresid].intree ? ares[aresid].intree : ares[aresid].treename), ares[aresid].sa.s + ares[aresid].name, info) ;
+                    tree_service_add(treename, h->res.sa.s + h->res.name, info) ;
 
-                if (!service_resolve_modify_field(&ares[aresid], E_RESOLVE_SERVICE_ENABLED, !action ? "0" : "1"))
-                    log_dieu(LOG_EXIT_SYS, "modify resolve file of: ", ares[aresid].sa.s + ares[aresid].name) ;
+                h->res.enabled = action ;
 
-                if (!resolve_write_g(wres, ares[aresid].sa.s + ares[aresid].path.home, ares[aresid].sa.s + ares[aresid].name))
-                    log_dieu(LOG_EXIT_SYS, "write  resolve file of: ", ares[aresid].sa.s + ares[aresid].name) ;
+                if (!resolve_write_g(wres, h->res.sa.s + h->res.path.home, h->res.sa.s + h->res.name))
+                    log_dieu(LOG_EXIT_SYS, "write  resolve file of: ", h->res.sa.s + h->res.name) ;
 
-                log_info("Disabled successfully service: ", name) ;
+                log_info("Disabled successfully: ", name) ;
 
-                visit[aresid] = 1 ;
+                h->visit = 1 ;
+
+                resolve_free(wres) ;
             }
         }
 
@@ -121,36 +128,37 @@ void service_enable_disable(graph_t *g, unsigned int idx, resolve_service_t *are
                 FOREACH_STK(&stk, pos) {
 
                     char *name = stk.s + pos ;
-                    int aresid = service_resolve_array_search(ares, areslen, name) ;
-                    if (aresid < 0)
+
+                    struct resolve_hash_s *h = hash_search(hres, name) ;
+                    if (h == NULL)
                         log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parse it?") ;
 
-                    if (!visit[aresid]) {
+                    if (!h->visit) {
 
-                        wres = resolve_set_struct(DATA_SERVICE, &ares[aresid]) ;
+                        wres = resolve_set_struct(DATA_SERVICE,  &h->res) ;
 
                         if (action)
-                            tree_service_add(ares[aresid].sa.s + (ares[aresid].intree ? ares[aresid].intree : ares[aresid].treename), ares[aresid].sa.s + ares[aresid].name, info) ;
+                            tree_service_add(treename, h->res.sa.s + h->res.name, info) ;
 
-                        if (!service_resolve_modify_field(&ares[aresid], E_RESOLVE_SERVICE_ENABLED, !action ? "0" : "1"))
-                            log_dieu(LOG_EXIT_SYS, "modify resolve file of: ", ares[aresid].sa.s + ares[aresid].name) ;
+                        h->res.enabled = action ;
 
-                        if (!resolve_write_g(wres, ares[aresid].sa.s + ares[aresid].path.home, ares[aresid].sa.s + ares[aresid].name))
-                            log_dieu(LOG_EXIT_SYS, "write  resolve file of: ", ares[aresid].sa.s + ares[aresid].name) ;
+                        if (!resolve_write_g(wres, h->res.sa.s + h->res.path.home, h->res.sa.s + h->res.name))
+                            log_dieu(LOG_EXIT_SYS, "write  resolve file of: ", h->res.sa.s + h->res.name) ;
 
-                        service_enable_disable_deps(g, aresid, ares, areslen, action, visit, propagate, info) ;
+                        service_enable_disable_deps(g, h, hres, action, propagate, info) ;
 
-                        visit[aresid] = 1 ;
+                        h->visit = 1 ;
 
-                        log_info(!action ? "Disabled" : "Enabled"," successfully service: ", ares[aresid].sa.s + ares[aresid].name) ;
+                        log_info(!action ? "Disabled" : "Enabled"," successfully service: ", h->res.sa.s + h->res.name) ;
+
+                        resolve_free(wres) ;
                     }
                 }
             }
         }
 
-        free(wres) ;
-        visit[idx] = 1 ;
+        hash->visit = 1 ;
 
-        log_info(!action ? "Disabled" : "Enabled"," successfully service: ", ares[idx].sa.s + ares[idx].name) ;
+        log_info(!action ? "Disabled" : "Enabled"," successfully: ", hash->res.sa.s + hash->res.name) ;
     }
 }
diff --git a/src/lib66/service/service_graph_build.c b/src/lib66/service/service_graph_build.c
index 09f2dc0ecec6ae9566e8ea52ef349deccc71602b..0a4d8fb92678e3a221354860309b31ed7334b735 100644
--- a/src/lib66/service/service_graph_build.c
+++ b/src/lib66/service/service_graph_build.c
@@ -23,8 +23,9 @@
 #include <66/service.h>
 #include <66/graph.h>
 #include <66/state.h>
+#include <66/hash.h>
 
-static void issupervised(char *store, resolve_service_t *ares, unsigned int areslen, char const *str)
+static void issupervised(char *store, struct resolve_hash_s **hres, char const *str)
 {
     size_t pos = 0, len = strlen(str) ;
     ss_state_t ste = STATE_ZERO ;
@@ -39,16 +40,16 @@ static void issupervised(char *store, resolve_service_t *ares, unsigned int ares
 
         char *name = stk.s + pos ;
 
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0) {
+        struct resolve_hash_s *hash = hash_search(hres, name) ;
+        if (hash == NULL) {
             log_warn("service: ", name, " not available -- ignore it") ;
             continue ;
         }
 
-        if (!state_check(&ares[aresid]))
+        if (!state_check(&hash->res))
             continue ;
 
-        if (!state_read(&ste, &ares[aresid]))
+        if (!state_read(&ste, &hash->res))
             continue ;
 
         if (ste.issupervised == STATE_FLAGS_TRUE)
@@ -60,23 +61,23 @@ static void issupervised(char *store, resolve_service_t *ares, unsigned int ares
     store[strlen(store) - 1] = 0 ;
 }
 
-void service_graph_build(graph_t *g, resolve_service_t *ares, unsigned int areslen, uint32_t flag)
+void service_graph_build(graph_t *g, struct resolve_hash_s **hres, uint32_t flag)
 {
     log_flow() ;
 
-    unsigned int pos = 0 ;
     ss_state_t ste = STATE_ZERO ;
     resolve_service_t_ref pres = 0 ;
+    struct resolve_hash_s *c, *tmp ;
 
-    for (; pos < areslen ; pos++) {
+    HASH_ITER(hh, *hres, c, tmp) {
 
-        pres = &ares[pos] ;
+        pres = &c->res ;
         char *service = pres->sa.s + pres->name ;
 
-        if (!state_check(&ares[pos]))
+        if (!state_check(pres))
             continue ;
 
-        if (!state_read(&ste, &ares[pos]))
+        if (!state_read(&ste, pres))
             continue ;
 
         if (ste.issupervised == STATE_FLAGS_FALSE && FLAGS_ISSET(flag, STATE_FLAGS_ISSUPERVISED)) {
@@ -95,7 +96,7 @@ void service_graph_build(graph_t *g, resolve_service_t *ares, unsigned int aresl
 
                 if (FLAGS_ISSET(flag, STATE_FLAGS_ISSUPERVISED)) {
 
-                    issupervised(store, ares, areslen, pres->sa.s + pres->dependencies.depends) ;
+                    issupervised(store, hres, pres->sa.s + pres->dependencies.depends) ;
 
                 } else {
 
@@ -114,7 +115,7 @@ void service_graph_build(graph_t *g, resolve_service_t *ares, unsigned int aresl
 
                 if (FLAGS_ISSET(flag, STATE_FLAGS_ISSUPERVISED)) {
 
-                    issupervised(store, ares, areslen, pres->sa.s + pres->dependencies.requiredby) ;
+                    issupervised(store, hres, pres->sa.s + pres->dependencies.requiredby) ;
 
                 } else {
 
diff --git a/src/lib66/service/service_graph_collect.c b/src/lib66/service/service_graph_collect.c
index b885e84c0db986730ca714bf2ee4a431e7cde670..99fcb3e64db79055d040aa1ef11377e8530623c7 100644
--- a/src/lib66/service/service_graph_collect.c
+++ b/src/lib66/service/service_graph_collect.c
@@ -27,12 +27,13 @@
 #include <66/state.h>
 #include <66/graph.h>
 #include <66/enum.h>
+#include <66/hash.h>
 
 /** list all services of the system dependending of the flag passed.
  * STATE_FLAGS_TOPARSE -> call sanitize_source
  * STATE_FLAGS_TOPROPAGATE -> it build with the dependencies/requiredby services.
  * STATE_FLAGS_ISSUPERVISED -> only keep already supervised service*/
-void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag)
+void service_graph_collect(graph_t *g, char const *slist, size_t slen, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag)
 {
     log_flow () ;
 
@@ -45,13 +46,11 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
     for (; pos < slen ; pos += strlen(slist + pos) + 1) {
 
         char const *name = slist + pos ;
+        struct resolve_hash_s *hash = hash_search(hres, name) ;
 
-        if (service_resolve_array_search(ares, (*areslen), name) < 0) {
+        if (hash == NULL) {
 
             resolve_service_t res = RESOLVE_SERVICE_ZERO ;
-            /** need to make a copy of the resolve due of the freed
-             * of the wres struct at the end of the process */
-            resolve_service_t cp = RESOLVE_SERVICE_ZERO ;
             wres = resolve_set_struct(DATA_SERVICE, &res) ;
 
             /** double pass with resolve_read.
@@ -90,10 +89,9 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
 
                 if (res.earlier) {
 
-                    if (!service_resolve_copy(&cp, &res))
-                        log_dieu(LOG_EXIT_SYS, "copy resolve file of: ", name, " -- please make a bug report") ;
-
-                    ares[(*areslen)++] = cp ;
+                    log_trace("add service: ", name, " to the graph selection") ;
+                    if (!hash_add(hres, name, res))
+                        log_dieu(LOG_EXIT_SYS, "append graph selection with: ", name) ;
                     continue ;
                 }
                 resolve_free(wres) ;
@@ -104,10 +102,9 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
 
                 if (ste.issupervised == STATE_FLAGS_TRUE) {
 
-                    if (!service_resolve_copy(&cp, &res))
-                        log_dieu(LOG_EXIT_SYS, "copy resolve file of: ", name, " -- please make a bug report") ;
-
-                    ares[(*areslen)++] = cp ;
+                    log_trace("add service: ", name, " to the graph selection") ;
+                    if (!hash_add(hres, name, res))
+                        log_dieu(LOG_EXIT_SYS, "append graph selection with: ", name) ;
 
                 } else {
                     resolve_free(wres) ;
@@ -116,10 +113,9 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
 
             } else {
 
-                if (!service_resolve_copy(&cp, &res))
-                    log_dieu(LOG_EXIT_SYS, "copy resolve file of: ", name, " -- please make a bug report") ;
-
-                ares[(*areslen)++] = cp ;
+                log_trace("add service: ", name, " to the graph selection") ;
+                if (!hash_add(hres, name, res))
+                    log_dieu(LOG_EXIT_SYS, "append graph selection with: ", name) ;
             }
 
             if (FLAGS_ISSET(flag, STATE_FLAGS_TOPROPAGATE)) {
@@ -132,7 +128,7 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
                     if (!stack_clean_string(&stk, res.sa.s + res.dependencies.depends, len))
                         log_dieusys(LOG_EXIT_SYS, "clean string") ;
 
-                    service_graph_collect(g, stk.s, stk.len, ares, areslen, info, flag) ;
+                    service_graph_collect(g, stk.s, stk.len, hres, info, flag) ;
 
                 }
 
@@ -144,11 +140,12 @@ void service_graph_collect(graph_t *g, char const *slist, size_t slen, resolve_s
                     if (!stack_clean_string(&stk, res.sa.s + res.dependencies.requiredby, len))
                         log_dieusys(LOG_EXIT_SYS, "clean string") ;
 
-                    service_graph_collect(g, stk.s, stk.len, ares, areslen, info, flag) ;
+                    service_graph_collect(g, stk.s, stk.len, hres, info, flag) ;
                 }
 
             }
-            resolve_free(wres) ;
+
+            free(wres) ;
         }
     }
 }
diff --git a/src/lib66/service/service_graph_g.c b/src/lib66/service/service_graph_g.c
index 7d806ccda27780cd4f335611191e4f46fe9a9aef..ac3f1ac6250125eb0e89347b9360307c2ed9249c 100644
--- a/src/lib66/service/service_graph_g.c
+++ b/src/lib66/service/service_graph_g.c
@@ -25,6 +25,7 @@
 #include <66/ssexec.h>
 #include <66/state.h>
 #include <66/enum.h>
+#include <66/hash.h>
 
 static void debug_flag(uint32_t flag)
 {
@@ -73,19 +74,19 @@ static void debug_flag(uint32_t flag)
     log_trace("requested flags to build the graph: ", req) ;
 }
 
-void service_graph_g(char const *slist, size_t slen, graph_t *graph, resolve_service_t *ares, unsigned int *areslen, ssexec_t *info, uint32_t flag)
+void service_graph_g(char const *slist, size_t slen, graph_t *graph, struct resolve_hash_s **hres, ssexec_t *info, uint32_t flag)
 {
     log_flow() ;
 
     debug_flag(flag) ;
 
-    service_graph_collect(graph, slist, slen, ares, areslen, info, flag) ;
+    service_graph_collect(graph, slist, slen, hres, info, flag) ;
 
-    if (!*areslen) {
+    if (!HASH_COUNT(*hres)) {
         /* avoid empty string */
         log_warn("no services matching the requirements at tree: ", info->treename.s) ;
         return ;
     }
 
-    service_graph_build(graph, ares, (*areslen), flag) ;
+    service_graph_build(graph, hres, flag) ;
 }
diff --git a/src/lib66/service/service_hash.c b/src/lib66/service/service_hash.c
new file mode 100644
index 0000000000000000000000000000000000000000..26d314e1476a2970b2d0d56802f8a04013ac377c
--- /dev/null
+++ b/src/lib66/service/service_hash.c
@@ -0,0 +1,70 @@
+/*
+ * service_hash.c
+ *
+ * Copyright (c) 2018-2023 Eric Vidal <eric@obarun.org>
+ *
+ * All rights reserved.
+ *
+ * This file is part of Obarun. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution.
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file./
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <oblibs/log.h>
+#include <oblibs/string.h>
+
+#include <66/service.h>
+#include <66/hash.h>
+
+#include <66/resolve.h>
+
+int hash_add(struct resolve_hash_s **hash, char const *name, resolve_service_t res)
+{
+    log_flow() ;
+
+	struct resolve_hash_s *s ;
+	s = (struct resolve_hash_s *)malloc(sizeof(*s));
+	if (s == NULL)
+		return 0 ;
+
+	memset(s, 0, sizeof(*s)) ;
+	s->visit = 0 ;
+	auto_strings(s->name, name) ;
+	s->res = res ;
+	HASH_ADD_STR(*hash, name, s) ;
+
+	return 1 ;
+}
+
+struct resolve_hash_s *hash_search(struct resolve_hash_s **hash, char const *name)
+{
+    log_flow() ;
+
+	struct resolve_hash_s *s ;
+	HASH_FIND_STR(*hash, name, s) ;
+	return s ;
+
+}
+
+int hash_count(struct resolve_hash_s **hash)
+{
+	return HASH_COUNT(*hash) ;
+}
+
+void hash_free(struct resolve_hash_s **hash)
+{
+    log_flow() ;
+
+	struct resolve_hash_s *c, *tmp ;
+
+	HASH_ITER(hh, *hash, c, tmp) {
+		stralloc_free(&c->res.sa) ;
+		HASH_DEL(*hash, c) ;
+		free(c) ;
+	}
+}
diff --git a/src/lib66/service/service_resolve_array_free.c b/src/lib66/service/service_resolve_array_free.c
deleted file mode 100644
index 9688b39d26e22021fe69f5f254cf0c314a933b51..0000000000000000000000000000000000000000
--- a/src/lib66/service/service_resolve_array_free.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * service_resolve_array_free.c
- *
- * Copyright (c) 2018-2023 Eric Vidal <eric@obarun.org>
- *
- * All rights reserved.
- *
- * This file is part of Obarun. It is subject to the license terms in
- * the LICENSE file found in the top-level directory of this
- * distribution.
- * This file may not be copied, modified, propagated, or distributed
- * except according to the terms contained in the LICENSE file./
- */
-
-#include <skalibs/stralloc.h>
-
-#include <66/service.h>
-
-void service_resolve_array_free(resolve_service_t *ares, unsigned int areslen)
-{
-
-    unsigned int pos = 0 ;
-    for (; pos < areslen ; pos++)
-        stralloc_free(&ares[pos].sa) ;
-}
-
diff --git a/src/lib66/service/service_resolve_array_search.c b/src/lib66/service/service_resolve_array_search.c
deleted file mode 100644
index 1872aba40f194306979789495b642df788c8e996..0000000000000000000000000000000000000000
--- a/src/lib66/service/service_resolve_array_search.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * service_resolve_array_search.c
- *
- * Copyright (c) 2018-2023 Eric Vidal <eric@obarun.org>
- *
- * All rights reserved.
- *
- * This file is part of Obarun. It is subject to the license terms in
- * the LICENSE file found in the top-level directory of this
- * distribution.
- * This file may not be copied, modified, propagated, or distributed
- * except according to the terms contained in the LICENSE file./
- */
-
-#include <string.h>
-#include <oblibs/log.h>
-#include <66/service.h>
-
-int service_resolve_array_search(resolve_service_t *ares, unsigned int areslen, char const *name)
-{
-    log_flow() ;
-
-    unsigned int pos = 0 ;
-
-    for (; pos < areslen ; pos++) {
-
-        char const *n = ares[pos].sa.s + ares[pos].name ;
-            if (!strcmp(name, n))
-                return pos ;
-    }
-
-    return -1 ;
-}
-
diff --git a/src/lib66/svc/svc_compute_ns.c b/src/lib66/svc/svc_compute_ns.c
index 266d3e3cfff8c3bd2022ff3609e713486f7012fe..b417f6bbff0680d5472335eb68ae411f361d5d0b 100644
--- a/src/lib66/svc/svc_compute_ns.c
+++ b/src/lib66/svc/svc_compute_ns.c
@@ -28,7 +28,7 @@
 #include <66/sanitize.h>
 
 /** sares -> services ares */
-int svc_compute_ns(resolve_service_t *sares, unsigned int sareslen, unsigned int saresid, uint8_t what, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate, pidservice_t *handled, unsigned int nhandled)
+int svc_compute_ns(resolve_service_t *res, uint8_t what, ssexec_t *info, char const *updown, uint8_t opt_updown, uint8_t reloadmsg,char const *data, uint8_t propagate, pidservice_t *handled, unsigned int nhandled)
 {
     log_flow() ;
 
@@ -39,12 +39,11 @@ int svc_compute_ns(resolve_service_t *sares, unsigned int sareslen, unsigned int
     _init_stack_(stk, SS_MAX_SERVICE * SS_MAX_SERVICE_NAME) ;
 
     unsigned int napid = 0 ;
-    unsigned int areslen = 0, list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1] ;
-    resolve_service_t ares[SS_MAX_SERVICE + 1] ;
+    unsigned int list[SS_MAX_SERVICE + 1], visit[SS_MAX_SERVICE + 1] ;
+    struct resolve_hash_s *hash = NULL ;
 
     memset(list, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
     memset(visit, 0, (SS_MAX_SERVICE + 1) * sizeof(unsigned int)) ;
-    memset(ares, 0, (SS_MAX_SERVICE + 1) * sizeof(resolve_service_t)) ;
     uint32_t gflag = STATE_FLAGS_TOPROPAGATE|STATE_FLAGS_WANTUP ;
 
     if (!propagate)
@@ -56,18 +55,18 @@ int svc_compute_ns(resolve_service_t *sares, unsigned int sareslen, unsigned int
         FLAGS_CLEAR(gflag, STATE_FLAGS_WANTUP) ;
     }
 
-    if (sares[saresid].dependencies.ncontents) {
+    if (res->dependencies.ncontents) {
 
         if (!stack_clean_string_g(&stk, res->sa.s + res->dependencies.contents))
             log_dieu(LOG_EXIT_SYS, "clean string") ;
 
     } else {
-        log_warn("empty ns: ", sares[saresid].sa.s + sares[saresid].name) ;
+        log_warn("empty ns: ", res->sa.s + res->name) ;
         return 0 ;
     }
 
     /** build the graph of the ns */
-    service_graph_g(stk.s, stk.len, &graph, ares, &areslen, info, gflag) ;
+    service_graph_g(stk.s, stk.len, &graph, &hash, info, gflag) ;
 
     if (!graph.mlen)
         log_die(LOG_EXIT_USER, "services selection is not supervised -- initiate its first") ;
@@ -76,28 +75,28 @@ int svc_compute_ns(resolve_service_t *sares, unsigned int sareslen, unsigned int
 
         char const *name = stk.s + pos ;
 
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0)
+        struct resolve_hash_s *h = hash_search(&hash, name) ;
+        if (h == NULL)
             log_die(LOG_EXIT_USER, "service: ", name, " not available -- did you parse it?") ;
 
-        if (ares[aresid].earlier) {
-            log_warn("ignoring ealier service: ", ares[aresid].sa.s + ares[aresid].name) ;
+        if (h->res.earlier) {
+            log_warn("ignoring ealier service: ", h->res.sa.s + h->res.name) ;
             continue ;
         }
-        graph_compute_visit(ares, aresid, visit, list, &graph, &napid, requiredby) ;
+        graph_compute_visit(*h, visit, list, &graph, &napid, requiredby) ;
     }
 
     if (!what)
-        sanitize_init(list, napid, &graph, ares, areslen) ;
+        sanitize_init(list, napid, &graph, &hash) ;
 
     pidservice_t apids[napid] ;
 
-    svc_init_array(list, napid, apids, &graph, ares, areslen, info, requiredby, gflag) ;
+    svc_init_array(list, napid, apids, &graph, &hash, info, requiredby, gflag) ;
 
-    r = svc_launch(apids, napid, what, &graph, ares, areslen, info, updown, opt_updown, reloadmsg, data, propagate) ;
+    r = svc_launch(apids, napid, what, &graph, &hash, info, updown, opt_updown, reloadmsg, data, propagate) ;
 
+    hash_free(&hash) ;
     graph_free_all(&graph) ;
-    service_resolve_array_free(ares, areslen) ;
 
     return r ;
 }
diff --git a/src/lib66/svc/svc_init_array.c b/src/lib66/svc/svc_init_array.c
index da7288f7671ae50b7002b718fbd3284b0159f110..c735caad74d7dd987574130d6eab1cc4426a13ef 100644
--- a/src/lib66/svc/svc_init_array.c
+++ b/src/lib66/svc/svc_init_array.c
@@ -42,7 +42,7 @@ static pidservice_t pidservice_init(unsigned int len)
     return pids ;
 }
 
-void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, uint8_t requiredby, uint32_t flag)
+void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apids, graph_t *g, struct resolve_hash_s **hres, ssexec_t *info, uint8_t requiredby, uint32_t flag)
 {
     log_flow() ;
 
@@ -55,20 +55,20 @@ void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apid
 
         char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[list[pos]].vertex ;
 
-        pids.aresid = service_resolve_array_search(ares, areslen, name) ;
-
-        if (pids.aresid < 0)
-            log_dieu(LOG_EXIT_SYS,"find ares id of: ", name, " -- please make a bug reports") ;
+        struct resolve_hash_s *hash = hash_search(hres, name) ;
+        if (hash == NULL)
+            log_dieu(LOG_EXIT_SYS,"find hash id of: ", name, " -- please make a bug reports") ;
 
+        pids.res = &hash->res ;
 
         if (FLAGS_ISSET(flag, STATE_FLAGS_TOPROPAGATE)) {
 
-            pids.nedge = graph_matrix_get_edge_g_sorted_list(pids.edge, g, name, requiredby, 1) ;
+            pids.nedge = graph_matrix_get_edge_g_sorted_list(pids.edge, g, name, requiredby, 0) ;
 
             if (pids.nedge < 0)
                 log_dieu(LOG_EXIT_SYS,"get sorted ", requiredby ? "required by" : "dependency", " list of service: ", name) ;
 
-            pids.nnotif = graph_matrix_get_edge_g_sorted_list(pids.notif, g, name, !requiredby, 1) ;
+            pids.nnotif = graph_matrix_get_edge_g_sorted_list(pids.notif, g, name, !requiredby, 0) ;
 
             if (pids.nnotif < 0)
                 log_dieu(LOG_EXIT_SYS,"get sorted ", !requiredby ? "required by" : "dependency", " list of service: ", name) ;
@@ -79,11 +79,11 @@ void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apid
         if (pids.vertex < 0)
             log_dieu(LOG_EXIT_SYS, "get vertex id -- please make a bug report") ;
 
-        if (ares[pids.aresid].type != TYPE_CLASSIC) {
+        if (pids.res->type != TYPE_CLASSIC) {
 
                 ss_state_t sta = STATE_ZERO ;
 
-                if (!state_read(&sta, &ares[pids.aresid]))
+                if (!state_read(&sta, pids.res))
                     log_dieusys(LOG_EXIT_SYS, "read state file of: ", name) ;
 
                 if (sta.isup == STATE_FLAGS_TRUE)
@@ -95,7 +95,7 @@ void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apid
 
             s6_svstatus_t status ;
 
-            r = s6_svstatus_read(ares[pids.aresid].sa.s + ares[pids.aresid].live.scandir, &status) ;
+            r = s6_svstatus_read(pids.res->sa.s + pids.res->live.scandir, &status) ;
 
             pid_t pid = !r ? 0 : status.pid ;
 
@@ -109,4 +109,4 @@ void svc_init_array(unsigned int *list, unsigned int listlen, pidservice_t *apid
 
         apids[pos] = pids ;
     }
-}
\ No newline at end of file
+}
diff --git a/src/lib66/svc/svc_launch.c b/src/lib66/svc/svc_launch.c
index e637489c3792fb92190a86705af748db3f719b90..c4806d04f8fdf2ba23f8bc10eddb14b58e82a549 100644
--- a/src/lib66/svc/svc_launch.c
+++ b/src/lib66/svc/svc_launch.c
@@ -43,8 +43,6 @@
 static unsigned int napid = 0 ;
 static unsigned int npid = 0 ;
 
-static resolve_service_t_ref pares = 0 ;
-static unsigned int pareslen = 0 ;
 static char data[DATASIZE + 1] ;
 static char updown[4] ;
 static uint8_t opt_updown = 0 ;
@@ -109,19 +107,6 @@ static inline void kill_all(pidservice_t *apids)
     while (j--) kill(apids[j].pid, SIGKILL) ;
 }
 
-static int pidservice_get_id(pidservice_t *apids, unsigned int id)
-{
-    log_flow() ;
-
-    unsigned int pos = 0 ;
-
-    for (; pos < napid ; pos++) {
-        if (apids[pos].vertex == id)
-            return (unsigned int) pos ;
-    }
-    return -1 ;
-}
-
 static int check_action(pidservice_t *apids, unsigned int pos, unsigned int receive, unsigned int what)
 {
     unsigned int p = char2enum[receive] ;
@@ -161,16 +146,16 @@ static void notify(pidservice_t *apids, unsigned int pos, char const *sig, unsig
 
             if (apids[pos].notif[i] == apids[idx].vertex && !FLAGS_ISSET(apids[idx].state, flag))  {
 
-                size_t nlen = uint_fmt(fmt, apids[pos].aresid) ;
+                size_t nlen = uint_fmt(fmt, pos) ;
                 fmt[nlen] = 0 ;
                 size_t len = nlen + 1 + 2 ;
                 char s[len + 1] ;
                 auto_strings(s, fmt, ":", sig, "@") ;
 
-                log_trace("sends notification ", sig, " to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name, " from: ", pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name) ;
+                log_trace("sends notification ", sig, " to: ", apids[idx].res->sa.s + apids[idx].res->name, " from: ", apids[pos].res->sa.s + apids[pos].res->name) ;
 
                 if (write(apids[idx].pipe[1], s, strlen(s)) < 0)
-                    log_dieusys(LOG_EXIT_SYS, "send notif to: ", pares[apids[idx].aresid].sa.s + pares[apids[idx].aresid].name) ;
+                    log_dieusys(LOG_EXIT_SYS, "send notif to: ", apids[idx].res->sa.s + apids[idx].res->name) ;
             }
         }
     }
@@ -186,8 +171,8 @@ static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, u
 
     int fd = 0 ;
     char fmt[UINT_FMT] ;
-    char const *name = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].name ;
-    char const *scandir = pares[apids[pos].aresid].sa.s + pares[apids[pos].aresid].live.scandir ;
+    char const *name = apids[pos].res->sa.s + apids[pos].res->name ;
+    char const *scandir = apids[pos].res->sa.s + apids[pos].res->live.scandir ;
     size_t scandirlen = strlen(scandir) ;
     char file[scandirlen +  6] ;
 
@@ -197,7 +182,7 @@ static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, u
 
     if (success) {
 
-        if (pares[apids[pos].aresid].type == TYPE_CLASSIC) {
+        if (apids[pos].res->type == TYPE_CLASSIC) {
 
             fd = open_trunc(file) ;
             if (fd < 0)
@@ -205,17 +190,17 @@ static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, u
             fd_close(fd) ;
         }
 
-        notify(apids, pos, "F", what) ;
-
         fmt[uint_fmt(fmt, exitcode)] = 0 ;
 
-        log_1_warn("Unable to ", reloadmsg == 1 ? "restart" : reloadmsg > 1 ? "reload" : what ? "stop" : "start", " service: ", name, " -- exited with signal: ", fmt) ;
+        log_1_warn("unable to ", reloadmsg == 1 ? "restart" : reloadmsg > 1 ? "reload" : what ? "stop" : "start", " service: ", name, " -- exited with signal: ", fmt) ;
+
+        notify(apids, pos, "F", what) ;
 
         FLAGS_SET(apids[pos].state, SVC_FLAGS_BLOCK|SVC_FLAGS_FATAL) ;
 
     } else {
 
-        if (!state_messenger(&pares[apids[pos].aresid], STATE_FLAGS_ISUP, \
+        if (!state_messenger(apids[pos].res, STATE_FLAGS_ISUP, \
                                                         data[1] == 'a' || \
                                                         data[1] == 'h' || \
                                                         data[1] == 'U' || \
@@ -223,7 +208,7 @@ static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, u
                                                         ? STATE_FLAGS_TRUE : what ? STATE_FLAGS_FALSE : STATE_FLAGS_TRUE))
             log_dieu(LOG_EXIT_SYS, "send message to state of: ", name) ;
 
-        if (!pares[apids[pos].aresid].execute.down && pares[apids[pos].aresid].type == TYPE_CLASSIC) {
+        if (!apids[pos].res->execute.down && apids[pos].res->type == TYPE_CLASSIC) {
 
             if (!what) {
 
@@ -242,12 +227,12 @@ static void announce(unsigned int pos, pidservice_t *apids, unsigned int what, u
             }
         }
 
+        log_info("Successfully ", reloadmsg == 1 ? "restarted" : reloadmsg > 1 ? "reloaded" : what ? "stopped" : "started", " service: ", name) ;
+
         notify(apids, pos, what ? "D" : "U", what) ;
 
         FLAGS_CLEAR(apids[pos].state, SVC_FLAGS_BLOCK) ;
         FLAGS_SET(apids[pos].state, flag|SVC_FLAGS_UNBLOCK) ;
-
-        log_info("Successfully ", reloadmsg == 1 ? "restarted" : reloadmsg > 1 ? "reloaded" : what ? "stopped" : "started", " service: ", name) ;
     }
 }
 
@@ -269,7 +254,7 @@ static int handle_signal(pidservice_t *apids, unsigned int what, graph_t *graph,
                 for (;;) {
 
                     unsigned int pos = 0 ;
-                    int wstat ;
+                    int wstat = 0 ;
                     pid_t r = wait_nohang(&wstat) ;
 
                     if (r < 0) {
@@ -290,17 +275,16 @@ static int handle_signal(pidservice_t *apids, unsigned int what, graph_t *graph,
                         if (!WIFSIGNALED(wstat) && !WEXITSTATUS(wstat)) {
 
                             announce(pos, apids, what, 0, 0) ;
+                            npid-- ;
 
                         } else {
 
                             ok = WIFSIGNALED(wstat) ? WTERMSIG(wstat) : WEXITSTATUS(wstat) ;
                             announce(pos, apids, what, 1, ok) ;
-
+                            npid-- ;
                             kill_all(apids) ;
                             break ;
                         }
-
-                        npid-- ;
                     }
                 }
                 break ;
@@ -318,23 +302,23 @@ static int handle_signal(pidservice_t *apids, unsigned int what, graph_t *graph,
     return ok ;
 }
 
-unsigned int compute_timeout(unsigned int idx, unsigned int what)
+unsigned int compute_timeout(resolve_service_t *res, unsigned int what)
 {
     unsigned int timeout = 0 ;
 
     if (!what) {
 
-        if (pares[idx].type == TYPE_ONESHOT && pares[idx].execute.timeout.up)
-            timeout = pares[idx].execute.timeout.up ;
-        else if (pares[idx].type == TYPE_CLASSIC && pares[idx].execute.timeout.kill)
-            timeout = pares[idx].execute.timeout.kill ;
+        if (res->type == TYPE_ONESHOT && res->execute.timeout.up)
+            timeout = res->execute.timeout.up ;
+        else if (res->type == TYPE_CLASSIC && res->execute.timeout.kill)
+            timeout = res->execute.timeout.kill ;
 
     } else {
 
-        if (pares[idx].type == TYPE_ONESHOT && pares[idx].execute.timeout.down)
-            timeout = pares[idx].execute.timeout.down ;
-        else if (pares[idx].type == TYPE_CLASSIC && pares[idx].execute.timeout.finish)
-            timeout = pares[idx].execute.timeout.finish ;
+        if (res->type == TYPE_ONESHOT && res->execute.timeout.down)
+            timeout = res->execute.timeout.down ;
+        else if (res->type == TYPE_CLASSIC && res->execute.timeout.finish)
+            timeout = res->execute.timeout.finish ;
     }
 
     if (!timeout && PINFO->opt_timeout)
@@ -348,8 +332,7 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
 {
     log_flow() ;
 
-    unsigned int pidx = apids[idx].aresid ;
-    uint8_t type = pares[pidx].type ;
+    uint8_t type = apids[idx].res->type ;
 
     pid_t pid ;
     int wstat ;
@@ -358,17 +341,17 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
 
     unsigned int timeout = 0 ;
 
-    timeout = compute_timeout(pidx, what) ;
+    timeout = compute_timeout(apids[idx].res, what) ;
 
     tfmt[uint_fmt(tfmt, timeout)] = 0 ;
 
     if (type == TYPE_CLASSIC) {
 
-        char *scandir = pares[pidx].sa.s + pares[pidx].live.scandir ;
+        char *scandir = apids[idx].res->sa.s + apids[idx].res->live.scandir ;
 
         if (updown[2] == 'U' || updown[2] == 'D' || updown[2] == 'R') {
 
-            if (!pares[pidx].notify)
+            if (!apids[idx].res->notify)
                 updown[2] = updown[2] == 'U' ? 'u' : updown[2] == 'D' ? 'd' : updown[2] == 'R' ? 'r' : updown[2] ;
 
         }
@@ -402,17 +385,9 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
 
     } else if (type == TYPE_ONESHOT) {
 
-        char *sa = pares[pidx].sa.s ;
-        char *name = sa + pares[pidx].name ;
-        size_t namelen = strlen(name) ;
-        char *home = pares[pidx].sa.s + pares[pidx].path.home ;
-        size_t homelen = strlen(home) ;
-
-        char script[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 7 + 1] ;
-        auto_strings(script, home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", name) ;
-
-        char *oneshotdir = pares[pidx].sa.s + pares[pidx].live.oneshotddir ;
-        char *scandir = pares[pidx].sa.s + pares[pidx].live.scandir ;
+        char *servicedir = apids[idx].res->sa.s + apids[idx].res->live.servicedir ;
+        char *oneshotdir = apids[idx].res->sa.s + apids[idx].res->live.oneshotddir ;
+        char *scandir = apids[idx].res->sa.s + apids[idx].res->live.scandir ;
         char oneshot[strlen(oneshotdir) + 2 + 1] ;
         auto_strings(oneshot, oneshotdir, "/s") ;
 
@@ -427,7 +402,7 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
         newargv[m++] = "--" ;
         newargv[m++] = oneshot ;
         newargv[m++] = !what ? "up" : "down" ;
-        newargv[m++] = script ;
+        newargv[m++] = servicedir ;
         newargv[m++] = 0 ;
 
         log_trace("sending ", !what ? "up" : "down", " to: ", scandir) ;
@@ -457,7 +432,7 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
                 newargv[m++] = "--" ;
                 newargv[m++] = oneshot ;
                 newargv[m++] = "up" ;
-                newargv[m++] = script ;
+                newargv[m++] = servicedir ;
                 newargv[m++] = 0 ;
 
                 pid = child_spawn0(newargv[0], newargv, (char const *const *) environ) ;
@@ -478,19 +453,19 @@ static int doit(pidservice_t *apids, unsigned int napid, unsigned int idx, unsig
 
     } else if (type == TYPE_MODULE) {
 
-        return svc_compute_ns(pares, pareslen, pidx, what, PINFO, updown, opt_updown, reloadmsg, data, PROPAGATE, apids, napid) ;
+        return svc_compute_ns(apids[idx].res, what, PINFO, updown, opt_updown, reloadmsg, data, PROPAGATE, apids, napid) ;
     }
 
     /* should be never reached*/
     return 0 ;
 }
 
-static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, tain *deadline)
+static int async_deps(struct resolve_hash_s **hres, pidservice_t *apids, unsigned int i, unsigned int what, ssexec_t *info, tain *deadline)
 {
     log_flow() ;
 
     int r ;
-    unsigned int pos = 0, id = 0, ilog = 0, idx = 0 ;
+    unsigned int pos = 0, id = 0, idx = 0 ;
     char buf[(UINT_FMT*2)*SS_MAX_SERVICE + 1] ;
 
     tain dead ;
@@ -504,7 +479,7 @@ static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ss
 
     memset(visit, 0, (n + 1) * sizeof(unsigned int));
 
-    log_trace("waiting dependencies for: ", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name) ;
+    log_trace("waiting dependencies for: ", apids[i].res->sa.s + apids[i].res->name) ;
 
     while (pos < n) {
 
@@ -515,7 +490,7 @@ static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ss
 
         if (!r) {
             errno = ETIMEDOUT ;
-            log_dieusys(LOG_EXIT_SYS,"time out", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name) ;
+            log_dieusys(LOG_EXIT_SYS,"timed out", apids[i].res->sa.s + apids[i].res->name) ;
         }
 
         if (x.revents & IOPAUSE_READ) {
@@ -547,11 +522,11 @@ static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ss
 
                 /**
                  * the received string have the format:
-                 *      index_of_the_ares_array_of_the_service_dependency:signal_receive
+                 *      apids_array_id:signal_receive
                  *
                  * typically:
                  *      - 10:D
-                 *      - 30:u
+                 *      - 8:u
                  *      - ...
                  *
                  * Split it and check the signal receive.*/
@@ -566,24 +541,18 @@ static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ss
                 if (!uint0_scan(line, &id))
                     log_dieusys(LOG_EXIT_SYS, "retrieve service number -- please make a bug report") ;
 
-                ilog = id ;
-
-                log_trace(pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name, " acknowledges: ", pc, " from: ", pares[ilog].sa.s + pares[ilog].name) ;
+                log_trace(apids[i].res->sa.s + apids[i].res->name, " acknowledges: ", pc, " from: ", apids[id].res->sa.s + apids[id].res->name) ;
 
                 if (!visit[pos]) {
 
-                    id = pidservice_get_id(apids, id) ;
-                    if (id < 0)
-                        log_dieu(LOG_EXIT_SYS, "get apidservice id -- please make a bug report") ;
-
                     id = check_action(apids, id, c, what) ;
                     if (id < 0)
-                        log_die(LOG_EXIT_SYS, "service dependency: ", pares[ilog].sa.s + pares[ilog].name, " of: ", pares[apids[i].aresid].sa.s + pares[apids[i].aresid].name," crashed") ;
+                        log_die(LOG_EXIT_SYS, "service dependency: ", apids[id].res->sa.s + apids[id].res->name, " of: ", apids[i].res->sa.s + apids[i].res->name," crashed") ;
 
                     if (!id)
                         continue ;
 
-                    visit[pos++]++ ;
+                    visit[pos++] ;
                 }
             }
         }
@@ -594,7 +563,7 @@ static int async_deps(pidservice_t *apids, unsigned int i, unsigned int what, ss
     return 1 ;
 }
 
-static int async(pidservice_t *apids, unsigned int napid, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline)
+static int async(struct resolve_hash_s **hres, pidservice_t *apids, unsigned int napid, unsigned int i, unsigned int what, ssexec_t *info, graph_t *graph, tain *deadline)
 {
     log_flow() ;
 
@@ -611,17 +580,15 @@ static int async(pidservice_t *apids, unsigned int napid, unsigned int i, unsign
             FLAGS_SET(apids[i].state, SVC_FLAGS_BLOCK) ;
 
             if (apids[i].nedge)
-                if (!async_deps(apids, i, what, info, deadline))
-                    log_warnu_return(LOG_EXIT_ZERO, !what ? "start" : "stop", " dependencies of service: ", name) ;
+                if (!async_deps(hres, apids, i, what, info, deadline))
+                    log_warnu_return(LOG_EXIT_SYS, !what ? "start" : "stop", " dependencies of service: ", name) ;
 
             e = doit(apids, napid, i, what, deadline) ;
 
         } else {
 
             log_warn("skipping service: ", name, " -- already in ", what ? "stopping" : "starting", " process") ;
-
             notify(apids, i, what ? "d" : "u", what) ;
-
         }
 
     } else {
@@ -634,7 +601,7 @@ static int async(pidservice_t *apids, unsigned int napid, unsigned int i, unsign
     return e ;
 }
 
-int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *graph, resolve_service_t *ares, unsigned int areslen, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate)
+int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *graph, struct resolve_hash_s **hres, ssexec_t *info, char const *rise, uint8_t rise_opt, uint8_t msg, char const *signal, uint8_t propagate)
 {
     log_flow() ;
 
@@ -653,8 +620,6 @@ int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *gra
     opt_updown = rise_opt ;
     reloadmsg = msg ;
     auto_strings(data, signal) ;
-    pares = ares ;
-    pareslen = areslen ;
 
     if (info->opt_timeout)
         tain_from_millisecs(&deadline, info->timeout) ;
@@ -700,7 +665,7 @@ int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *gra
 
             close(apidservice[pos].pipe[1]) ;
 
-            e = async(apidservice, napid, pos, what, info, graph, &deadline) ;
+            e = async(hres, apidservice, napid, pos, what, info, graph, &deadline) ;
 
             goto end ;
         }
@@ -734,11 +699,11 @@ int svc_launch(pidservice_t *apids, unsigned int len, uint8_t what, graph_t *gra
 
     selfpipe_finish() ;
 
-    end:
-        for (pos = 0 ; pos < napid ; pos++) {
-            close(apidservice[pos].pipe[1]) ;
-            close(apidservice[pos].pipe[0]) ;
-        }
+    for (pos = 0 ; pos < napid ; pos++) {
+        close(apidservice[pos].pipe[1]) ;
+        close(apidservice[pos].pipe[0]) ;
+    }
 
+    end:
         return e ;
 }
diff --git a/src/lib66/svc/svc_unsupervise.c b/src/lib66/svc/svc_unsupervise.c
index 9533ae1ae64a2eed3cb201accd3572c94ba7ecfa..e48f0562385b95f70a1f95fda057d7a9da90e617 100644
--- a/src/lib66/svc/svc_unsupervise.c
+++ b/src/lib66/svc/svc_unsupervise.c
@@ -50,13 +50,12 @@ static void sanitize_it(resolve_service_t *res)
 }
 
 /** this function considers that the service is already down except for the logger */
-void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, resolve_service_t *ares, unsigned int areslen, ssexec_t *info)
+void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, struct resolve_hash_s **hres, ssexec_t *info)
 {
     log_flow() ;
 
     unsigned int pos = 0 ;
     size_t bpos = 0 ;
-    stralloc sa = STRALLOC_ZERO ;
 
     if (!alen)
         return ;
@@ -65,15 +64,15 @@ void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, resolve
 
         char *name = g->data.s + genalloc_s(graph_hash_t,&g->hash)[alist[pos]].vertex ;
 
-        int aresid = service_resolve_array_search(ares, areslen, name) ;
-        if (aresid < 0)
-            log_dieu(LOG_EXIT_SYS,"find ares id of: ", name, " -- please make a bug reports") ;
+        struct resolve_hash_s *hash = hash_search(hres, name) ;
+        if (hash == NULL)
+            log_dieu(LOG_EXIT_SYS,"find hash id of: ", name, " -- please make a bug reports") ;
 
-        sanitize_it(&ares[aresid]) ;
+        sanitize_it(&hash->res) ;
 
-        if (ares[aresid].type == TYPE_MODULE && ares[aresid].dependencies.ncontents) {
+        if (hash->res.type == TYPE_MODULE && hash->res.dependencies.ncontents) {
 
-            sa.len = 0, bpos = 0 ;
+            bpos = 0 ;
 
             _init_stack_(stk, strlen(hash->res.sa.s + hash->res.dependencies.contents)) ;
 
@@ -82,14 +81,13 @@ void svc_unsupervise(unsigned int *alist, unsigned int alen, graph_t *g, resolve
 
             FOREACH_STK(&stk, bpos) {
 
-                int aresid = service_resolve_array_search(ares, areslen, sa.s + bpos) ;
-                if (aresid < 0)
-                    log_dieu(LOG_EXIT_SYS,"find ares id of: ", sa.s + bpos, " -- please make a bug reports") ;
+                struct resolve_hash_s *h = hash_search(hres, stk.s + bpos) ;
+                if (h == NULL)
+                    log_dieu(LOG_EXIT_SYS,"find hash id of: ", stk.s + bpos, " -- please make a bug reports") ;
 
-                sanitize_it(&ares[aresid]) ;
+                sanitize_it(&h->res) ;
             }
         }
     }
-    stralloc_free(&sa) ;
 }