/*
 * ssexec_scandir_create.c
 *
 * Copyright (c) 2018-2024 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 <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <stdarg.h>

#include <oblibs/string.h>
#include <oblibs/log.h>
#include <oblibs/directory.h>
#include <oblibs/types.h>
#include <oblibs/files.h>

#include <skalibs/djbunix.h>
#include <skalibs/buffer.h>
#include <skalibs/sgetopt.h>

#include <66/constants.h>
#include <66/utils.h>
#include <66/config.h>
#include <66/ssexec.h>
#include <66/enum.h>

#include <execline/config.h>
#include <s6/config.h>

#define CRASH 0
#define FINISH 1
#define INT 2
#define QUIT 3
#define TERM 4
#define USR1 5
#define USR2 6
#define PWR 7
#define WINCH 8

#define AUTO_CRTE_CHW 1
#define AUTO_CRTE_CHW_CHM 2
#define PERM1777 S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO

static uid_t OWNER ;
static char *OWNERSTR ;
static gid_t GIDOWNER ;
static char GIDSTR[GID_FMT] ;

static char const *skel = SS_SKEL_DIR ;
static char const *log_user = SS_LOGGER_RUNNER ;
static unsigned int BOOT = 0 ;
static unsigned int CONTAINER = SS_BOOT_CONTAINER ;
static unsigned int CATCH_LOG = SS_BOOT_CATCH_LOG ;
static size_t compute_buf_size(char const *str,...) ;
static size_t CONFIG_STR_LEN = 0 ;
static int BUF_FD ; // general buffer fd


static void inline auto_chown(char const *str)
{
    log_flow() ;

    log_trace("chown directory: ",str," to: ",OWNERSTR,":",GIDSTR) ;
    if (chown(str,OWNER,GIDOWNER) < 0)
        log_dieusys(LOG_EXIT_SYS,"chown: ",str) ;
}

static void inline auto_dir(char const *str,mode_t mode)
{
    log_flow() ;

    log_trace("create directory: ",str) ;
    if (!dir_create_parent(str,mode))
        log_dieusys(LOG_EXIT_SYS,"create directory: ",str) ;

    if (!OWNER) {
        size_t n = strlen(str), i = 0 ;
        char tmp[n + 1] ;
        for (; i < n ; i++) {

            if ((str[i] == '/') && i)
            {
                tmp[i] = 0 ;
                auto_chown(tmp) ;
            }
            tmp[i] = str[i] ;
        }
    }
    auto_chown(str) ;
}

static void inline auto_chmod(char const *str,mode_t mode)
{
    log_flow() ;

    if (chmod(str,mode) < 0)
        log_dieusys(LOG_EXIT_SYS,"chmod: ",str) ;
}

static void inline auto_file(char const *dst,char const *file,char const *contents,size_t conlen)
{
    log_flow() ;

    char f[strlen(dst) + 1 + strlen(file) + 1] ;
    auto_strings(f, dst, "/", file) ;

    log_trace("write file: ", f) ;
    if (!file_write_unsafe(dst,file,contents,conlen))
        log_dieusys(LOG_EXIT_SYS,"write file: ",dst,"/",file) ;

    auto_chown(f) ;
}

static void inline auto_check(char const *str,mode_t type,mode_t perm,int what)
{
    log_flow() ;

    int r ;
    r = scan_mode(str,S_IFDIR) ;
    if (r < 0) { errno = EEXIST ; log_diesys(LOG_EXIT_SYS,"conflicting format of: ",str) ; }
    if (!r)
    {
        auto_dir(str,type) ;
        if (what > 0) auto_chown(str) ;
        if (what > 1) auto_chmod(str,perm) ;
    }
}

static void inline auto_fifo(char const *str)
{
    log_flow() ;

    int r ;
    r = scan_mode(str,S_IFIFO) ;
    if (r < 0) { errno = EEXIST ; log_diesys(LOG_EXIT_SYS,"conflicting format of: ",str) ; }
    if (!r)
    {
        log_trace("create fifo: ",str) ;
        if (mkfifo(str, 0600) < 0)
            log_dieusys(LOG_EXIT_SYS,"create fifo: ",str) ;
    }
}

static void inline auto_rm(char const *str)
{
    log_flow() ;

    int r ;
    r = scan_mode(str,S_IFDIR) ;
    if (r > 0)
    {
        log_info("removing: ",str,"...") ;
        if (!dir_rm_rf(str)) log_dieusys(LOG_EXIT_SYS,"remove: ",str) ;
    }
}

static void inline log_perm(char const *str,uid_t *uid,gid_t *gid)
{
    log_flow() ;

    if (!youruid(uid,str)) log_dieusys(LOG_EXIT_SYS,"set uid of: ",str) ;
    if (!yourgid(gid,*uid)) log_dieusys(LOG_EXIT_SYS,"set gid of: ",str) ;
}

void inline shebang(buffer *b, char const *opts)
{
    log_flow() ;

    if (!auto_buf(b, "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb ", opts, "\n"))
        log_die_nomem("buffer") ;
}

void append_shutdown(buffer *b, char const *live, char const *opts)
{
    log_flow() ;

    if (!auto_buf(b,SS_BINPREFIX "66-shutdown ",opts))
        log_die_nomem("buffer") ;

    if (!CONTAINER)
        if (!auto_buf(b," -a"))
            log_die_nomem("buffer") ;

    if (!auto_buf(b," -l ",live," -- now\n"))
        log_die_nomem("buffer") ;

}

static size_t compute_buf_size(char const *str,...)
{

    va_list alist ;
    va_start(alist,str) ;
    size_t len = 0 ;

    while (str != 0) {
        len += strlen(str) ;
        str = va_arg(alist, char const *) ;
    }
    va_end(alist) ;

    return len ;
}

static buffer init_buffer(char const *dst, char const *file, size_t len)
{
    log_flow() ;

    int fd ;
    buffer b ;

    size_t dstlen = strlen(dst), filen = strlen(file) ;
    char w[dstlen + 1 + filen + 1] ;
    char buf[len + 1] ;
    auto_strings(w, dst, "/", file) ;

    fd = open_trunc(w) ;

    if (fd < 0  || ndelay_off(fd) < 0)
        log_die(LOG_EXIT_SYS,"open trunc") ;

    buffer_init(&b,&fd_writev, fd, buf, len) ;

    return b ;
}

void write_to_bufnclose(buffer *b, char const *dst, char const *file)
{
    if (!buffer_flush(b))
        log_dieusys(LOG_EXIT_SYS, "write to: ", dst, "/", file) ;

    fd_close(BUF_FD) ;
}

void write_shutdownd(char const *live, char const *scandir)
{
    log_flow() ;

    buffer b ;
    size_t blen = compute_buf_size(live, skel, 0) ;
    blen += 500 + CONFIG_STR_LEN ;
    size_t scandirlen = strlen(scandir) ;
    char shut[scandirlen + 1 + SS_BOOT_SHUTDOWND_LEN + 5 + 1] ;

    auto_strings(shut,scandir,"/",SS_BOOT_SHUTDOWND) ;

    auto_check(shut,0755,0755,AUTO_CRTE_CHW_CHM) ;

    auto_strings(shut + scandirlen + 1 + SS_BOOT_SHUTDOWND_LEN,"/fifo") ;

    auto_fifo(shut) ;

    shut[scandirlen + 1 + SS_BOOT_SHUTDOWND_LEN] = 0 ;

    b = init_buffer(shut, "run", blen) ;

    shebang(&b, "-P") ;
    if (!auto_buf(&b,
        SS_BINPREFIX "66-shutdownd -l ",
        live," -s ",skel," -g 3000"))
            log_die_nomem("buffer") ;

    if (CONTAINER)
        if (!auto_buf(&b," -B"))
            log_die_nomem("buffer") ;

    if (!CATCH_LOG)
        if (!auto_buf(&b," -c"))
            log_die_nomem("buffer") ;

    if (!auto_buf(&b,"\n"))
        log_die_nomem("buffer") ;

    write_to_bufnclose(&b, shut, "run") ;

    auto_strings(shut + scandirlen + 1 + SS_BOOT_SHUTDOWND_LEN,"/run") ;

    auto_chmod(shut,0755) ;
}

void write_bootlog(char const *live, char const *scandir)
{
    log_flow() ;

    int r ;
    uid_t uid = -1 ;
    gid_t gid = -1 ;
    size_t livelen = strlen(live), scandirlen = strlen(scandir), ownerlen = uid_fmt(OWNERSTR,OWNER), loglen = 0, blen = 0 ;
    buffer b ;
    char path[livelen + 4 + ownerlen + 1] ;
    char logdir[scandirlen + SS_SCANDIR_LEN + SS_LOG_SUFFIX_LEN + 1 + 5 + 1] ;

    /** run/66/scandir/uid_name/scandir-log */
    auto_strings(logdir,scandir,"/" SS_SCANDIR SS_LOG_SUFFIX) ;

    loglen = scandirlen + SS_SCANDIR_LEN + SS_LOG_SUFFIX_LEN + 1 ;

    auto_check(logdir,0755,0,AUTO_CRTE_CHW) ;

    /** make the fifo*/
    auto_strings(logdir + loglen, "/fifo") ;

    auto_fifo(logdir) ;
    auto_chown(logdir) ;

    /** set the log path for the run file
     * /run/66/log*/
    auto_strings(path,live,"log/",OWNERSTR) ;

    log_trace("create directory: ",path) ;
    r = dir_create_parent(path,02750) ;
    if (!r)
        log_dieusys(LOG_EXIT_SYS,"create: ",path) ;

    log_perm(log_user,&uid,&gid) ;

    if (chown(path,uid,gid) < 0)
        log_dieusys(LOG_EXIT_SYS,"chown: ",path) ;

    auto_chmod(path,02755) ;

    logdir[loglen] = 0 ;

    blen = compute_buf_size(live, logdir, log_user, path, 0) ;
    blen += 500 + CONFIG_STR_LEN;
    b = init_buffer(logdir, "run", blen) ;

    /** make run file */
    shebang(&b,"-P") ;
    if (CONTAINER) {

        if (!auto_buf(&b,EXECLINE_BINPREFIX "fdmove -c 1 2\n"))
            log_die_nomem("buffer") ;

    } else {

        if (!auto_buf(&b,
            EXECLINE_BINPREFIX "redirfd -w 1 /dev/null\n"))
                log_die_nomem("buffer") ;
    }

    if (!auto_buf(&b,
            EXECLINE_BINPREFIX "redirfd -rnb 0 fifo\n" \
            S6_BINPREFIX "s6-setuidgid ",
            log_user,
            "\n" S6_BINPREFIX "s6-log -bpd3 -- 1"))
                log_die_nomem("buffer") ;

    if (SS_LOGGER_TIMESTAMP < TIME_NONE)
        if (!auto_buf(&b, SS_LOGGER_TIMESTAMP == TIME_ISO ? " T " : " t "))
            log_die_nomem("buffer") ;

    if (!auto_buf(&b,path,"\n"))
        log_die_nomem("buffer") ;

    write_to_bufnclose(&b, logdir, "run") ;

    auto_file(logdir, SS_NOTIFICATION, "3\n",2) ;

    auto_strings(logdir + loglen,"/run") ;

    auto_chmod(logdir,0755) ;
    auto_chown(logdir) ;
}

