From a27767daa48eaafab5623cb046e1a367910c161f Mon Sep 17 00:00:00 2001
From: obarun <eric@obarun.org>
Date: Thu, 29 Sep 2022 20:42:56 +1100
Subject: [PATCH] revamp of the write process

---
 src/include/66/write.h                       |  31 +++
 src/lib66/write/deps-lib/deps                |  14 ++
 src/lib66/write/write_classic.c              |  57 +++++
 src/lib66/write/write_common.c               | 136 ++++++++++++
 src/lib66/write/write_environ.c              |  36 +++
 src/lib66/write/write_execute_scripts.c      | 104 +++++++++
 src/lib66/write/write_execute_scripts_user.c |  60 +++++
 src/lib66/write/write_logger.c               | 217 +++++++++++++++++++
 src/lib66/write/write_oneshot.c              |  41 ++++
 src/lib66/write/write_service.c              | 102 +++++++++
 src/lib66/write/write_uint.c                 |  30 +++
 11 files changed, 828 insertions(+)
 create mode 100644 src/include/66/write.h
 create mode 100644 src/lib66/write/deps-lib/deps
 create mode 100644 src/lib66/write/write_classic.c
 create mode 100644 src/lib66/write/write_common.c
 create mode 100644 src/lib66/write/write_environ.c
 create mode 100644 src/lib66/write/write_execute_scripts.c
 create mode 100644 src/lib66/write/write_execute_scripts_user.c
 create mode 100644 src/lib66/write/write_logger.c
 create mode 100644 src/lib66/write/write_oneshot.c
 create mode 100644 src/lib66/write/write_service.c
 create mode 100644 src/lib66/write/write_uint.c

