Skip to content
Snippets Groups Projects
parse_service.c 11.52 KiB
/*
 * parse_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 <66/parser.h>

#include <stdint.h>
#include <string.h>

#include <oblibs/log.h>
#include <oblibs/sastr.h>
#include <oblibs/string.h>
#include <oblibs/types.h>

#include <skalibs/stralloc.h>
#include <skalibs/genalloc.h>

#include <66/utils.h>
#include <66/constants.h>
#include <66/ssexec.h>
#include <66/service.h>
#include <66/tree.h>

static void parse_service_instance(stralloc *frontend, char const *svsrc, char const *sv, int insta)
{
    log_flow() ;

    stralloc sa = STRALLOC_ZERO ;

    if (!instance_splitname(&sa, sv, insta, SS_INSTANCE_TEMPLATE))
        log_die(LOG_EXIT_SYS, "split instance service: ", sv) ;

    log_trace("read frontend service of: ", svsrc, sa.s) ;

    if (read_svfile(frontend, sa.s, svsrc) <= 0)
        log_dieusys(LOG_EXIT_SYS, "read frontend service of: ", svsrc, sa.s) ;

    stralloc_free(&sa) ;

    if (!instance_create(frontend, sv, SS_INSTANCE_REGEX, insta))
        log_die(LOG_EXIT_SYS, "create instance service: ", sv) ;

    /** ensure that we have an empty line at the end of the string*/
    if (!auto_stra(frontend, "\n"))
        log_die_nomem("stralloc") ;

}

static int parse_add_service(stralloc *parsed_list, sv_alltype *alltype, char const *service, uint8_t conf)
{
    log_flow() ;

    log_trace("add service: ", service) ;

    // keep overwrite_conf
    alltype->overwrite_conf = conf ;

    // keep source of the frontend file
    alltype->src = keep.len ;
    if (!sastr_add_string(&keep, service))
        return 0 ;

    // keep service on current list
    if (!sastr_add_string(parsed_list, service))
        return 0 ;

    if (!genalloc_append(sv_alltype, &gasv, alltype))
        return 0 ;

    return 1 ;
}

static void set_info(ssexec_t *info)
{
    log_flow() ;

    info->tree.len = 0 ;
    int r = ssexec_set_treeinfo(info) ;
    if (r == -4) log_die(LOG_EXIT_USER,"You're not allowed to use the tree: ",info->tree.s) ;
    if (r == -3) log_dieu(LOG_EXIT_USER,"find the current tree. You must use the -t options") ;
    if (r == -2) log_dieu(LOG_EXIT_USER,"set the tree name") ;
    if (r == -1) log_dieu(LOG_EXIT_USER,"parse seed file") ;
    if (!r) log_dieusys(LOG_EXIT_SYS,"find tree: ", info->treename.s) ;

}
#include <stdio.h>
/* @sv -> name of the service to parse with
 * the path of the frontend file source
 * @Return 0 on fail
 * @Return 1 on success
 * @Return 2 -> already parsed */