void write_control(char const *scandir,char const *live, char const *filename, int file)
{
    log_flow() ;

    buffer b ;
    size_t scandirlen = strlen(scandir), filen = strlen(filename), blen = 0 ;
    char mode[scandirlen + SS_SVSCAN_LOG_LEN + filen + 1] ;

    auto_strings(mode,scandir,SS_SVSCAN_LOG) ;

    blen = compute_buf_size(live, scandir, 0) ;
    blen += 500 + CONFIG_STR_LEN ;

    b = init_buffer(mode, filename + 1, blen) ;

    shebang(&b,"-P") ;

    if (file == FINISH)
    {
        if (CONTAINER) {

            if (!auto_buf(&b,
                SS_BINPREFIX "execl-envfile ",live, SS_BOOT_CONTAINER_DIR "/",OWNERSTR,"\n" \
                EXECLINE_BINPREFIX "fdclose 1\n" \
                EXECLINE_BINPREFIX "fdclose 2\n" \
                EXECLINE_BINPREFIX "wait { }\n" \
                EXECLINE_BINPREFIX "foreground {\n" \
                SS_BINPREFIX "66-hpr -f -n -${HALTCODE} -l ",live," \n}\n" \
                EXECLINE_BINPREFIX "exit ${EXITCODE}\n"))
                    log_die_nomem("buffer") ;

        } else if (BOOT) {

            if (!auto_buf(&b,
                EXECLINE_BINPREFIX "redirfd -w 2 /dev/console\n" \
                EXECLINE_BINPREFIX "fdmove -c 1 2\n" \
                EXECLINE_BINPREFIX "foreground { " SS_BINPREFIX "66-echo -- \"scandir ",
                scandir," exited. Rebooting.\" }\n" \
                SS_BINPREFIX "66-hpr -r -f -l ",
                live,"\n"))
                    log_die_nomem("buffer") ;

        } else {

            if (!auto_buf(&b,
                SS_BINPREFIX "66-echo -- \"scandir ",
                scandir," stopped...\"\n"))
                    log_die_nomem("buffer") ;
        }
        goto write ;
    }

    if (file == CRASH)
    {

        if (CONTAINER) {

            if (!auto_buf(&b,
                EXECLINE_BINPREFIX "foreground {\n" \
                EXECLINE_BINPREFIX "fdmove -c 1 2\n" \
                SS_BINPREFIX "66-echo \"scandir crashed. Killing everythings and exiting.\"\n}\n" \
                EXECLINE_BINPREFIX "foreground {\n" \
                EXECLINE_BINPREFIX "66-nuke\n}\n" \
                EXECLINE_BINPREFIX "wait { }\n" \
                SS_BINPREFIX "66-hpr -f -n -p -l ",live,"\n"))
                    log_die_nomem("buffer") ;
        }
        else {

            if (!auto_buf(&b,
                EXECLINE_BINPREFIX "redirfd -w 2 /dev/console\n" \
                EXECLINE_BINPREFIX "fdmove -c 1 2\n" \
                EXECLINE_BINPREFIX "foreground { " SS_BINPREFIX "66-echo -- \"scandir ",
                scandir, " crashed."))
                    log_die_nomem("buffer") ;

            if (BOOT) {

                if (!auto_buf(&b,
                    " Rebooting.\" }\n" \
                    SS_BINPREFIX "66-hpr -r -f -l ",
                    live,"\n"))
                        log_die_nomem("buffer") ;

            } else if (!auto_buf(&b,"\" }\n"))
                log_die_nomem("buffer") ;
        }

        goto write ;
    }
    if (!BOOT) {

        if (!auto_buf(&b,
            EXECLINE_BINPREFIX "foreground { " SS_BINPREFIX "66 -v3 -l ",
            live," tree stop }\n"))
                log_die_nomem("buffer") ;

    }

    switch(file)
    {
        case PWR:
        case USR1:

            if (BOOT)
                append_shutdown(&b,live,"-p") ;

            break ;
        case USR2:

            if (BOOT)
                append_shutdown(&b,live,"-h") ;

            break ;
        case TERM:
        case QUIT:
            break ;

        case INT:

            if (BOOT)
                append_shutdown(&b,live,"-r") ;

            break ;

        case WINCH:
            break ;

        default:
            break ;
    }

    write:

        write_to_bufnclose(&b, mode, filename + 1) ;

        auto_strings(mode + scandirlen + SS_SVSCAN_LOG_LEN, filename) ;

        auto_chmod(mode,0755) ;
        auto_chown(mode) ;
}

