/* * parser.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 <string.h> #include <oblibs/string.h> #include <oblibs/types.h> #include <oblibs/log.h> #include <oblibs/sastr.h> #include <oblibs/files.h> #include <skalibs/stralloc.h> #include <skalibs/djbunix.h>//environ #include <66/resolve.h> #include <66/utils.h> #include <66/constants.h> #include <66/environ.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_before(ssexec_t *info,stralloc *parsed_list,stralloc *tree_list, char const *sv,unsigned int *nbsv, stralloc *sasv,uint8_t force,uint8_t conf,uint8_t disable_module,char const *directory_forced) { log_flow() ; log_trace("start parse process of service: ",sv) ; if (sastr_cmp(parsed_list,sv) >= 0) { log_warn("ignoring: ",sv," service: already parsed") ; sasv->len = 0 ; return 2 ; } int insta ; uint8_t exist = 0 ; size_t svlen = strlen(sv), svnamelen ; char svname[svlen + 1], svsrc[svlen + 1] ; if (!ob_basename(svname,sv)) log_warnu_return(LOG_EXIT_ZERO,"get basename of: ",sv) ; if (!ob_dirname(svsrc,sv)) log_warnu_return(LOG_EXIT_ZERO,"get dirname of: ",sv) ; svnamelen = strlen(svname) ; char tree[info->tree.len + SS_SVDIRS_LEN + 1] ; auto_strings(tree,info->tree.s,SS_SVDIRS) ; int r = parse_service_check_enabled(tree,svname,force,&exist) ; if (!r) { sasv->len = 0 ; return 2 ; } sv_alltype sv_before = SV_ALLTYPE_ZERO ; sasv->len = 0 ; insta = instance_check(svname) ; if (!insta) { log_warn_return(LOG_EXIT_ZERO, "invalid instance name: ",svname) ; } else if (insta > 0) { stralloc tmp = STRALLOC_ZERO ; instance_splitname(&tmp,svname,insta,SS_INSTANCE_TEMPLATE) ; log_trace("read service file of: ",svsrc,tmp.s) ; if (read_svfile(sasv,tmp.s,svsrc) <= 0) return 0 ; stralloc_free(&tmp) ; } else { log_trace("read service file of: ",svsrc,svname) ; if (read_svfile(sasv,svname,svsrc) <= 0) return 0 ; } if (!get_svtype(&sv_before,sasv->s)) log_warn_return (LOG_EXIT_ZERO,"invalid value for key: ",get_key_by_enum(ENUM_KEY_SECTION_MAIN,KEY_MAIN_TYPE)," in service file: ",svsrc,svname) ; sv_before.cname.name = keep.len ; if (!stralloc_catb(&keep,svname,svnamelen + 1)) return 0 ; /** contents of directory should be listed by ss_resolve_src_path * execpt for module type */ if (scan_mode(sv,S_IFDIR) == 1 && sv_before.cname.itype != TYPE_MODULE) return 1 ; if (insta > 0) { if (!instance_create(sasv,svname,SS_INSTANCE_REGEX,insta)) log_warn_return(LOG_EXIT_ZERO,"create instance service: ",svname) ; /** ensure that we have an empty line at the end of the string*/ if (!stralloc_cats(sasv,"\n")) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!stralloc_0(sasv)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } if (!parser(&sv_before,sasv,svname,sv_before.cname.itype)) return 0 ; if ((sv_before.cname.itype > TYPE_CLASSIC && force > 1) || !exist) if (!parse_service_all_deps(info,parsed_list,tree_list,&sv_before,sv,nbsv,sasv,force,conf,directory_forced)) return 0 ; if (sv_before.cname.itype == TYPE_MODULE) { r = parse_module(&sv_before,info,parsed_list,tree_list,svname,svsrc,nbsv,sasv,force,conf) ; if (!r) return 0 ; else if (r == 2) { sasv->len = 0 ; goto add ; } if (force > 1 && exist && disable_module) { char const *newargv[4] ; unsigned int m = 0 ; newargv[m++] = "fake_name" ; newargv[m++] = svname ; newargv[m++] = 0 ; if (ssexec_disable(m,newargv,(const char *const *)environ,info)) log_warnu_return(LOG_EXIT_ZERO,"disable module: ",svname) ; } } add: if (!parse_add_service(parsed_list,&sv_before,sv,nbsv,info->owner,conf)) return 0 ; return 1 ; } int parse_service_all_deps(ssexec_t *info,stralloc *parsed_list, stralloc *tree_list, sv_alltype *sv_before,char const *sv, unsigned int *nbsv,stralloc *sasv,uint8_t force, uint8_t conf,char const *directory_forced) { log_flow() ; stralloc rebuild = STRALLOC_ZERO ; if (!parse_service_deps(info,parsed_list,tree_list,sv_before,sv,nbsv,sasv,force,conf,directory_forced)) return 0 ; if (!parse_service_opts_deps(&rebuild,info,parsed_list,tree_list,sv_before,sv,nbsv,sasv,force,conf,KEY_MAIN_EXTDEPS,0)) return 0 ; if (!parse_service_opts_deps(&rebuild,info,parsed_list,tree_list,sv_before,sv,nbsv,sasv,force,conf,KEY_MAIN_OPTSDEPS,0)) return 0 ; if (rebuild.len) { size_t pos = 0 ; stralloc old = STRALLOC_ZERO ; //rebuild the dependencies list of the service int id = sv_before->cname.idga ; unsigned int nid = sv_before->cname.nga ; for (;nid; id += strlen(deps.s + id) + 1, nid--) { if (!stralloc_catb(&old,deps.s + id,strlen(deps.s + id) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } for (pos = 0 ; pos < rebuild.len ; pos += strlen(rebuild.s + pos) + 1) { if (!stralloc_catb(&old,rebuild.s + pos,strlen(rebuild.s + pos) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } sv_before->cname.idga = deps.len ; sv_before->cname.nga = 0 ; for (pos = 0 ; pos < old.len ; pos += strlen(old.s + pos) + 1) { if (!stralloc_catb(&deps,old.s + pos,strlen(old.s + pos) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; sv_before->cname.nga++ ; } stralloc_free(&old) ; } stralloc_free(&rebuild) ; return 1 ; } int parse_service_deps(ssexec_t *info,stralloc *parsed_list,stralloc *tree_list, sv_alltype *sv_before, char const *sv,unsigned int *nbsv,stralloc *sasv,uint8_t force,uint8_t conf,char const *directory_forced) { log_flow() ; int r ; char *dname = 0 ; stralloc newsv = STRALLOC_ZERO ; if (sv_before->cname.nga) { size_t id = sv_before->cname.idga, nid = sv_before->cname.nga ; for (;nid; id += strlen(deps.s + id) + 1, nid--) { newsv.len = 0 ; if (sv_before->cname.itype != TYPE_BUNDLE) { log_trace("service: ",sv, " depends on: ",deps.s+id) ; }else log_trace("bundle: ",sv, " contents: ",deps.s+id," as service") ; dname = deps.s + id ; r = ss_resolve_src_path(&newsv,dname,info->owner,directory_forced) ; if (r < 1) goto err ;//don't warn here, the ss_revolve_src_path already warn user if (!parse_service_before(info,parsed_list,tree_list,newsv.s,nbsv,sasv,force,conf,0,directory_forced)) goto err ; } } else log_trace(sv,": haven't dependencies") ; stralloc_free(&newsv) ; return 1 ; err: stralloc_free(&newsv) ; return 0 ; } int parse_service_opts_deps(stralloc *rebuild,ssexec_t *info,stralloc *parsed_list,stralloc *tree_list,sv_alltype *sv_before,char const *sv,unsigned int *nbsv,stralloc *sasv,uint8_t force,uint8_t conf,uint8_t mandatory,char const *directory_forced) { log_flow() ; int r ; stralloc newsv = STRALLOC_ZERO ; size_t pos = 0 , baselen = strlen(info->base.s) + SS_SYSTEM_LEN ; uint8_t found = 0, ext = mandatory == KEY_MAIN_EXTDEPS ? 1 : 0 ; char *optname = 0 ; char btmp[baselen + 1] ; auto_strings(btmp,info->base.s,SS_SYSTEM) ; int idref = sv_before->cname.idopts ; unsigned int nref = sv_before->cname.nopts ; if (ext) { idref = sv_before->cname.idext ; nref = sv_before->cname.next ; } // only pass here for the first time if (!tree_list->len) { if (!sastr_dir_get(tree_list, btmp,SS_BACKUP + 1, S_IFDIR)) log_warnusys_return(LOG_EXIT_ZERO,"get list of tree at: ",btmp) ; } if (nref) { // may have no tree yet if (tree_list->len) { size_t id = idref, nid = nref ; for (;nid; id += strlen(deps.s + id) + 1, nid--) { newsv.len = 0 ; optname = deps.s + id ; for(pos = 0 ; pos < tree_list->len ; pos += strlen(tree_list->s + pos) +1 ) { found = 0 ; char *tree = tree_list->s + pos ; if (obstr_equal(tree,info->treename.s)) continue ; size_t treelen = strlen(tree) ; char tmp[baselen + 1 + treelen + SS_SVDIRS_LEN + 1] ; auto_strings(tmp,btmp,"/",tree,SS_SVDIRS) ; parse_service_check_enabled(tmp,optname,force,&found) ; if (found) { log_trace(ext ? "external" : "optional"," service dependency: ",optname," is already enabled at tree: ",btmp,"/",tree) ; break ; } } if (!found) { // -1 mean system error. If the service doesn't exist it return 0 r = ss_resolve_src_path(&newsv,optname,info->owner,directory_forced) ; if (r == -1) { goto err ; //don't warn here, the ss_revolve_src_path already warn user } else if (!r) { if (ext) goto err ; } // be paranoid with the else if else if (r == 1) { if (!parse_service_before(info,parsed_list,tree_list,newsv.s,nbsv,sasv,force,conf,0,directory_forced)) goto err ; // add the new deps if (!stralloc_catb(rebuild,optname,strlen(optname) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; // we only keep the first found on optsdepends if (!ext) break ; } } } } } stralloc_free(&newsv) ; return 1 ; err: stralloc_free(&newsv) ; return 0 ; } /** General helper */ int parse_service_check_enabled(char const *tree,char const *svname,uint8_t force,uint8_t *exist) { log_flow() ; /** char const tree -> tree.s + SS_SVDIRS */ size_t namelen = strlen(svname), newlen = strlen(tree) ; char tmp[newlen + SS_DB_LEN + SS_SRC_LEN + 1 + namelen + 1] ; auto_strings(tmp,tree,SS_DB,SS_SRC,"/",svname) ; /** search in db first, the most used */ if (scan_mode(tmp,S_IFDIR) > 0) { (*exist) = 1 ; if (!force) goto found ; } /** svc */ auto_string_from(tmp,newlen,SS_SVC,"/",svname) ; if (scan_mode(tmp,S_IFDIR) > 0) { (*exist) = 1 ; if (!force) goto found ; } return 1 ; found: log_info("ignoring: ",svname," service: already enabled") ; return 0 ; } int parse_add_service(stralloc *parsed_list,sv_alltype *sv_before,char const *service,unsigned int *nbsv,uid_t owner,uint8_t conf) { log_flow() ; log_trace("add service: ",service) ; stralloc saconf = STRALLOC_ZERO ; size_t svlen = strlen(service) ; // keep overwrite_conf sv_before->overwrite_conf = conf ; // keep source of the frontend file sv_before->src = keep.len ; if (!stralloc_catb(&keep,service,svlen + 1)) goto err ; // keep service on current list if (!stralloc_catb(parsed_list,service,svlen + 1)) goto err ; if (!genalloc_append(sv_alltype,&gasv,sv_before)) goto err ; (*nbsv)++ ; stralloc_free(&saconf) ; return 1 ; err: stralloc_free(&saconf) ; return 0 ; }