/* * parser.c * * Copyright (c) 2018-2020 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 <stdio.h> //rename #include <unistd.h> //chdir #include <oblibs/string.h> #include <oblibs/types.h> #include <oblibs/log.h> #include <oblibs/sastr.h> #include <oblibs/environ.h> #include <oblibs/files.h> #include <oblibs/mill.h> #include <oblibs/directory.h> #include <skalibs/stralloc.h> #include <skalibs/djbunix.h> #include <skalibs/env.h> #include <skalibs/bytestr.h>//byte_count #include <skalibs/genalloc.h> #include <66/resolve.h> #include <66/utils.h> #include <66/constants.h> #include <66/environ.h> #define SS_MODULE_CONFIG_DIR "/configure" #define SS_MODULE_CONFIG_DIR_LEN (sizeof SS_MODULE_CONFIG_DIR - 1) #define SS_MODULE_CONFIG_SCRIPT "configure" #define SS_MODULE_CONFIG_SCRIPT_LEN (sizeof SS_MODULE_CONFIG_SCRIPT - 1) #define SS_MODULE_SERVICE "/service" #define SS_MODULE_SERVICE_LEN (sizeof SS_MODULE_SERVICE - 1) #define SS_MODULE_SERVICE_INSTANCE "/service@" #define SS_MODULE_SERVICE_INSTANCE_LEN (sizeof SS_MODULE_SERVICE_INSTANCE - 1) static int check_dir(char const *src,char const *dir) { int r ; size_t srclen = strlen(src) ; size_t dirlen = strlen(dir) ; char tsrc[srclen + dirlen + 1] ; auto_strings(tsrc,src,dir) ; r = scan_mode(tsrc,S_IFDIR) ; if (r < 0) { errno = EEXIST ; log_warnusys_return(LOG_EXIT_ZERO,"conflicting format of: ",tsrc) ; } if (!r) { if (!dir_create_parent(tsrc,0755)) log_warnusys_return(LOG_EXIT_ZERO,"create directory: ",tsrc) ; } return 1 ; } static int get_list(stralloc *list, stralloc *sdir,size_t len, char const *svname, mode_t mode) { sdir->len = len ; if (!auto_stra(sdir,SS_MODULE_SERVICE)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!sastr_dir_get_recursive(list,sdir->s,"",mode)) log_warnusys_return(LOG_EXIT_ZERO,"get file(s) of module: ",svname) ; sdir->len = len ; if (!auto_stra(sdir,SS_MODULE_SERVICE_INSTANCE)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!sastr_dir_get_recursive(list,sdir->s,"",mode)) log_warnusys_return(LOG_EXIT_ZERO,"get file(s) of module: ",svname) ; return 1 ; } /** return 1 on success * return 0 on failure * return 2 on already enabled * @svname do not contents the path of the frontend file*/ int parse_module(sv_alltype *sv_before,ssexec_t *info,stralloc *parsed_list,stralloc *tree_list, char const *svname,unsigned int *nbsv, stralloc *sasv,uint8_t force,uint8_t conf) { log_trace("start parse process of module: ",svname) ; int r, err = 1, insta = -1, svtype = -1, from_ext_insta = 0 ; size_t pos = 0, id, nid, newlen, ndeps ; stralloc sdir = STRALLOC_ZERO ; // service dir stralloc list = STRALLOC_ZERO ; stralloc tmp = STRALLOC_ZERO ; stralloc moduleinsta = STRALLOC_ZERO ; stralloc addonsv = STRALLOC_ZERO ; /** should be always right, * be paranoid and check it */ insta = instance_check(svname) ; if (insta <= 0) log_warn_return(LOG_EXIT_ZERO,"invalid module instance name: ",svname); if (!ss_resolve_module_path(&sdir,&tmp,svname,info->owner)) return 0 ; /** check mandatory directories: * module/module_name, module/module_name/{configure,service,service@} */ if (!check_dir(tmp.s,"")) return 0 ; if (!check_dir(tmp.s,SS_MODULE_CONFIG_DIR)) return 0 ; if (!check_dir(tmp.s,SS_MODULE_SERVICE)) return 0 ; if (!check_dir(tmp.s,SS_MODULE_SERVICE_INSTANCE)) return 0 ; newlen = sdir.len ; char permanent_sdir[sdir.len + 1] ; auto_strings(permanent_sdir,sdir.s) ; r = scan_mode(sdir.s,S_IFDIR) ; if (r < 0) { errno = EEXIST ; log_warnusys_return(LOG_EXIT_ZERO,"conflicting format of: ",sdir.s) ; } else if (!r) { if (!hiercopy(tmp.s,sdir.s)) log_warnusys_return(LOG_EXIT_ZERO,"copy: ",tmp.s," to: ",sdir.s) ; } else { /** Must reconfigure all services of the module */ if (force < 2) { log_warn("skip configuration of the module: ",svname," -- already configured") ; err = 2 ; goto make_deps ; } if (rm_rf(sdir.s) < 0) log_warnusys_return (LOG_EXIT_ZERO,"remove: ",sdir.s) ; if (!hiercopy(tmp.s,sdir.s)) log_warnusys_return(LOG_EXIT_ZERO,"copy: ",tmp.s," to: ",sdir.s) ; } /** regex file content */ list.len = 0 ; if (!get_list(&list,&sdir,newlen,svname,S_IFREG)) return 0 ; if (!regex_replace(&list,sv_before,svname)) return 0 ; /* regex directories name */ if (!get_list(&list,&sdir,newlen,svname,S_IFDIR)) return 0 ; if (!regex_rename(&list,sv_before->type.module.iddir,sv_before->type.module.ndir,sdir.s)) return 0 ; /* regex files name */ list.len = 0 ; if (!get_list(&list,&sdir,newlen,svname,S_IFREG)) return 0 ; if (!regex_rename(&list,sv_before->type.module.idfiles,sv_before->type.module.nfiles,sdir.s)) return 0 ; /* launch configure script */ if (!regex_configure(sv_before,info,permanent_sdir,svname,conf)) return 0 ; make_deps: /** get all services */ tmp.len = 0 ; list.len = 0 ; if (!auto_stra(&tmp,permanent_sdir,SS_MODULE_SERVICE)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!sastr_dir_get_recursive(&list,tmp.s,"",S_IFREG)) log_warnusys_return(LOG_EXIT_ZERO,"get file(s) of module: ",svname) ; /** add addon service */ if (sv_before->type.module.naddservices > 0) { id = sv_before->type.module.idaddservices, nid = sv_before->type.module.naddservices ; for (;nid; id += strlen(keep.s + id) + 1, nid--) { char *name = keep.s + id ; if (ss_resolve_src_path(&list,name,info->owner) < 1) log_warnu_return(LOG_EXIT_ZERO,"resolve source path of: ",name) ; } } tmp.len = 0 ; sdir.len = 0 ; /** remake the deps field */ id = sv_before->cname.idga, nid = sv_before->cname.nga ; for (;nid; id += strlen(deps.s + id) + 1, nid--) { /** incoporate the @deps inside the list to parse, * this avoid to make it twice at parsed_enabled() */ if (ss_resolve_src_path(&list,deps.s + id,info->owner) < 1) log_warnu_return(LOG_EXIT_ZERO,"resolve source path of: ",deps.s + id) ; if (!stralloc_catb(&tmp,deps.s + id,strlen(deps.s + id) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!stralloc_catb(&sdir,deps.s + id,strlen(deps.s + id) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } /** parse all services of the modules */ for (pos = 0 ; pos < list.len ; pos += strlen(list.s + pos) + 1) { insta = 0, svtype = -1 , from_ext_insta = 0 ; char *sv = list.s + pos ; size_t len = strlen(sv) ; char bname[len + 1] ; char *pbname = bname ; addonsv.len = 0 ; if (!ob_basename(bname,sv)) log_warnu_return(LOG_EXIT_ZERO,"find basename of: ",sv) ; /** detect cyclic call. Sub-module cannot call it itself*/ if (!strcmp(svname,bname)) log_warn_return(LOG_EXIT_ZERO,"cyclic call detected -- ",svname," call ",bname) ; insta = instance_check(bname) ; if (!insta) log_warn_return(LOG_EXIT_ZERO,"invalid instance name: ",sv) ; if (insta > 0) { /** we can't know the origin of the instance * search first at service@ directory, if it not found * pass through the classic ss_resolve_src_path() function */ pbname = bname ; int found = 0 ; size_t len = strlen(permanent_sdir) ; char tmp[len + SS_MODULE_SERVICE_INSTANCE_LEN + 2] ; auto_strings(tmp,permanent_sdir,SS_MODULE_SERVICE_INSTANCE,"/") ; r = ss_resolve_src(&addonsv,pbname,tmp,&found) ; if (r == -1) log_warnusys_return(LOG_EXIT_ZERO,"parse source directory: ",tmp) ; if (!r) { if (ss_resolve_src_path(&addonsv,pbname,info->owner) < 1) log_warnu_return(LOG_EXIT_ZERO,"resolve source path of: ",pbname) ; } from_ext_insta++ ; sv = addonsv.s ; len = strlen(sv) ; } /** merge configuration file from upstream for each service * inside the module*/ conf = 2 ; if (!parse_service_before(info,parsed_list,tree_list,sv,nbsv,sasv,force,conf,0)) log_warnu_return(LOG_EXIT_ZERO,"parse: ",sv," from module: ",svname) ; char ext_insta[len + 1] ; if (from_ext_insta) { size_t len = strlen(sv) ; r = get_rlen_until(sv,'@',len) + 1 ; size_t newlen = len - (len - r) ; auto_strings(ext_insta,sv) ; ext_insta[newlen] = 0 ; sv = ext_insta ; } svtype = get_svtype_from_file(sv) ; if (svtype == -1) log_warnu_return(LOG_EXIT_ZERO,"get svtype of: ",sv) ; if (!stralloc_catb(&sdir,pbname,strlen(pbname) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (svtype != TYPE_CLASSIC) if (!stralloc_catb(&tmp,pbname,strlen(pbname) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } /** we want all service contained into the module including * the deps of the service */ for (ndeps = 0; ndeps < genalloc_len(sv_alltype,&gasv); ndeps++) { sv_alltype_ref service = &genalloc_s(sv_alltype,&gasv)[ndeps] ; int id = service->cname.itype == TYPE_MODULE ? service->cname.idcontents : service->cname.idga ; unsigned int nid = service->cname.itype == TYPE_MODULE ? service->cname.ncontents : service->cname.nga ; for (;nid; id += strlen(deps.s + id) + 1, nid--) { if (sastr_cmp(&sdir,deps.s + id) == -1) if (!stralloc_catb(&sdir,deps.s + id,strlen(deps.s + id) + 1)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; } } sv_before->cname.idga = deps.len ; sv_before->cname.nga = 0 ; for (pos = 0 ;pos < tmp.len ; pos += strlen(tmp.s + pos) + 1) { stralloc_catb(&deps,tmp.s + pos,strlen(tmp.s + pos) + 1) ; sv_before->cname.nga++ ; } sv_before->cname.idcontents = deps.len ; for (pos = 0 ;pos < sdir.len ; pos += strlen(sdir.s + pos) + 1) { stralloc_catb(&deps,sdir.s + pos,strlen(sdir.s + pos) + 1) ; sv_before->cname.ncontents++ ; } tmp.len = 0 ; if (!auto_stra(&tmp,permanent_sdir,SS_MODULE_CONFIG_DIR)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (rm_rf(tmp.s) < 0) log_warnusys_return(LOG_EXIT_ZERO,"remove: ",tmp.s) ; stralloc_free(&sdir) ; stralloc_free(&list) ; stralloc_free(&tmp) ; stralloc_free(&moduleinsta) ; stralloc_free(&addonsv) ; return err ; } /* helper */ /* 0 filename undefine * -1 system error * should return at least 2 meaning :: no file define*/ int regex_get_file_name(char *filename,char const *str) { int r ; size_t pos = 0 ; stralloc kp = STRALLOC_ZERO ; parse_mill_t MILL_GET_COLON = { .open = ':', .close = ':', .skip = " \t\r", .skiplen = 3, .forceclose = 1, .inner.debug = "get_colon" } ; r = mill_element(&kp,str,&MILL_GET_COLON,&pos) ; if (r == -1) goto err ; auto_strings(filename,kp.s) ; stralloc_free(&kp) ; return pos ; err: stralloc_free(&kp) ; return -1 ; } int regex_get_replace(char *replace, char const *str) { int pos = get_len_until(str,'=') ; if (!pos || pos == -1) return 0 ; char tmp[pos + 1] ; memcpy(tmp,str,pos) ; tmp[pos] = 0 ; auto_strings(replace,tmp) ; return 1 ; } int regex_get_regex(char *regex, char const *str) { size_t len = strlen(str) ; int pos = get_len_until(str,'=') ; if (!pos || pos == -1) return 0 ; pos++ ; // remove '=' char tmp[len + 1] ; memcpy(tmp,str + pos,len-pos) ; tmp[len-pos] = 0 ; auto_strings(regex,tmp) ; return 1 ; } int regex_replace(stralloc *list,sv_alltype *sv_before,char const *svname) { int r ; size_t in = 0, pos, inlen ; stralloc tmp = STRALLOC_ZERO ; for (; in < list->len; in += strlen(list->s + in) + 1) { tmp.len = 0 ; char *str = list->s + in ; size_t len = strlen(str) ; char bname[len + 1] ; char dname[len + 1] ; if (!ob_basename(bname,str)) log_warnu_return(LOG_EXIT_ZERO,"get basename of: ",str) ; if (!ob_dirname(dname,str)) log_warnu_return(LOG_EXIT_ZERO,"get dirname of: ",str) ; log_trace("read service file of: ",dname,bname) ; r = read_svfile(&tmp,bname,dname) ; if (!r) log_warnusys_return(LOG_EXIT_ZERO,"read file: ",str) ; if (r == -1) continue ; pos = sv_before->type.module.start_infiles, inlen = sv_before->type.module.end_infiles ; for (; pos < inlen ; pos += strlen(keep.s + pos) + 1) { int all = 0, fpos = 0 ; char filename[512] = { 0 } ; char replace[512] = { 0 } ; char regex[512] = { 0 } ; char const *line = keep.s + pos ; if (strlen(line) >= 511) log_warn_return(LOG_EXIT_ZERO,"limit exceeded in service: ", svname) ; if ((line[0] != ':') || (get_sep_before(line + 1,':','=') < 0)) log_warn_return(LOG_EXIT_ZERO,"bad format in line: ",line," of key @infiles field") ; fpos = regex_get_file_name(filename,line) ; if (fpos == -1) log_warnu_return(LOG_EXIT_ZERO,"get filename of line: ",line) ; else if (fpos < 3) all = 1 ; if (!regex_get_replace(replace,line+fpos)) log_warnu_return(LOG_EXIT_ZERO,"replace string of line: ",line) ; if (!regex_get_regex(regex,line+fpos)) log_warnu_return(LOG_EXIT_ZERO,"regex string of line: ",line) ; if (obstr_equal(bname,filename) || all) { if (!sastr_replace_all(&tmp,replace,regex)) log_warnu_return(LOG_EXIT_ZERO,"replace: ",replace," by: ", regex," in file: ",str) ; if (!file_write_unsafe(dname,bname,tmp.s,tmp.len)) log_warnusys_return(LOG_EXIT_ZERO,"write: ",dname,"/","filename") ; } } } freed: stralloc_free(&tmp) ; return 1 ; } int regex_rename(stralloc *list, int id, unsigned int nid, char const *sdir) { stralloc tmp = STRALLOC_ZERO ; size_t pos = id, len = nid, in ; pos = id, len = nid ; for (;len; pos += strlen(keep.s + pos) + 1,len--) { char *line = keep.s + pos ; char replace[512] = { 0 } ; char regex[512] = { 0 } ; if (!regex_get_replace(replace,line)) log_warnu_return(LOG_EXIT_ZERO,"replace string of line: ",line) ; if (!regex_get_regex(regex,line)) log_warnu_return(LOG_EXIT_ZERO,"regex string of line: ",line) ; for (in = 0 ; in < list->len; in += strlen(list->s + in) + 1) { tmp.len = 0 ; char *str = list->s + in ; size_t len = strlen(str) ; char dname[len + 1] ; if (!ob_dirname(dname,str)) log_warnu_return(LOG_EXIT_ZERO,"get dirname of: ",str) ; if (!sabasename(&tmp,str,len)) log_warnusys_return(LOG_EXIT_ZERO,"get basename of: ",str) ; if (!stralloc_0(&tmp)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; if (!sastr_replace(&tmp,replace,regex)) log_warnu_return(LOG_EXIT_ZERO,"replace: ",replace," by: ", regex," in file: ",str) ; char new[len + tmp.len + 1] ; auto_strings(new,dname,tmp.s) ; /** do not try to rename the same directory */ if (!obstr_equal(str,new)) { log_trace("rename: ",str," to: ",new) ; if (rename(str,new) == -1) log_warnusys_return(LOG_EXIT_ZERO,"rename: ",str," to: ",new) ; } } } stralloc_free(&tmp) ; return 1 ; } int regex_configure(sv_alltype *sv_before,ssexec_t *info, char const *module_dir,char const *module_name,uint8_t conf) { int wstat, r ; pid_t pid ; size_t clen = sv_before->type.module.configure > 0 ? 1 : 0 ; size_t module_dirlen = strlen(module_dir), n ; stralloc oenv = STRALLOC_ZERO ; stralloc env = STRALLOC_ZERO ; char const *newargv[2 + clen] ; unsigned int m = 0 ; char pwd[module_dirlen + SS_MODULE_CONFIG_DIR_LEN + 1] ; auto_strings(pwd,module_dir,SS_MODULE_CONFIG_DIR) ; char config_script[module_dirlen + SS_MODULE_CONFIG_DIR_LEN + 1 + SS_MODULE_CONFIG_SCRIPT_LEN + 1] ; auto_strings(config_script,module_dir,SS_MODULE_CONFIG_DIR,"/",SS_MODULE_CONFIG_SCRIPT) ; r = scan_mode(config_script,S_IFREG) ; if (r > 0) { /** export ssexec_t info value on the environment */ { char owner[UID_FMT]; owner[uid_fmt(owner,info->owner)] = 0 ; oenv.len = 0 ; char verbo[UINT_FMT]; verbo[uid_fmt(verbo,VERBOSITY)] = 0 ; oenv.len = 0 ; auto_stra(&env,"MOD_NAME=",module_name,"\n") ; auto_stra(&env,"MOD_BASE=",info->base.s,"\n") ; auto_stra(&env,"MOD_LIVE=",info->live.s,"\n") ; auto_stra(&env,"MOD_TREE=",info->tree.s,"\n") ; auto_stra(&env,"MOD_SCANDIR=",info->scandir.s,"\n") ; auto_stra(&env,"MOD_TREENAME=",info->treename.s,"\n") ; auto_stra(&env,"MOD_OWNER=",owner,"\n") ; auto_stra(&env,"MOD_VERBOSITY=",verbo,"\n") ; auto_stra(&env,"MOD_MODULE_DIR=",module_dir,"\n") ; auto_stra(&env,"MOD_SKEL_DIR=",SS_SKEL_DIR,"\n") ; auto_stra(&env,"MOD_SERVICE_SYSDIR=",SS_SERVICE_SYSDIR,"\n") ; auto_stra(&env,"MOD_SERVICE_ADMDIR=",SS_SERVICE_ADMDIR,"\n") ; auto_stra(&env,"MOD_SERVICE_ADMCONFDIR=",SS_SERVICE_ADMCONFDIR,"\n") ; auto_stra(&env,"MOD_MODULE_SYSDIR=",SS_MODULE_SYSDIR,"\n") ; auto_stra(&env,"MOD_MODULE_ADMDIR=",SS_MODULE_ADMDIR,"\n") ; auto_stra(&env,"MOD_SCRIPT_SYSDIR=",SS_SCRIPT_SYSDIR,"\n") ; auto_stra(&env,"MOD_USER_DIR=",SS_USER_DIR,"\n") ; auto_stra(&env,"MOD_SERVICE_USERDIR=",SS_SERVICE_USERDIR,"\n") ; auto_stra(&env,"MOD_SERVICE_USERCONFDIR=",SS_SERVICE_USERCONFDIR,"\n") ; auto_stra(&env,"MOD_MODULE_USERDIR=",SS_MODULE_USERDIR,"\n") ; auto_stra(&env,"MOD_SCRIPT_USERDIR=",SS_SCRIPT_USERDIR,"\n") ; } /** environment is not mandatory */ if (sv_before->opts[2] > 0) { /** sv_before->srconf is not set yet * we don't care, env_compute will find it. * env_compute return 2 in case of need to write */ r = env_compute(&oenv,sv_before,conf) ; if (!r) log_warnu_return(LOG_EXIT_ZERO,"compute environment") ; /** prepare for the environment merge */ if (oenv.len) { if (!environ_clean_envfile(&env,&oenv)) log_warnu_return(LOG_EXIT_ZERO,"prepare environment") ; if (!stralloc_0(&env)) log_warnsys_return(LOG_EXIT_ZERO,"stralloc") ; env.len-- ; } } if (!sastr_split_string_in_nline(&env)) log_warnu_return(LOG_EXIT_ZERO,"rebuild environment") ; n = env_len((const char *const *)environ) + 1 + byte_count(env.s,env.len,'\0') ; char const *newenv[n + 1] ; if (!env_merge (newenv, n ,(const char *const *)environ,env_len((const char *const *)environ),env.s, env.len)) log_warnusys_return(LOG_EXIT_ZERO,"build environment") ; if (chdir(pwd) < 0) log_warnusys_return(LOG_EXIT_ZERO,"chdir to: ",pwd) ; newargv[m++] = config_script ; if (sv_before->type.module.configure > 0) newargv[m++] = keep.s + sv_before->type.module.configure ; newargv[m++] = 0 ; log_info("launch script configure of module: ",module_name) ; pid = child_spawn0(newargv[0],newargv,newenv) ; if (waitpid_nointr(pid,&wstat, 0) < 0) log_warnusys_return(LOG_EXIT_ZERO,"wait for: ",config_script) ; if (wstat) log_warnu_return(LOG_EXIT_ZERO,"run: ",config_script) ; } stralloc_free(&oenv) ; stralloc_free(&env) ; return 1 ; }