void auto_empty_file(char const *dst, char const *filename, char const *contents)
{
    size_t dstlen = strlen(dst), filen = strlen(filename) ;

    char tmp[dstlen + filen + 1] ;
    auto_strings(tmp, dst, filename) ;

    if (!file_write_unsafe_g(tmp, contents))
        log_dieusys(LOG_EXIT_SYS, "create file: ", tmp) ;

    auto_chown(tmp) ;
}

static void create_service_skel(char const *service, char const *target, char const *notif, ssexec_t *info)
{
    size_t targetlen = strlen(target) ;
    size_t servicelen = strlen(service) + 1 ;

    char dst[targetlen + 1 + servicelen + info->ownerlen + 21 + 1] ;
    auto_strings(dst, target, "/", service, "/data/rules/uid/", info->ownerstr) ;

    auto_dir(dst, 0755) ;
    auto_empty_file(dst, "/allow", "") ;

    char sym[targetlen + 1 + servicelen + 22 + 1] ;
    auto_strings(sym, target, "/", service, "/data/rules/uid/self") ;

    log_trace("point symlink: ", sym, " to ", info->ownerstr) ;
    if (symlink(info->ownerstr, sym) < 0)
        log_dieusys(LOG_EXIT_SYS, "symlink: ", sym) ;

    if (lchown(sym, OWNER, GIDOWNER) < 0)
        log_dieusys(LOG_EXIT_SYS, "chown: ", sym) ;

    auto_strings(dst, target, "/", service, "/data/rules/gid/", info->ownerstr) ;
    auto_dir(dst, 0755) ;

    auto_empty_file(dst, "/allow", "") ;

    auto_strings(dst, target, "/", service, "/") ;
    auto_file(dst, SS_NOTIFICATION, notif, strlen(notif)) ;
}