diff --git a/src/include/66/write.h b/src/include/66/write.h
new file mode 100644
index 00000000..b4b24dc5
--- /dev/null
+++ b/src/include/66/write.h
@@ -0,0 +1,31 @@
+/*
+ * write.h
+ *
+ * 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./
+ */
+
+#ifndef SS_WRITE_H
+#define SS_WRITE_H
+
+#include <stdint.h>
+#include <66/service.h>
+
+extern int write_services(resolve_service_t *res, char const *workdir, uint8_t force) ;
+extern void write_classic(resolve_service_t *res, char const *dst, uint8_t force) ;
+extern void write_common(resolve_service_t *res, char const *dst) ;
+extern void write_environ(char const *name, char const *contents, char const *dst) ;
+extern void write_execute_scripts(resolve_service_t *res, resolve_service_addon_scripts_t *scripts, char const *file, char const *dst) ;
+extern void write_execute_scripts_user(resolve_service_t *res, resolve_service_addon_scripts_t *scripts, char const *file, char const *dst) ;
+extern void write_logger(resolve_service_t *res, char const *destination, uint8_t force) ;
+extern void write_oneshot(resolve_service_t *res, char const *dst) ;
+extern void write_uint(char const *dst, char const *name, uint32_t ui) ;
+
+#endif
diff --git a/src/lib66/write/deps-lib/deps b/src/lib66/write/deps-lib/deps
new file mode 100644
index 00000000..bd387c82
--- /dev/null
+++ b/src/lib66/write/deps-lib/deps
@@ -0,0 +1,14 @@
+write_classic.o
+write_common.o
+write_environ.o
+write_execute_scripts.o
+write_execute_scripts_user.o
+write_logger.o
+write_oneshot.o
+write_service.o
+write_uint.o
+-ls6
+-loblibs
+-lexecline
+-lskarnet
+
diff --git a/src/lib66/write/write_classic.c b/src/lib66/write/write_classic.c
new file mode 100644
index 00000000..e582c0a7
--- /dev/null
+++ b/src/lib66/write/write_classic.c
@@ -0,0 +1,57 @@
+/*
+ * write_classic.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 <stdint.h>
+
+#include <oblibs/log.h>
+#include <oblibs/string.h>
+
+#include <66/service.h>
+#include <66/write.h>
+
+/* dst e.g. /var/lib/66/system/<tree>/servicedirs/svc/<name> */
+
+void write_classic(resolve_service_t *res, char const *dst, uint8_t force)
+{
+    log_flow() ;
+
+    /**notification,timeout, ... */
+    write_common(res, dst) ;
+
+    /** run file */
+    write_execute_scripts(res, &res->execute.run, "run", dst) ;
+
+    /** finish file */
+    if (res->execute.finish.run_user)
+        write_execute_scripts(res, &res->execute.finish, "finish", dst) ;
+
+    /** run.user file */
+    write_execute_scripts_user(res, &res->execute.run, "run.user", dst) ;
+
+    /** finish.user file */
+    if (res->execute.finish.run_user)
+        write_execute_scripts_user(res, &res->execute.finish, "finish.user", dst) ;
+
+    /** logger */
+    if (res->logger.name) {
+
+        char destination[strlen(dst)] ;
+
+        if (!ob_dirname(destination, dst))
+            log_dieu(LOG_EXIT_SYS, "get dirname of: ", dst) ;
+
+        write_logger(res, destination, force) ;
+    }
+
+}
diff --git a/src/lib66/write/write_common.c b/src/lib66/write/write_common.c
new file mode 100644
index 00000000..e0d35104
--- /dev/null
+++ b/src/lib66/write/write_common.c
@@ -0,0 +1,136 @@
+/*
+ * write_common.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 <string.h>
+#include <errno.h>
+
+#include <oblibs/log.h>
+#include <oblibs/string.h>
+#include <oblibs/files.h>
+#include <oblibs/sastr.h>
+#include <oblibs/directory.h>
+#include <oblibs/types.h>
+
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+
+#include <66/service.h>
+#include <66/write.h>
+#include <66/constants.h>
+#include <66/environ.h>
+#include <66/enum.h>
+
+void write_common(resolve_service_t *res, char const *dst)
+{
+    log_flow() ;
+
+    char *time = 0 ;
+    size_t dstlen = strlen(dst) ;
+
+    /** down file */
+    if (res->execute.down)
+        if (!file_create_empty(dst, "down", 0644))
+            log_dieusys(LOG_EXIT_SYS, "create down file") ;
+
+    /** notification-fd */
+    if (res->notify)
+        write_uint(dst, SS_NOTIFICATION, res->notify) ;
+
+    /** timeout family
+     *
+     * Only write timeout file for classic service.
+     * All others services are read directly through
+     * the resolve file at start process. */
+    if (res->execute.timeout.kill)
+        write_uint(dst, "timeout-kill", res->execute.timeout.kill) ;
+
+    if (res->execute.timeout.finish)
+        write_uint(dst, "timeout-finish", res->execute.timeout.finish) ;
+
+    /** max-death-tally */
+    if (res->maxdeath)
+        write_uint(dst, SS_MAXDEATHTALLY, res->maxdeath) ;
+
+    /** down-signal */
+    if (res->execute.downsignal)
+        write_uint(dst, "down-signal", res->execute.downsignal) ;
+
+    /** environment
+     * environment for module is already written by the parse_module() function */
+    if (res->environ.env && res->type != TYPE_MODULE) {
+
+        stralloc dst = STRALLOC_ZERO ;
+        stralloc contents = STRALLOC_ZERO ;
+        char name[strlen(res->sa.s + res->name) + 2] ;
+        auto_strings(name, ".", res->sa.s + res->name) ;
+
+        if (!env_prepare_for_write(&dst, &contents, res))
+            log_dieu(LOG_EXIT_SYS, "prepare environment for: ", res->sa.s + res->name) ;
+
+        write_environ(name, contents.s, dst.s) ;
+
+        stralloc_free(&dst) ;
+        stralloc_free(&contents) ;
+    }
+
+    /** hierarchy copy */
+    if (res->hiercopy) {
+
+        int r ;
+        size_t pos = 0 ;
+        stralloc sa = STRALLOC_ZERO ;
+        char *src = res->sa.s + res->path.frontend ;
+        size_t srclen = strlen(src) ;
+
+        if (!sastr_clean_string(&sa, res->sa.s + res->hiercopy))
+            log_dieu(LOG_EXIT_SYS, "clean string") ;
+
+        FOREACH_SASTR(&sa, pos) {
+
+            char *what = sa.s + pos ;
+            size_t whatlen = strlen(what) ;
+            char tmp[SS_MAX_PATH_LEN + 1] ;
+            char basedir[srclen + 1] ;
+
+            if (!ob_dirname(basedir, src))
+                log_dieu(LOG_EXIT_SYS, "get dirname of: ", src) ;
+
+            if (what[0] == '.') {
+
+                if (!dir_beabsolute(tmp, src))
+                    log_dieusys(LOG_EXIT_SYS, "find absolute path of: ", what) ;
+
+            } else {
+
+                auto_strings(tmp, what) ;
+            }
+
+            r = scan_mode(tmp, S_IFDIR) ;
+            if (r <= 0) {
+
+                r = scan_mode(tmp, S_IFREG) ;
+                if (!r)
+                    log_dieusys(LOG_EXIT_SYS, "find: ", tmp) ;
+                if (r < 0) {
+                    errno = ENOTSUP ;
+                    log_diesys(LOG_EXIT_SYS, "invalid format of: ", tmp) ;
+                }
+            }
+
+            if (!hiercopy(tmp, dst))
+                log_dieusys(LOG_EXIT_SYS, "copy: ", tmp, " to: ", dst) ;
+        }
+    }
+}
diff --git a/src/lib66/write/write_environ.c b/src/lib66/write/write_environ.c
new file mode 100644
index 00000000..46482ef3
--- /dev/null
+++ b/src/lib66/write/write_environ.c
@@ -0,0 +1,36 @@
+/*
+ * write_environ.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 <string.h>
+
+#include <oblibs/log.h>
+#include <oblibs/files.h>
+#include <oblibs/types.h>
+
+void write_environ(char const *name, char const *contents, char const *dst)
+{
+    log_flow() ;
+
+    int r ;
+    size_t len = strlen(contents) ;
+
+    r = scan_mode(dst,S_IFDIR) ;
+    if (r < 0)
+        log_die(LOG_EXIT_SYS, "conflicting format of the environment directory: ", dst) ;
+    else if (!r)
+        log_dieusys(LOG_EXIT_SYS, "find environment directory: ", dst) ;
+
+    if (!file_write_unsafe(dst, name, contents, len))
+        log_dieusys(LOG_EXIT_SYS, "create file: ", dst, "/", name) ;
+}
diff --git a/src/lib66/write/write_execute_scripts.c b/src/lib66/write/write_execute_scripts.c
new file mode 100644
index 00000000..4bab4205
--- /dev/null
+++ b/src/lib66/write/write_execute_scripts.c
@@ -0,0 +1,104 @@
+/*
+ * write_execute_scripts.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 <string.h>
+#include <sys/stat.h>
+
+#include <oblibs/log.h>
+#include <oblibs/string.h>
+#include <oblibs/files.h>
+
+#include <66/service.h>
+#include <66/config.h>
+#include <66/constants.h>
+#include <66/enum.h>
+
+#include <s6/config.h>
+
+#ifndef FAKELEN
+#define FAKELEN strlen(run)
+#endif
+
+void write_execute_scripts(resolve_service_t *res, resolve_service_addon_scripts_t *scripts, char const *file, char const *dst)
+{
+    log_flow() ;
+
+    char write[strlen(dst) + 1 + strlen(file) + 1] ;
+    char *shebang = "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+    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) + strlen(res->sa.s + scripts->runas) + strlen(res->sa.s + res->environ.envdir) + SS_SYM_VERSION_LEN + (SS_MAX_PATH*2) + SS_MAX_PATH + strlen(file) + 117 + 1] ;
+
+    auto_strings(write, dst, "/", file) ;
+
+    /** shebang */
+    auto_strings(run, shebang) ;
+
+    if (res->logger.name && res->type != TYPE_ONESHOT)
+        auto_strings(run + FAKELEN, \
+                "fdmove 1 0\n", \
+                "s6-fdholder-retrieve ", \
+                res->sa.s + res->live.fdholderdir, "/s ", \
+                "\"" SS_FDHOLDER_PIPENAME "w-", \
+                res->sa.s + res->name, SS_LOG_SUFFIX "\"\n", \
+                "fdswap 0 1\n") ;
+
+
+    /** environ */
+    if (res->environ.env)
+        auto_strings(run + FAKELEN, res->sa.s + res->environ.envdir, SS_SYM_VERSION "\n") ;
+
+    /** log redirection for oneshot service */
+    if (res->logger.name && res->type == TYPE_ONESHOT) {
+
+        char verbo[UINT_FMT] ;
+        verbo[uint_fmt(verbo, VERBOSITY)] = 0 ;
+
+        size_t namelen = strlen(res->sa.s + res->name) ;
+        size_t syslen = res->owner ? strlen(res->sa.s + res->path.home) + 1 + 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)
+
+                auto_strings(dstlog, res->sa.s + res->path.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) ;
+        }
+
+        auto_strings(run + FAKELEN, "execl-toc -v", verbo, " -d ", dstlog, " -m 0755\n") ;
+        auto_strings(run + FAKELEN, "redirfd -a 1 ", dstlog, "/current\n") ;
+    }
+
+    /** runas */
+    if (!res->owner && scripts->runas)
+        auto_strings(run + FAKELEN, S6_BINPREFIX "s6-setuidgid ", res->sa.s + scripts->runas, "\n") ;
+
+    auto_strings(run + FAKELEN, "./", file, ".user") ;
+
+    if (!file_write_unsafe(dst, file, run, FAKELEN))
+        log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/", file) ;
+
+    if (chmod(write, 0755) < 0)
+        log_dieusys(LOG_EXIT_SYS, "chmod", write) ;
+
+}
diff --git a/src/lib66/write/write_execute_scripts_user.c b/src/lib66/write/write_execute_scripts_user.c
new file mode 100644
index 00000000..65ee0fff
--- /dev/null
+++ b/src/lib66/write/write_execute_scripts_user.c
@@ -0,0 +1,60 @@
+/*
+ * write_execute_scripts_user.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 <string.h>
+#include <sys/stat.h>
+
+#include <oblibs/log.h>
+#include <oblibs/string.h>
+#include <oblibs/files.h>
+
+#include <66/service.h>
+#include <66/config.h>
+#include <66/enum.h>
+
+#ifndef FAKELEN
+#define FAKELEN strlen(run)
+#endif
+
+void write_execute_scripts_user(resolve_service_t *res, resolve_service_addon_scripts_t *scripts, char const *file, char const *dst)
+{
+    log_flow() ;
+
+    char *shebang = scripts->shebang ? res->sa.s + scripts->shebang : SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+    size_t shebanglen = strlen(shebang) ;
+    size_t scriptlen = strlen(res->sa.s + scripts->run_user) ;
+    char run[2 + shebanglen + 15 + scriptlen + 1] ;
+    int build = !strcmp(res->sa.s + scripts->build, "custom") ? 1 : 0 ;
+    char write[strlen(dst) + 1 + strlen(file) + 1] ;
+
+    auto_strings(write, dst, "/", file) ;
+
+    auto_strings(run, "#!") ;
+
+    if (build && scripts->shebang)
+        auto_strings(run + FAKELEN, res->sa.s + scripts->shebang, "\n") ;
+    else
+        auto_strings(run + FAKELEN, \
+                    SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n",
+                    "fdmove -c 2 1\n") ;
+
+    auto_strings(run + FAKELEN, res->sa.s + scripts->run_user) ;
+
+    if (!file_write_unsafe(dst, file, run, FAKELEN))
+        log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/", file) ;
+
+    if (chmod(write, 0755) < 0)
+        log_dieusys(LOG_EXIT_SYS, "chmod", write) ;
+
+}
diff --git a/src/lib66/write/write_logger.c b/src/lib66/write/write_logger.c
new file mode 100644
index 00000000..46bcd69b
--- /dev/null
+++ b/src/lib66/write/write_logger.c
@@ -0,0 +1,217 @@
+/*
+ * write_logger.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 <string.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <oblibs/string.h>
+#include <oblibs/log.h>
+#include <oblibs/types.h>
+#include <oblibs/directory.h>
+#include <oblibs/files.h>
+
+#include <skalibs/types.h>
+
+#include <66/config.h>
+#include <66/write.h>
+#include <66/enum.h>
+#include <66/utils.h>
+#include <s6/config.h>
+#include <66/constants.h>
+
+#include <s6/config.h>
+
+#ifndef FAKELEN
+#define FAKELEN strlen(run)
+#endif
+
+/** @destination -> /var/lib/66/system/<tree>/servicedirs/svc/ */
+
+void write_logger(resolve_service_t *res, char const *destination, uint8_t force)
+{
+    log_flow() ;
+
+    int r ;
+    uid_t log_uid ;
+    gid_t log_gid ;
+    uint8_t owner = res->owner ;
+
+    int build = !strcmp(res->sa.s + res->logger.execute.run.build, "custom") ? 1 : 0 ;
+
+    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 ;
+
+    char dst[strlen(destination) + 1 + strlen(res->sa.s + res->logger.name) + 1] ;
+    auto_strings(dst, destination, "/", res->sa.s + res->logger.name) ;
+
+    r = scan_mode(dst, S_IFDIR) ;
+    if (r && force) {
+
+        if (!dir_rm_rf(dst))
+            log_dieusys(LOG_EXIT_SYS, "delete: ", dst) ;
+
+    } else if (r) {
+
+        log_warn("ignoring ", dst, " -- already exist") ;
+        return ;
+    }
+
+    if (!dir_create_parent(dst, 0755))
+        log_dieusys(LOG_EXIT_SYS, "create directory: ", dst) ;
+
+    if (res->logger.execute.timeout.kill)
+        write_uint(dst, "timeout-kill", res->logger.execute.timeout.kill) ;
+
+    if (res->logger.execute.timeout.finish)
+        write_uint(dst, "timeout-finish", res->logger.execute.timeout.finish) ;
+
+    /** 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 ;
+
+    } else
+        pback = "3" ;
+
+    /** file size */
+    if (res->logger.maxsize) {
+
+        max[uint32_fmt(max,res->logger.maxsize)] = 0 ;
+        pmax = max ;
+
+    } else
+        pmax = "1000000" ;
+
+    /** log destination */
+    size_t namelen = strlen(res->sa.s + res->name) ;
+    size_t syslen = res->owner ? strlen(res->sa.s + res->path.home) + 1 + 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)
+
+            auto_strings(dstlog, res->sa.s + res->path.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) ;
+    }
+
+    if (!dir_create_parent(dstlog, 0755))
+        log_dieusys(LOG_EXIT_SYS, "create directory: ", dstlog) ;
+
+    if (!owner && ((res->logger.execute.run.build == BUILD_AUTO) || (!res->logger.execute.run.build))) {
+
+        if (!youruid(&log_uid, logrunner) || !yourgid(&log_gid, log_uid))
+            log_dieusys(LOG_EXIT_SYS, "get uid and gid of: ", logrunner) ;
+
+        if (chown(dstlog, log_uid, log_gid) == -1)
+            log_dieusys(LOG_EXIT_SYS, "chown: ", dstlog) ;
+    }
+
+    {
+        /** dst/run file */
+        char *shebang = res->logger.execute.run.shebang ? res->sa.s + res->logger.execute.run.shebang : "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+
+        char run[strlen(shebang) + strlen(res->sa.s + res->live.fdholderdir) + SS_FDHOLDER_PIPENAME_LEN +  strlen(res->sa.s + res->logger.name) + strlen(S6_BINPREFIX) + strlen(res->sa.s + res->logger.execute.run.runas) + 53 + 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") ;
+
+        /** runas */
+        if (!res->owner && res->logger.execute.run.runas)
+            auto_strings(run + FAKELEN, S6_BINPREFIX "s6-setuidgid ", res->sa.s + res->logger.execute.run.runas, "\n") ;
+
+        auto_strings(run + FAKELEN, "./run.user\n") ;
+
+        if (!file_write_unsafe(dst, "run", run, FAKELEN))
+            log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/run.user") ;
+
+        char write[strlen(dst) + 5] ;
+        auto_strings(write, dst, "/run") ;
+
+        if (chmod(write, 0755) < 0)
+            log_dieusys(LOG_EXIT_SYS, "chmod", write) ;
+    }
+
+    if (!build) {
+
+        char *shebang = "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n" ;
+        size_t shebanglen = strlen(shebang) ;
+
+        char run[shebanglen + strlen(S6_BINPREFIX) + strlen(logrunner) + strlen(pback) + strlen(timestamp) + strlen(pmax) + strlen(dstlog) + 45 + 1] ;
+
+        auto_strings(run, \
+                    shebang, \
+                    "fdmove -c 2 1\n") ;
+
+        if (!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, " ", dstlog, "\n") ;
+
+        if (!file_write_unsafe(dst, "run.user", run, FAKELEN))
+            log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/run.user") ;
+
+        /** notification fd */
+        if (SS_LOGGER_NOTIFY)
+            write_uint(dst, SS_NOTIFICATION, 3) ;
+
+    } else {
+
+        if (!file_write_unsafe(dst, "run.user", res->sa.s + res->logger.execute.run.run_user, strlen(res->sa.s + res->logger.execute.run.run_user)))
+            log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/run.user") ;
+    }
+
+    char write[strlen(dst) + 10] ;
+    auto_strings(write, dst, "/run.user") ;
+
+    if (chmod(write, 0755) < 0)
+        log_dieusys(LOG_EXIT_SYS, "chmod", write) ;
+}
diff --git a/src/lib66/write/write_oneshot.c b/src/lib66/write/write_oneshot.c
new file mode 100644
index 00000000..673f975e
--- /dev/null
+++ b/src/lib66/write/write_oneshot.c
@@ -0,0 +1,41 @@
+/*
+ * write_oneshot.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 <oblibs/log.h>
+
+#include <66/service.h>
+#include <66/write.h>
+
+void write_oneshot(resolve_service_t *res, char const *dst)
+{
+    log_flow() ;
+
+    /**notification,timeout, ... */
+    write_common(res, dst) ;
+
+    /** run file */
+    write_execute_scripts(res, &res->execute.run, "up", dst) ;
+
+    /** finish file */
+    if (res->execute.finish.run_user)
+        write_execute_scripts(res, &res->execute.finish, "down", dst) ;
+
+    /** run.user file */
+    write_execute_scripts_user(res, &res->execute.run, "up.user", dst) ;
+
+    /** finish.user file */
+    if (res->execute.finish.run_user)
+        write_execute_scripts_user(res, &res->execute.finish, "down.user", dst) ;
+
+}
diff --git a/src/lib66/write/write_service.c b/src/lib66/write/write_service.c
new file mode 100644
index 00000000..d9d4c8da
--- /dev/null
+++ b/src/lib66/write/write_service.c
@@ -0,0 +1,102 @@
+/*
+ * write_service.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 <string.h>
+#include <stdint.h>
+
+#include <oblibs/string.h>
+#include <oblibs/log.h>
+#include <oblibs/types.h>
+#include <oblibs/directory.h>
+
+#include <66/service.h>
+#include <66/resolve.h>
+#include <66/enum.h>
+#include <66/write.h>
+#include <66/constants.h>
+
+/** @Return 0 on fail
+ * @Return 1 on success
+ * @Return 2 if the service is ignored
+ *
+ * @workdir -> /var/lib/66/system/<tree>/servicedirs/
+ * */
+int write_services(resolve_service_t *res, char const *workdir, uint8_t force)
+{
+    log_flow() ;
+
+    int r ;
+    size_t workdirlen = strlen(workdir) ;
+    char *name = res->sa.s + res->name ;
+    size_t namelen = strlen(name) ;
+    int type = res->type ;
+
+    {
+        resolve_service_t fres = RESOLVE_SERVICE_ZERO ;
+        resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, &fres) ;
+
+        if (resolve_read_g(wres, workdir, name))
+            if (fres.type != type && fres.live.disen)
+                log_die(LOG_EXIT_SYS, "Detection of incompatible type format for: ", name, " -- current: ", get_key_by_enum(ENUM_TYPE, type), " previous: ", get_key_by_enum(ENUM_TYPE, fres.type)) ;
+
+        resolve_free(wres) ;
+    }
+
+    char wname[workdirlen + SS_SVC_LEN + 1 + namelen + 1] ;
+    auto_strings(wname, workdir, SS_SVC, "/", name) ;
+
+    r = scan_mode(wname, S_IFDIR) ;
+    if (r < 0)
+        log_die(LOG_EXIT_SYS, "unvalide source: ", wname) ;
+
+    if ((r && force) || !r) {
+
+        if (dir_rm_rf(wname) < 0)
+            log_dieusys(LOG_EXIT_SYS, "remove: ", wname) ;
+
+        if (!dir_create_parent(wname, 0755))
+            log_dieusys(LOG_EXIT_SYS, "create ", wname) ;
+
+    } else if (r && !force) {
+
+        log_info("Ignoring: ", name, " service: already written") ;
+        return 2 ;
+    }
+
+    log_trace("Write service ", name, " ...") ;
+
+    switch(type) {
+
+        case TYPE_CLASSIC:
+
+            write_classic(res, wname, force) ;
+            break ;
+
+        case TYPE_ONESHOT:
+
+            write_oneshot(res, wname) ;
+            break ;
+
+        default:
+            log_die(LOG_EXIT_SYS, "unkown type: ", get_key_by_enum(ENUM_TYPE, type)) ;
+    }
+
+    return 1 ;
+}
+
+
+
+
diff --git a/src/lib66/write/write_uint.c b/src/lib66/write/write_uint.c
new file mode 100644
index 00000000..6ec3e99a
--- /dev/null
+++ b/src/lib66/write/write_uint.c
@@ -0,0 +1,30 @@
+/*
+ * write_uint.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 <stdint.h>
+
+#include <oblibs/log.h>
+#include <oblibs/files.h>
+
+#include <skalibs/types.h>
+
+void write_uint(char const *dst, char const *name, uint32_t ui)
+{
+    log_flow() ;
+
+    char number[UINT32_FMT] ;
+
+    if (!file_write_unsafe(dst, name, number, uint32_fmt(number,ui)))
+        log_dieusys(LOG_EXIT_SYS, "write: ", dst, "/", name) ;
+}
-- 
GitLab