int parse_service(char const *sv, stralloc *parsed_list, ssexec_t *info, uint8_t force, uint8_t conf)
{

    log_flow() ;

    if (sastr_cmp(parsed_list, sv) >= 0) {

        log_warn("ignoring: ", sv, " service -- already parsed") ;
        return 2 ;
    }

    log_trace("parse service: ", sv) ;

    int insta, r ;
    size_t svlen = strlen(sv) ;
    char svname[svlen + 1], svsrc[svlen + 1] ;

    sv_alltype alltype = SV_ALLTYPE_ZERO ;
    stralloc frontend = STRALLOC_ZERO ;
    stralloc satree = STRALLOC_ZERO ;

    if (!ob_basename(svname, sv))
        log_dieu(LOG_EXIT_SYS, "get basename of: ", sv) ;

    if (!ob_dirname(svsrc, sv))
        log_dieu(LOG_EXIT_SYS, "get dirname of: ", sv) ;

    insta = instance_check(svname) ;
    if (!insta) {

        log_die(LOG_EXIT_SYS, "invalid instance name: ", svname) ;

    } else if (insta > 0) {

        parse_service_instance(&frontend, svsrc, svname, insta) ;

    } else {
        log_trace("read frontend service of: ", sv) ;

        if (read_svfile(&frontend, svname, svsrc) <= 0)
            log_dieusys(LOG_EXIT_SYS, "read frontend service of: ", sv) ;
    }

    r = service_isenabledat(&satree, svname) ;
    if (r < -1)
        log_dieu(LOG_EXIT_SYS, "check already enabled services") ;

    if (r > 0) {

        if (force) {

            /* -t option was used */
            if (!info->skip_opt_tree) {

                if (strcmp(info->treename.s, satree.s))
                    log_die(LOG_EXIT_SYS,"you can not force to enable again a service on different tree -- current: ", satree.s, " asked: ", info->treename.s, ". Try first to disable it") ;

            } else {

                if (!auto_stra(&info->treename, satree.s))
                    log_die_nomem("stralloc") ;
            }

            goto set ;

        } else {

            log_info("ignoring service: ", sv, " -- already enabled at tree: ", satree.s) ;

            /** we don't care about the use of the -t option. The define of the
             * info->tree and info->treename is just made to avoid segmentation fault
             * at the rest of the process. The service is not parsed or enable again anyway. */

            info->treename.len = 0 ;
            if (!auto_stra(&info->treename, satree.s))
                log_die_nomem("stralloc") ;

            set_info(info) ;

            sv_alltype_free(&alltype) ;
            stralloc_free(&frontend) ;
            stralloc_free(&satree) ;
            return 2 ;
        }
    }

    if (info->skip_opt_tree) {

        /** first try to find the @intree key at the frontend file*/
        if (!get_svintree(&alltype,frontend.s))
            log_die(LOG_EXIT_USER, "invalid value for key: ",get_key_by_enum(ENUM_KEY_SECTION_MAIN,KEY_MAIN_INTREE)," in service file: ", sv) ;

        if (alltype.cname.intree >= 0) {

            info->treename.len = 0 ;
            if (!auto_stra(&info->treename, keep.s + alltype.cname.intree))
                log_die_nomem("stralloc") ;

        }
    }

    set:

    set_info(info) ;

    if (!get_svtype(&alltype, frontend.s))
        log_die(LOG_EXIT_USER, "invalid value for key: ", get_key_by_enum(ENUM_KEY_SECTION_MAIN, KEY_MAIN_TYPE), " at frontend service: ", sv) ;

    /** contents of directory should be listed by service_frontend_path
     * except for module type */
    if (scan_mode(sv,S_IFDIR) == 1 && alltype.cname.itype != TYPE_MODULE)
        goto freed ;

    alltype.cname.name = keep.len ;
    if (!sastr_add_string(&keep, svname))
        log_die_nomem("stralloc") ;

    if (!parser(&alltype, &frontend, svname, alltype.cname.itype))
        log_dieu(LOG_EXIT_SYS, "parse service: ", sv) ;

    if (!parse_add_service(parsed_list, &alltype, sv, conf))
        log_dieu(LOG_EXIT_SYS, "add service: ", sv) ;

    freed:
    stralloc_free(&frontend) ;
    stralloc_free(&satree) ;

    return 1 ;
}

int parse_service_deps(sv_alltype *alltype, ssexec_t *info, stralloc *parsed_list, uint8_t force, char const *directory_forced)
{

    log_flow() ;

    int r, e = 0 ;
    stralloc sa = STRALLOC_ZERO ;

    if (alltype->cname.nga) {

        size_t id = alltype->cname.idga, nid = alltype->cname.nga ;
        for (; nid ; id += strlen(deps.s + id) + 1, nid--) {

            sa.len = 0 ;

            if (alltype->cname.itype != TYPE_BUNDLE) {

                log_trace("service: ", keep.s + alltype->cname.name, " depends on: ", deps.s + id) ;

            } else log_trace("bundle: ", keep.s + alltype->cname.name, " contents: ", deps.s + id," as service") ;

            r = service_frontend_path(&sa, deps.s + id, info->owner, directory_forced) ;
            if (r < 1) goto err ;//don't warn here, the ss_revolve_src_path do it

            if (!parse_service(sa.s, parsed_list, info, force, alltype->overwrite_conf))
                goto err ;
        }

    } else log_trace(keep.s + alltype->cname.name,": haven't dependencies") ;

    e = 1 ;

    err:
        stralloc_free(&sa) ;
        return e ;
}