static void create_service_oneshot(char const *scandir, ssexec_t *info)
{
    size_t scandirlen = strlen(scandir) ;
    size_t fdlen = scandirlen + 1 + SS_ONESHOTD_LEN ;

    create_service_skel(SS_ONESHOTD, scandir, "3\n", info) ;
    size_t runlen = strlen(SS_EXECLINE_SHEBANGPREFIX) + strlen(SS_LIBEXECPREFIX) + 174 ;
    char run[runlen + 1] ;
    auto_strings(run,"#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n", \
                    "fdmove -c 2 1\n", \
                    "fdmove 1 3\n", \
                    "s6-ipcserver-socketbinder -- s\n", \
                    "s6-ipcserverd -1 --\n", \
                    "s6-ipcserver-access -v0 -E -l0 -i data/rules --\n", \
                    "s6-sudod -t 30000 --\n", \
                    SS_LIBEXECPREFIX "66-oneshot --\n") ;


    char dst[fdlen + 5] ;
    auto_strings(dst, scandir, "/", SS_ONESHOTD, "/run") ;

    // -1 openwritenclose_unsafe do not accept closed string
    if (!openwritenclose_unsafe(dst, run, runlen))
        log_dieusys(LOG_EXIT_SYS, "write: ", dst) ;

    if (chmod(dst, 0755) < 0)
        log_dieusys(LOG_EXIT_SYS, "chmod: ", dst) ;

    auto_chown(dst) ;
}

