From a5da7c835fff693f07ad31e4739b0110ca83a5f8 Mon Sep 17 00:00:00 2001
From: obarun <eric@obarun.org>
Date: Mon, 1 Nov 2021 16:58:15 +1100
Subject: [PATCH] add @intree feature

---
 doc/frontend.md          |  53 +++---
 src/include/66/enum.h    |  27 ++-
 src/lib66/parser_utils.c |  45 ++++-
 src/lib66/ss_get_enum.c  |  25 +++
 src/lib66/tree_seed.c    | 400 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 511 insertions(+), 39 deletions(-)
 create mode 100644 src/lib66/tree_seed.c

diff --git a/doc/frontend.md b/doc/frontend.md
index 0a8b77cd..b952644c 100644
--- a/doc/frontend.md
+++ b/doc/frontend.md
@@ -275,36 +275,6 @@ This section is *mandatory*. (!)
 
     ---
 
-- @name
-
-    *Corresponds to the name of the service directory of [s6](https://skarnet.org/software/s6) and [s6-rc](https://skarnet.org/software/s6-rc) programs*.
-
-    Name of the service.
-
-    mandatory : no
-
-    syntax : inline
-
-    valid values :
-
-    * This field has no effect except for instantiated services. In such case the name must contain the complete name of the frontend service file.
-
-        For example, the following is valid:
-
-        ````
-            @name = tty@mine-@I
-        ````
-
-        where:
-
-        ````
-            @name = mine-@I
-        ````
-
-        is not for a frontend service file named tty@.
-
-    ---
-
 - @version
 
     *Without equivalent, this key is unique to 66 tools*.
@@ -657,6 +627,24 @@ This section is *mandatory*. (!)
 
         **Note** : 66 version must be higher than 0.3.0.1.
 
+- @intree
+
+    *Without equivalent, this key is unique to 66 tools*.
+
+    mandatory : no
+
+    syntax : inline
+
+    valid values :
+
+    * Any valid seed file name.
+
+    The service will automatically be activated at the tree name set in the *@intree* key value.
+
+    **Note** : The corresponding seed file *must* exist on your system to be effective.
+
+    ---
+
 ---
 
 ## Section: [start]
@@ -1082,7 +1070,9 @@ This prototype contain all valid section with all valid `key=value` pair.
 
 ```
     [main]
-
+    @type = classic,longrun,bundle,module
+    @description = ""
+    @version = 0.0.0
     @depends = ()
     @optsdepends = ()
     @extdepends = ()
@@ -1098,6 +1088,7 @@ This prototype contain all valid section with all valid `key=value` pair.
     @maxdeath =
     @down-signal =
     @hiercopy = ()
+    @intree =
 
     [start]
     @build = auto,custom
diff --git a/src/include/66/enum.h b/src/include/66/enum.h
index dae8ea7c..3bcabd4d 100644
--- a/src/include/66/enum.h
+++ b/src/include/66/enum.h
@@ -35,6 +35,7 @@ enum enum_main_e
     ENUM_MANDATORY ,
     ENUM_TIME ,
     ENUM_LOGOPTS ,
+    ENUM_SEED ,
     ENUM_ENDOFKEY
 } ;
 
@@ -73,6 +74,7 @@ enum enum_key_section_main_e
     KEY_MAIN_HIERCOPY ,
     KEY_MAIN_SIGNAL ,
     KEY_MAIN_FLAGS ,
+    KEY_MAIN_INTREE ,
     KEY_MAIN_ENDOFKEY
 } ;
 
@@ -242,6 +244,22 @@ enum enum_logopts_e
 
 extern char const *enum_str_logopts[] ;
 
+typedef enum enum_seed_e enum_seed_t, *enum_seed_t_ref ;
+enum enum_seed_e
+{
+
+    SEED_DEPENDS = 0 ,
+    SEED_REQUIREDBY ,
+    SEED_ENABLE ,
+    SEED_ALLOW ,
+    SEED_DENY ,
+    SEED_CURRENT ,
+    SEED_GROUP ,
+    SEED_SERVICES ,
+    SEED_ENDOFKEY
+
+} ;
+
 
 typedef struct enum_all_enum_s enum_all_enum_t, *enum_all_enum_t_ref ;
 struct enum_all_enum_s
@@ -250,6 +268,8 @@ struct enum_all_enum_s
     char const **str ;
 } ;
 
+
+
 extern ssize_t get_enum_by_key_one(char const *str, int const e) ;
 extern ssize_t get_enum_by_key(char const *str) ;
 extern char const *get_key_by_enum(int const e, int const key) ;
@@ -291,12 +311,11 @@ static key_description_t const main_section_list[] =
     { .name = &enum_str_key_section_main[KEY_MAIN_TYPE], .id = KEY_MAIN_TYPE, .expected = EXPECT_LINE },
     { .name = &enum_str_key_section_main[KEY_MAIN_VERSION], .id = KEY_MAIN_VERSION, .expected = EXPECT_LINE },
     { .name = &enum_str_key_section_main[KEY_MAIN_DESCRIPTION], .id = KEY_MAIN_DESCRIPTION, .expected = EXPECT_QUOTE },
+    { .name = &enum_str_key_section_main[KEY_MAIN_CONTENTS], .id = KEY_MAIN_CONTENTS, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_DEPENDS], .id = KEY_MAIN_DEPENDS, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_OPTSDEPS], .id = KEY_MAIN_OPTSDEPS, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_EXTDEPS], .id = KEY_MAIN_EXTDEPS, .expected = EXPECT_BRACKET },
-    { .name = &enum_str_key_section_main[KEY_MAIN_CONTENTS], .id = KEY_MAIN_CONTENTS, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_OPTIONS], .id = KEY_MAIN_OPTIONS, .expected = EXPECT_BRACKET },
-    { .name = &enum_str_key_section_main[KEY_MAIN_FLAGS], .id = KEY_MAIN_FLAGS, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_NOTIFY], .id = KEY_MAIN_NOTIFY, .expected = EXPECT_UINT },
     { .name = &enum_str_key_section_main[KEY_MAIN_USER], .id = KEY_MAIN_USER, .expected = EXPECT_BRACKET },
     { .name = &enum_str_key_section_main[KEY_MAIN_T_FINISH], .id = KEY_MAIN_T_FINISH, .expected = EXPECT_UINT },
@@ -304,8 +323,10 @@ static key_description_t const main_section_list[] =
     { .name = &enum_str_key_section_main[KEY_MAIN_T_UP], .id = KEY_MAIN_T_UP, .expected = EXPECT_UINT },
     { .name = &enum_str_key_section_main[KEY_MAIN_T_DOWN], .id = KEY_MAIN_T_DOWN, .expected = EXPECT_UINT },
     { .name = &enum_str_key_section_main[KEY_MAIN_DEATH], .id = KEY_MAIN_DEATH, .expected = EXPECT_UINT },
-    { .name = &enum_str_key_section_main[KEY_MAIN_SIGNAL], .id = KEY_MAIN_SIGNAL, .expected = EXPECT_UINT },
     { .name = &enum_str_key_section_main[KEY_MAIN_HIERCOPY], .id = KEY_MAIN_HIERCOPY, .expected = EXPECT_BRACKET },
+    { .name = &enum_str_key_section_main[KEY_MAIN_SIGNAL], .id = KEY_MAIN_SIGNAL, .expected = EXPECT_UINT },
+    { .name = &enum_str_key_section_main[KEY_MAIN_FLAGS], .id = KEY_MAIN_FLAGS, .expected = EXPECT_BRACKET },
+    { .name = &enum_str_key_section_main[KEY_MAIN_INTREE], .id = KEY_MAIN_INTREE, .expected = EXPECT_LINE },
     { .name = &enum_str_key_section_main[KEY_MAIN_ENDOFKEY] }
 } ;
 
diff --git a/src/lib66/parser_utils.c b/src/lib66/parser_utils.c
index b988bced..6d6377e9 100644
--- a/src/lib66/parser_utils.c
+++ b/src/lib66/parser_utils.c
@@ -496,7 +496,6 @@ int nocheck_toservice(keynocheck *nocheck,int svtype, sv_alltype *service)
     return 1 ;
 }
 
-
 /**********************************
  *      store
  * *******************************/
@@ -615,9 +614,12 @@ int keep_common(sv_alltype *service,keynocheck *nocheck,int svtype)
                 if (p == -1 && owner)
                 {
                     int e = 0 ;
-                    for (int i = 1; i < nb+1; i++)
-                        if (service->user[i] == owner) e = 1 ;
-
+                    for (int i = 1; i < nb+1; i++) {
+                        if (service->user[i] == owner) {
+                            e = 1 ;
+                            break ;
+                        }
+                    }
                     if (!e)
                         log_warnu_return(LOG_EXIT_ZERO,"use the service -- permission denied") ;
                 }
@@ -725,6 +727,11 @@ int keep_common(sv_alltype *service,keynocheck *nocheck,int svtype)
                 return 0 ;
             }
             break ;
+        case KEY_MAIN_INTREE:
+            service->cname.intree = keep.len ;
+            if (!stralloc_catb(&keep,chval,*chlen + 1))
+                log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ;
+            break ;
         default: log_warn_return(LOG_EXIT_ZERO,"unknown key: ",get_key_by_enum(ENUM_KEY_SECTION_MAIN,nocheck->idkey)) ;
 
     }
@@ -1231,7 +1238,6 @@ int check_valid_runas(keynocheck *ch)
     return 1 ;
 }
 
-
 void parse_err(int ierr,keynocheck *check)
 {
     log_flow() ;
@@ -1327,3 +1333,32 @@ int get_svtype_from_file(char const *file)
     stralloc_free(&tmp) ;
     return svtype ;
 }
+
+int get_svintree(sv_alltype *sv_before, char const *contents)
+{
+    log_flow() ;
+
+    int r ;
+    stralloc sa = STRALLOC_ZERO ;
+
+    if (!auto_stra(&sa,contents)) goto err ;
+
+    /** @intree may not exist */
+    r = sastr_find(&sa,get_key_by_enum(ENUM_KEY_SECTION_MAIN,KEY_MAIN_INTREE)) ;
+    if (r == -1) { sv_before->cname.intree == -1 ; goto freed ; }
+
+    if (!environ_get_val_of_key(&sa,get_key_by_enum(ENUM_KEY_SECTION_MAIN,KEY_MAIN_INTREE))) goto err ;
+
+    if (!sastr_clean_element(&sa)) goto err ;
+
+    sv_before->cname.intree = keep.len ;
+    if (!sastr_add_string(&keep, sa.s))
+        goto err ;
+
+    freed:
+    stralloc_free(&sa) ;
+    return 1 ;
+    err:
+        stralloc_free(&sa) ;
+        return 0 ;
+}
diff --git a/src/lib66/ss_get_enum.c b/src/lib66/ss_get_enum.c
index 3434108a..67e7275e 100644
--- a/src/lib66/ss_get_enum.c
+++ b/src/lib66/ss_get_enum.c
@@ -49,6 +49,7 @@ char const *enum_str_key_section_main[] = {
     "@hiercopy" ,
     "@down-signal" ,
     "@flags" ,
+    "@intree" ,
     0
 } ;
 
@@ -145,11 +146,34 @@ char const *enum_str_time[] = {
 
 char const *enum_str_logopts[] = {
     "producer-for" ,
+
     "consumer-for" ,
     "pipeline-name" ,
     0
 } ;
 
+char const *enum_str_seed[] = {
+
+    "depends" ,
+    "requiredby" ,
+    "enable" ,
+    "allow" ,
+    "deny" ,
+    "current" ,
+    "group" ,
+    "services" ,
+    /**
+     *
+     *
+     * rajouter owner qui correspond detenteur de la commande lancer
+     * cela permet de faire la difference entre allow et deny list
+     * et le vrai propriétaire de l'arbre
+     *
+     *
+     * */
+    0
+} ;
+
 enum_all_enum_t enum_all[] = {
 
     [ENUM_SECTION] = { .enum_all = SECTION_ENDOFKEY - ENUM_START, .str = enum_str_section } ,
@@ -166,6 +190,7 @@ enum_all_enum_t enum_all[] = {
     [ENUM_MANDATORY] = { .enum_all = MANDATORY_ENDOFKEY - ENUM_START , .str = enum_str_mandatory } ,
     [ENUM_TIME] = { .enum_all = TIME_ENDOFKEY - ENUM_START , .str = enum_str_time } ,
     [ENUM_LOGOPTS] = { .enum_all = LOGOPTS_ENDOFKEY - ENUM_START , .str = enum_str_logopts } ,
+    [ENUM_SEED] = { .enum_all = SEED_ENDOFKEY - ENUM_START , .str = enum_str_seed } ,
     [ENUM_ENDOFKEY] = { 0 }
 
 } ;
diff --git a/src/lib66/tree_seed.c b/src/lib66/tree_seed.c
new file mode 100644
index 00000000..2c8c49bd
--- /dev/null
+++ b/src/lib66/tree_seed.c
@@ -0,0 +1,400 @@
+/*
+ * tree_seed.c
+ *
+ * Copyright (c) 2018-2021 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 <66/tree.h>
+
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>//getuid
+
+#include <oblibs/string.h>
+#include <oblibs/files.h>
+#include <oblibs/log.h>
+#include <oblibs/environ.h>
+#include <oblibs/sastr.h>
+#include <oblibs/types.h>
+
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+
+#include <66/enum.h>
+#include <66/utils.h>
+#include <66/config.h>
+
+stralloc saseed = STRALLOC_ZERO ;
+
+void tree_seed_free(void)
+{
+
+    stralloc_free(&saseed) ;
+
+}
+
+static ssize_t tree_seed_get_key(char *table,char const *str)
+{
+    ssize_t pos = -1 ;
+
+    pos = get_len_until(str,'=') ;
+
+    if (pos == -1)
+        return -1 ;
+
+    auto_strings(table,str) ;
+
+    table[pos] = 0 ;
+
+    pos++ ; // remove '='
+
+    return pos ;
+}
+
+int tree_seed_parse_file(ss_tree_seed_t *seed, char const *seedpath)
+{
+    log_flow() ;
+
+    int r ;
+
+    stralloc sa = STRALLOC_ZERO ;
+    size_t pos = 0 ;
+
+    size_t filesize=file_get_size(seedpath) ;
+    if (!filesize) {
+
+        log_warn(seedpath," is empty") ;
+        goto err ;
+    }
+
+    r = openreadfileclose(seedpath, &sa, filesize) ;
+    if(!r) {
+
+        log_warnusys("open ", seedpath) ;
+        goto err ;
+    }
+
+    /** ensure that we have an empty line at the end of the string*/
+    if (!auto_stra(&sa,"\n"))
+        log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ;
+
+    if (!environ_get_clean_env(&sa)) {
+
+        log_warnu("clean seed file: ", seedpath) ;
+        goto err ;
+    }
+
+    if (!sastr_split_string_in_nline(&sa))
+        goto err ;
+
+    FOREACH_SASTR(&sa, pos) {
+
+
+        char *line = sa.s + pos, *key = 0, *val = 0 ;
+        size_t len = strlen(line) ;
+
+        char tmp[len + 1] ;
+
+        r = tree_seed_get_key(tmp, line) ;
+
+        /** should never happens */
+        if (r == -1) {
+            log_warn("invalid format of key: ",line," -- please make a bug report") ;
+            goto err ;
+        }
+
+        key = tmp ;
+        val = line + r ;
+
+        ssize_t e = get_enum_by_key(key) ;
+
+        switch (e) {
+
+            case SEED_DEPENDS :
+
+                seed->depends = saseed.len ;
+                if (!sastr_add_string(&saseed, val))
+                    goto err ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_REQUIREDBY :
+
+                seed->requiredby = saseed.len ;
+                if (!sastr_add_string(&saseed, val))
+                    goto err ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_ENABLE :
+
+                if (!strcmp(val,"true") || !strcmp(val,"True"))
+                    seed->enabled = 1 ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_ALLOW :
+
+                seed->allow = saseed.len ;
+                if (!sastr_add_string(&saseed, val))
+                    goto err ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_DENY :
+
+                seed->deny = saseed.len ;
+                if (!sastr_add_string(&saseed, val))
+                    goto err ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_CURRENT :
+
+                if (!strcmp(val,"true") || !strcmp(val,"True"))
+                    seed->current = 1 ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_GROUP :
+
+                if (strcmp(val,"boot") && strcmp(val,"admin") && strcmp(val,"user")) {
+
+                    log_warn("invalid group: ", val) ;
+                    goto err ;
+                }
+
+                seed->group = saseed.len ;
+                if (!sastr_add_string(&saseed, val))
+                    goto err ;
+
+                seed->nopts++ ;
+
+                break ;
+
+            case SEED_SERVICES :
+                {
+                    stralloc sv = STRALLOC_ZERO ;
+
+                    if (!sastr_clean_string_wdelim(&sv, val, ',') ||
+                        !sastr_rebuild_in_oneline(&sv)) {
+
+                            log_warnu("clean service list") ;
+                            goto err ;
+                    }
+
+                    seed->services = saseed.len ;
+                    if (!sastr_add_string(&saseed, sv.s))
+                        goto err ;
+
+                    seed->nopts++ ;
+
+                    break ;
+                }
+
+            default :
+
+                log_warn("unknown key: ", key, " -- ignoring") ;
+                break ;
+        }
+
+    }
+
+    stralloc_free(&sa) ;
+    return 1 ;
+    err:
+        stralloc_free(&sa) ;
+        return 0 ;
+}
+
+/** @Return -1 bad format e.g want REG get DIR
+ * @Return  0 fail
+ * @Return success */
+static int tree_seed_file_isvalid(char const *seedpath, char const *treename)
+{
+    log_flow() ;
+    int r ;
+    size_t slen = strlen(seedpath), tlen = strlen(treename) ;
+    char seed[slen + tlen + 1] ;
+    auto_strings(seed, seedpath, treename) ;
+
+    r = scan_mode(seed, S_IFREG) ;
+
+    return r ;
+}
+
+int tree_seed_resolve_path(stralloc *sa, char const *seed)
+{
+    log_flow() ;
+
+    int r ;
+
+    char *src = 0 ;
+    uid_t uid = getuid() ;
+    if (!uid) {
+
+        src = SS_SEED_ADMDIR ;
+
+    } else {
+
+        if (!set_ownerhome(sa, uid))
+            log_warnusys_return(LOG_EXIT_ZERO, "set home directory") ;
+
+        if (!auto_stra(sa, SS_SEED_USERDIR))
+            log_warnsys_return(LOG_EXIT_ZERO, "stralloc") ;
+
+        src = sa->s ;
+    }
+
+    r = tree_seed_file_isvalid(src, seed) ;
+    if (r == -1)
+        return 0 ;
+
+    if (!r) {
+
+        /** yeah double check because we can come from !uid */
+        src = SS_SEED_ADMDIR ;
+        r = tree_seed_file_isvalid(src, seed) ;
+        if (r == -1)
+            return 0 ;
+
+        if (!r) {
+
+            src = SS_SEED_SYSDIR ;
+            r = tree_seed_file_isvalid(src, seed) ;
+            if (r != 1)
+                return 0 ;
+
+        }
+    }
+
+    sa->len = 0 ;
+    if (!auto_stra(sa,src, seed))
+        log_warnsys_return(LOG_EXIT_ZERO, "stralloc") ;
+
+    return 1 ;
+
+}
+
+int tree_seed_isvalid(char const *seed)
+{
+    int e = 1 ;
+    stralloc src = STRALLOC_ZERO ;
+
+    if (!tree_seed_resolve_path(&src, seed))
+        e = 0 ;
+
+    stralloc_free(&src) ;
+
+    return e ;
+}
+
+int tree_seed_ismandatory(ss_tree_seed_t *seed, uint8_t check_service)
+{
+    log_flow() ;
+
+    uid_t uid = getuid() ;
+    size_t pos = 0 ;
+    stralloc sv = STRALLOC_ZERO ;
+
+    char *group = saseed.s + seed->group ;
+    char *service = saseed.s + seed->services ;
+
+    if (!uid && (!strcmp(group, "user"))) {
+
+        log_warn("Only regular user can use this seed") ;
+        goto err ;
+
+    } else if (uid && (!strcmp(group, "root"))) {
+
+        log_warn("Only root user can use this seed") ;
+        goto err ;
+    }
+
+    if (!strcmp(saseed.s + seed->name , "boot") && seed->enabled) {
+
+        log_warn("enable was asked for a tree on group boot -- ignoring enable request") ;
+        seed->enabled = 0 ;
+    }
+
+    if (check_service) {
+
+        stralloc sasrc = STRALLOC_ZERO ;
+
+        if (!stralloc_cats(&sv, service) ||
+            !sastr_clean_element(&sv)) {
+
+                log_warnu("clean service list") ;
+                stralloc_free(&sasrc) ;
+                goto err ;
+        }
+
+        FOREACH_SASTR(&sv, pos) {
+
+            char *s = sv.s + pos ;
+
+            /** ss_resolve_src already warn user */
+            if (!ss_resolve_src_path(&sasrc,s, uid, 0)) {
+
+                stralloc_free(&sasrc) ;
+                goto err ;
+            }
+        }
+
+        stralloc_free(&sasrc) ;
+    }
+
+    stralloc_free(&sv) ;
+    return 1 ;
+    err:
+        stralloc_free(&sv) ;
+        return 0 ;
+}
+
+/** Return 0 on fail
+ * Return 1 if tree need to be created
+ * Return 2 if tree already exist */
+int tree_seed_setseed(ss_tree_seed_t *seed, char const *treename, uint8_t check_service)
+{
+    log_flow() ;
+
+    stralloc src = STRALLOC_ZERO ;
+
+    if (!tree_seed_resolve_path(&src, treename))
+        goto err ;
+
+    seed->name = saseed.len ;
+    if (!sastr_add_string(&saseed, treename))
+        goto err ;
+
+    if (!tree_seed_parse_file(seed, src.s) ||
+        !tree_seed_ismandatory(seed, check_service))
+            goto err ;
+
+    stralloc_free(&src) ;
+    return 1 ;
+    err:
+        stralloc_free(&src) ;
+        return 0 ;
+}
-- 
GitLab