int parse_service_optsdeps(stralloc *rebuild, sv_alltype *alltype, ssexec_t *info, stralloc *parsed_list, uint8_t force, uint8_t field, char const *directory_forced)
{
    log_flow() ;

    int r, e = 0 ;
    stralloc sa = STRALLOC_ZERO ;

    size_t id, nid ;
    uint8_t ext = field == KEY_MAIN_EXTDEPS ? 1 : 0 ;
    int idref = alltype->cname.idopts ;
    unsigned int nref = alltype->cname.nopts ;
    if (ext) {
        idref = alltype->cname.idext ;
        nref = alltype->cname.next ;
    }

    if (nref) {

        id = (size_t)idref, nid = (size_t)nref ;
        for (; nid ; id += strlen(deps.s + id) + 1, nid--) {

            sa.len = 0 ;
            // 0 -> not found, 1 -> found, -1 -> system error
            r = service_isenabled(deps.s + id) ;
            if (r == -1)
                log_dieu(LOG_EXIT_SYS, "check already enabled services") ;

            if (r > 0) {
                if (!ext)
                    break ;
                else
                    continue ;
            }

            r = service_frontend_path(&sa, deps.s + id, info->owner, directory_forced) ;
            if (r == -1)
                goto err ;

            if (!r) {

                log_trace("unable to find", ext ? " external " : " optional ", "dependency: ", deps.s + id, " for service: ", keep.s + alltype->cname.name) ;

                // external deps must exist
                if (ext)
                    goto err ;

                continue ;
            }

            if (!parse_service(sa.s, parsed_list, info, force, alltype->overwrite_conf))
                goto err ;

            if (!sastr_add_string(rebuild, deps.s + id))
                log_die_nomem("stralloc") ;

            // we only keep the first optsdepends found
            if (!ext) break ;
        }

    } else log_trace(keep.s + alltype->cname.name,": haven't", ext ? " external " : " optional ", "dependencies") ;

    e = 1 ;

    err:
        stralloc_free(&sa) ;
        return e ;
}

int parse_service_alldeps(sv_alltype *alltype, ssexec_t *info, stralloc *parsed_list, uint8_t force, char const *directory_forced)
{
    log_flow() ;

    int e = 0 ;
    size_t id, nid, pos ;
    char *name = keep.s + alltype->cname.name ;
    stralloc sa = STRALLOC_ZERO ;
    stralloc rebuild = STRALLOC_ZERO ;

    if (!parse_service_deps(alltype, info, parsed_list, force, directory_forced)) {
        log_warnu("parse dependencies of: ", name) ;
        goto err ;
    }

    if (!parse_service_optsdeps(&rebuild, alltype, info, parsed_list, force, KEY_MAIN_EXTDEPS,  directory_forced)) {
        log_warnu("parse external dependencies of: ", name) ;
        goto err ;
    }

    if (!parse_service_optsdeps(&rebuild, alltype, info, parsed_list, force, KEY_MAIN_OPTSDEPS,  directory_forced)) {
        log_warnu("parse optional dependencies of: ", name) ;
        goto err ;
    }

    // rebuild the dependencies list of the service to add the new dependencies.
    if (rebuild.len) {

        pos = 0 ;
        sa.len = 0 ;
        id = alltype->cname.idga ;
        nid = alltype->cname.nga ;

        {
            for (; nid ; id += strlen(deps.s + id) + 1, nid--) {
                if (!sastr_add_string(&sa, deps.s + id)) {
                    log_warn("stralloc") ;
                    goto err ;
                }
            }


            FOREACH_SASTR(&rebuild, pos) {
                if (!sastr_add_string(&sa, rebuild.s + pos)) {
                    log_warn("stralloc") ;
                    goto err ;
                }
            }

        }

        alltype->cname.idga = deps.len ;
        alltype->cname.nga = 0 ;

        pos = 0 ;
        FOREACH_SASTR(&sa, pos) {

            log_info("rebuil list", sa.s + pos) ;

            if (!sastr_add_string(&deps,sa.s + pos)) {
                log_warn("stralloc") ;
                goto err ;
            }

            alltype->cname.nga++ ;
        }
    }

    e = 1 ;

    err:
        stralloc_free(&rebuild) ;
        stralloc_free(&sa) ;

    return e ;
}