static void create_service_fdholder(char const *scandir, ssexec_t *info)
{
    size_t scandirlen = strlen(scandir) ;
    size_t fdlen = scandirlen + 1 + SS_FDHOLDER_LEN ;

    create_service_skel(SS_FDHOLDER, scandir, "1\n", info) ;

    char dst[fdlen + info->ownerlen + 20 + 1] ;
    auto_strings(dst, scandir, "/", SS_FDHOLDER, "/data/rules/uid/", info->ownerstr, "/env") ;

    auto_dir(dst, 0755) ;

    auto_empty_file(dst, "/S6_FDHOLDER_GETDUMP", "\n") ;
    auto_empty_file(dst, "/S6_FDHOLDER_LIST", "\n") ;
    auto_empty_file(dst, "/S6_FDHOLDER_SETDUMP", "\n") ;

    auto_strings(dst + fdlen + info->ownerlen + 20, "/S6_FDHOLDER_STORE_REGEX" ) ;

    if(!openwritenclose_unsafe(dst, "^" SS_FDHOLDER_PIPENAME "\n", SS_FDHOLDER_PIPENAME_LEN + 2))
        log_dieusys(LOG_EXIT_SYS, "write: ", dst) ;

    auto_chown(dst) ;

    char sym[fdlen + info->ownerlen + 47 + 1] ;
    auto_strings(sym, scandir, "/", SS_FDHOLDER, "/data/rules/uid/",info->ownerstr,"/env/S6_FDHOLDER_RETRIEVE_REGEX") ;

    auto_strings(dst, "S6_FDHOLDER_STORE_REGEX") ;

    log_trace("point symlink: ", sym, " to ", dst) ;
    if (symlink(dst, sym) < 0)
        log_dieusys(LOG_EXIT_SYS, "symlink: ", dst) ;

    if (lchown(sym, OWNER, GIDOWNER) < 0)
        log_dieusys(LOG_EXIT_SYS, "chown: ", sym) ;

    auto_strings(sym, scandir, "/", SS_FDHOLDER, "/data/rules/gid/", info->ownerstr, "/env") ;
    auto_strings(dst, "../../uid/", info->ownerstr, "/env") ;

    log_trace("point symlink: ", sym, " to ", dst) ;
    if (symlink(dst, sym) < 0)
        log_dieusys(LOG_EXIT_SYS, "symlink: ", dst) ;

    if (lchown(sym, OWNER, GIDOWNER) < 0)
        log_dieusys(LOG_EXIT_SYS, "chown: ", sym) ;

    size_t runlen = strlen(SS_EXECLINE_SHEBANGPREFIX) + strlen(SS_LIBEXECPREFIX) + 277 + 1 ;

    char run[runlen] ;
    auto_strings(run, "#!" SS_EXECLINE_SHEBANGPREFIX "execlineb -P\n", \
                "pipeline -dw -- {\n",
                "   if -- {\n", \
                "       forstdin -x0 -- i\n", \
                "           exit 0\n", \
                "   }\n", \
                "   if -nt -- {\n", \
                "       redirfd -r 0 ./data/autofilled\n", \
                "       s6-ipcclient -l0 -- s\n", \
                "       ", SS_LIBEXECPREFIX "66-fdholder-filler -1 --\n", \
                "   }\n", \
                "   s6-svc -t .\n", \
                "}\n", \
                "s6-fdholder-daemon -1 -i data/rules -- s\n") ;

    auto_strings(dst, scandir, "/", SS_FDHOLDER, "/run") ;

    // -1 openwritenclose_unsafe do not accept closed string
    if (!openwritenclose_unsafe(dst, run, strlen(run) - 1))
        log_dieusys(LOG_EXIT_SYS, "write: ", dst) ;

    auto_chmod(dst, 0755) ;
    auto_chown(dst) ;

    auto_strings(dst, scandir, "/", SS_FDHOLDER, "/data/autofilled") ;

    // -1 openwritenclose_unsafe do not accept closed string
    if(!openwritenclose_unsafe(dst, "\n", 1))
        log_dieusys(LOG_EXIT_SYS, "write: ", dst) ;

    auto_chown(dst) ;
}

