/*
 * write_common.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 <stdlib.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 <skalibs/unix-transactional.h>

#include <66/service.h>
#include <66/write.h>
#include <66/constants.h>
#include <66/environ.h>
#include <66/enum.h>

int write_common(resolve_service_t *res, char const *dst, uint8_t force)
{
    log_flow() ;

    /** down file */
    if (res->execute.down) {
        log_trace("create file: ", dst, "/down") ;
        if (!file_create_empty(dst, "down", 0644))
            log_warnusys_return(LOG_EXIT_ZERO, "create down file") ;
    }

    /** notification-fd */
    if (res->notify)
        if (!write_uint(dst, SS_NOTIFICATION, res->notify))
            log_warnusys_return(LOG_EXIT_ZERO, "write uint file", SS_NOTIFICATION) ;

    /** timeout family
     * Only write timeout file for classic service.
     * S6-supervise need it otherwise it's read directly
     * from the resolve file at start process. */
    if (res->type == TYPE_CLASSIC && res->execute.timeout.start)
        if (!write_uint(dst, "timeout-kill", res->execute.timeout.start))
            log_warnusys_return(LOG_EXIT_ZERO, "write uint file timeout-kill") ;

    if (res->type == TYPE_CLASSIC && res->execute.timeout.stop)
        if (!write_uint(dst, "timeout-finish", res->execute.timeout.stop))
            log_warnusys_return(LOG_EXIT_ZERO, "write uint file timeout-finish") ;

    /** max-death-tally */
    if (res->maxdeath)
        if (!write_uint(dst, SS_MAXDEATHTALLY, res->maxdeath))
            log_warnusys_return(LOG_EXIT_ZERO, "write uint file", SS_MAXDEATHTALLY) ;

    /** down-signal */
    if (res->execute.downsignal)
        if (!write_uint(dst, "down-signal", res->execute.downsignal))
            log_warnusys_return(LOG_EXIT_ZERO, "write uint file down-signal") ;

    /** environment for module is already written by the regex_configure() 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_warnusys_return(LOG_EXIT_ZERO, "prepare environment for: ", res->sa.s + res->name) ;

        if (!write_environ(name, contents.s, dst.s))
            log_warnusys_return(LOG_EXIT_ZERO, "write environment for: ", res->sa.s + res->name) ;

        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), dstlen = strlen(dst) ;
        char basedir[srclen + 1] ;

        if (!ob_dirname(basedir, src))
            log_warnusys_return(LOG_EXIT_ZERO, "get dirname of: ", src) ;

        if (!sastr_clean_string(&sa, res->sa.s + res->hiercopy))
            log_warnusys_return(LOG_EXIT_ZERO, "clean string") ;

        FOREACH_SASTR(&sa, pos) {

            char *what = sa.s + pos ;
            int fd ;
            size_t wlen = strlen(what) ;
            char tmp[SS_MAX_PATH_LEN + 1] ;
            char dest[dstlen + 1 + wlen + 1] ;
            char basename[SS_MAX_PATH_LEN + 1] ;

            if (what[0] == '/' ) {

                auto_strings(tmp, what) ;

                if (!ob_basename(basename, what))
                    log_warnusys_return(LOG_EXIT_ZERO, "get basename of: ", what) ;

                what = basename ;

            } else if (what[0] == '.' && ((what[1] == '/') || (what[1] == '.' && what[2] == '/'))) {
               /** distinction between .file and ./ ../ directory */
                char b[strlen(src) + 1]  ;

                if (!ob_dirname(b, src))
                    log_warnusys_return(LOG_EXIT_ZERO, "get dirname of: ", src) ;

                fd = open_read(".") ;
                if (fd < 0)
                    log_warnusys_return(LOG_EXIT_ZERO, "open current directory") ;

                if (chdir(b) < 0) {
                    fd_close(fd) ;
                    log_warnusys_return(LOG_EXIT_ZERO, "change directory") ;
                }

                char *p = realpath(what, tmp) ;
                if (!p) {
                    fd_close(fd) ;
                    log_warnusys_return(LOG_EXIT_ZERO, "get absolute path of: ", what) ;
                }

                if (fd_chdir(fd) < 0) {
                    fd_close(fd) ;
                    log_warnusys_return(LOG_EXIT_ZERO, "change directory") ;
                }

                fd_close(fd) ;

                if (!ob_basename(basename, what))
                    log_warnusys_return(LOG_EXIT_ZERO, "get basename of: ", what) ;

                what = basename ;

            } else {

                auto_strings(tmp, basedir, what) ;
            }

            r = scan_mode(tmp, S_IFDIR) ;
            if (r <= 0) {

                r = scan_mode(tmp, S_IFREG) ;
                if (!r)
                    log_warnusys_return(LOG_EXIT_ZERO, "find: ", tmp) ;
                else if (r < 0) {
                    errno = ENOTSUP ;
                    log_warnusys_return(LOG_EXIT_ZERO, "invalid format of: ", tmp) ;
                }
            }

            auto_strings(dest, dst, "/", what) ;

            log_trace("copy: ", tmp, " to: ", dest) ;
            if (!hiercopy(tmp, dest))
                log_warnusys_return(LOG_EXIT_ZERO, "copy: ", tmp, " to: ", dest) ;
        }

        stralloc_free(&sa) ;
    }

    /** run file */
    if (!write_execute_scripts("run", res->sa.s + res->execute.run.run, dst, 0)) {
        parse_cleanup(res, dst, force) ;
        log_dieu(LOG_EXIT_SYS, "write execute script of: ", res->sa.s + res->name) ;
    }

    /** finish file */
    if (res->execute.finish.run) {
        if (!write_execute_scripts("finish", res->sa.s + res->execute.finish.run, dst, 0)) {
            parse_cleanup(res, dst, force) ;
            log_dieu(LOG_EXIT_SYS, "write execute script of: ", res->sa.s + res->name) ;
        }
    }
    return 1 ;
}