static void create_scandir(char const *live, char const *scandir, ssexec_t *info)
{
    log_flow() ;

    size_t scanlen = strlen(scandir) ;
    char tmp[scanlen + 11 + 1] ;

    /** run/66/scandir/<uid> */
    auto_strings(tmp,scandir) ;

    auto_check(tmp,0755,0,AUTO_CRTE_CHW) ;

    /** run/66/scandir/uid/.svscan */
    auto_strings(tmp + scanlen, SS_SVSCAN_LOG) ;

    auto_check(tmp,0755,0,AUTO_CRTE_CHW) ;

    char const *const file[] =
    {
        "/crash", "/finish", "/SIGINT",
        "/SIGQUIT", "/SIGTERM", "/SIGUSR1", "/SIGUSR2",
        "/SIGPWR", "/SIGWINCH"
     } ;

    log_trace("write control file... ") ;
    for (int i = 0 ; i < 9; i++)
        write_control(scandir,live,file[i],i) ;

    if (BOOT) {

        if (CATCH_LOG)
            write_bootlog(live, scandir) ;

        write_shutdownd(live, scandir) ;
    }

    create_service_fdholder(scandir, info) ;
    create_service_oneshot(scandir, info) ;
}

void sanitize_live(char const *live)
{
    log_flow() ;

    size_t livelen = strlen(live) ;
    char tmp[livelen + SS_BOOT_CONTAINER_DIR_LEN + 1 + strlen(OWNERSTR) + 1] ;

    /** run/66 */
    auto_check(live,0755,0,AUTO_CRTE_CHW) ;

    /** run/66/scandir */
    auto_strings(tmp,live,SS_SCANDIR) ;
    auto_check(tmp,0755,PERM1777,AUTO_CRTE_CHW_CHM) ;

    if (CONTAINER) {
        /** run/66/container */
        auto_strings(tmp + livelen,SS_BOOT_CONTAINER_DIR,"/",OWNERSTR) ;
        auto_check(tmp,0755,PERM1777,AUTO_CRTE_CHW_CHM) ;
        auto_file(tmp,SS_BOOT_CONTAINER_HALTCMD,"EXITCODE=0\nHALTCODE=p\n",22) ;
    }

    /** run/66/log */
    auto_strings(tmp + livelen, SS_LOG) ;
    auto_check(tmp,0755,PERM1777,AUTO_CRTE_CHW_CHM) ;

    /** /run/66/state*/
    auto_strings(tmp + livelen,SS_STATE + 1) ;
    auto_check(tmp,0755,PERM1777,AUTO_CRTE_CHW_CHM) ;
}

int ssexec_scandir_create(int argc, char const *const *argv, ssexec_t *info)
{
    log_flow() ;

    int r ;

    CONFIG_STR_LEN = compute_buf_size(SS_BINPREFIX, SS_EXTBINPREFIX, SS_EXTLIBEXECPREFIX, SS_LIBEXECPREFIX, SS_EXECLINE_SHEBANGPREFIX, 0) ;

    OWNER = info->owner ;
    OWNERSTR = info->ownerstr ;

    {
        subgetopt l = SUBGETOPT_ZERO ;

        for (;;)
        {
            int opt = subgetopt_r(argc, argv, OPTS_SCANDIR_CREATE, &l) ;
            if (opt == -1) break ;

            switch (opt) {

                case 'b' :

                    if (OWNER)
                        log_die(LOG_EXIT_USER, "-b options can be set only with root") ;

                    BOOT = 1 ;
                    break ;

                case 'B' :

                    if (OWNER)
                        log_die(LOG_EXIT_USER, "-B options can be set only with root") ;

                    CONTAINER = 1 ;
                    BOOT = 1 ;
                    break ;

                case 's' :

                    skel = l.arg ;
                    break ;

                case 'c' :

                    CATCH_LOG = 0 ;
                    break ;

                case 'L' :

                    log_user = l.arg ;
                    break ;

                default :

                    log_usage(info->usage, "\n", info->help) ;
            }
        }
        argc -= l.ind ; argv += l.ind ;
    }

    if (BOOT && OWNER && !CONTAINER)
        log_die(LOG_EXIT_USER, "-b options can be set only with root") ;

    if (!yourgid(&GIDOWNER,OWNER))
        log_dieusys(LOG_EXIT_SYS, "set gid of: ", OWNERSTR) ;

    GIDSTR[gid_fmt(GIDSTR,GIDOWNER)] = 0 ;

    if (BOOT && skel[0] != '/')
        log_die(LOG_EXIT_USER, "rc.shutdown: ", skel, " must be an absolute path") ;

    r = scan_mode(info->scandir.s, S_IFDIR) ;
    if (r < 0) log_die(LOG_EXIT_SYS, "scandir: ", info->scandir.s, " exist with unkown mode") ;
    if (!r) {

        log_trace("sanitize ", info->live.s, " ...") ;
        sanitize_live(info->live.s) ;
        log_info ("create scandir ", info->scandir.s, " ...") ;
        create_scandir(info->live.s, info->scandir.s, info) ;

    } else
        log_info("scandir: ", info->scandir.s, " already exist, keep it") ;

    return 0 ;
}