/* * ssexec_env.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 <string.h> #include <stdlib.h>//getenv #include <unistd.h>//_exit,access #include <oblibs/obgetopt.h> #include <oblibs/log.h> #include <oblibs/files.h> #include <oblibs/string.h> #include <oblibs/types.h> #include <oblibs/sastr.h> #include <oblibs/environ.h> #include <skalibs/stralloc.h> #include <skalibs/genalloc.h> #include <skalibs/buffer.h> #include <skalibs/diuint32.h> #include <skalibs/djbunix.h> #include <skalibs/unix-transactional.h>//atomic_symlink #include <66/ssexec.h> #include <66/utils.h> #include <66/config.h> #include <66/parser.h> #include <66/environ.h> #include <66/constants.h> #include <66/resolve.h> enum tasks_e { T_UNSET = 0 , T_EDIT , T_VLIST , T_LIST , T_REPLACE } ; #define MAXOPTS 3 #define checkopts(n) if (n >= MAXOPTS) log_die(LOG_EXIT_USER, "too many versions number") #define DELIM ',' static uint8_t check_current_version(char const *svconf,char const *version) { stralloc sa = STRALLOC_ZERO ; if (!env_find_current_version(&sa,svconf)) log_dieu(LOG_EXIT_SYS,"find current version") ; char bname[sa.len + 1] ; if (!ob_basename(bname,sa.s)) log_dieu(LOG_EXIT_SYS,"get basename of: ",sa.s) ; stralloc_free(&sa) ; return !version_cmp(bname,version,SS_CONFIG_VERSION_NDOT) ? 1 : 0 ; } static void run_editor(char const *src, char const *const *envp) { char *editor = getenv("EDITOR") ; if (!editor) { editor = getenv("SUDO_USER") ; if (editor) log_dieu(LOG_EXIT_SYS,"get EDITOR with sudo command -- please try to use the -E sudo option e.g. sudo -E 66-env -e <service>") ; else log_dieusys(LOG_EXIT_SYS,"get EDITOR") ; } char const *const newarg[3] = { editor, src, 0 } ; xpathexec_run (newarg[0],newarg,envp) ; } static void do_import(char const *svname, char const *svconf, char const *version, int svtype) { size_t pos = 0 ; stralloc sasrc = STRALLOC_ZERO ; char *src_version = 0 ; char *dst_version = 0 ; if (!sastr_clean_string_wdelim(&sasrc,version,DELIM)) log_dieu(LOG_EXIT_SYS,"clean string: ",version) ; unsigned int n = sastr_len(&sasrc) ; checkopts(n) ; FOREACH_SASTR(&sasrc,pos) { if (!pos) src_version = sasrc.s + pos ; else dst_version = sasrc.s + pos ; } if (!env_import_version_file(svname,svconf,src_version,dst_version,svtype)) log_dieu(LOG_EXIT_SYS,"import configuration file from version: ",src_version," to version: ",dst_version) ; stralloc_free(&sasrc) ; } static void replace_value_of_key(stralloc *srclist,char const *key) { stralloc sakey = STRALLOC_ZERO ; size_t pos = 0 ; int start = -1 ,end = -1 ; if (!auto_stra(&sakey,key)) log_die_nomem("stralloc") ; if (!environ_get_key_nclean(&sakey,&pos)) log_die(LOG_EXIT_SYS,"invalid format at key: ",key) ; sakey.len-- ; if (!auto_stra(&sakey,"=")) log_die_nomem("stralloc") ; start = sastr_find(srclist,sakey.s) ; if (start == -1) { log_1_warnu("find key: ",sakey.s) ; return ; } end = get_len_until(srclist->s + start,'\n') ; if (end == -1) log_dieu(LOG_EXIT_SYS,"find end of line") ; sakey.len = 0 ; if (!stralloc_catb(&sakey,srclist->s + start,end ) || !stralloc_0(&sakey)) log_die_nomem("stralloc") ; if (!strcmp(sakey.s,key)) return ; if (!sastr_replace(srclist,sakey.s,key)) log_dieu(LOG_EXIT_SYS,"replace: ",sakey.s," by: ",key) ; stralloc_free(&sakey) ; } int ssexec_env(int argc, char const *const *argv,char const *const *envp,ssexec_t *info) { int r ; size_t pos = 0 ; stralloc satmp = STRALLOC_ZERO ; stralloc sasrc = STRALLOC_ZERO ; stralloc saversion = STRALLOC_ZERO ; stralloc eversion = STRALLOC_ZERO ; stralloc savar = STRALLOC_ZERO ; stralloc salist = STRALLOC_ZERO ; ss_resolve_t res = RESOLVE_ZERO ; uint8_t todo = T_UNSET ; char const *sv = 0, *svconf = 0, *src = 0, *treename = 0, *import = 0 ; { subgetopt_t l = SUBGETOPT_ZERO ; for (;;) { int opt = getopt_args(argc,argv, ">c:s:VLd:r:ei:", &l) ; if (opt == -1) break ; if (opt == -2) log_die(LOG_EXIT_USER,"options must be set first") ; switch (opt) { case 'c' : if (env_check_version(&saversion,l.arg) <= 0) log_dieu(LOG_EXIT_SYS,"check version format") ; break ; case 's' : if (env_check_version(&eversion,l.arg) <= 0) log_dieu(LOG_EXIT_SYS,"check version format") ; break ; case 'V' : if (todo != T_UNSET) log_usage(usage_env) ; todo = T_VLIST ; break ; case 'L' : if (todo != T_UNSET) log_usage(usage_env) ; todo = T_LIST ; break ; case 'd' : log_1_warn("-d: deprecated option") ; goto freed ; case 'r' : if (!sastr_add_string(&savar,l.arg)) log_die_nomem("stralloc") ; if (todo != T_UNSET && todo != T_REPLACE) log_usage(usage_env) ; todo = T_REPLACE ; break ; case 'e' : if (todo != T_UNSET) log_usage(usage_env) ; todo = T_EDIT ; break ; case 'i' : import = l.arg ; break ; default : log_usage(usage_env) ; } } argc -= l.ind ; argv += l.ind ; } if (argc < 1) log_usage(usage_env) ; sv = argv[0] ; if (todo == T_UNSET && !import && !saversion.len && !eversion.len) todo = T_EDIT ; treename = !info->opt_tree ? 0 : info->treename.s ; r = ss_resolve_svtree(&sasrc,sv,treename) ; if (r == -1) log_dieu(LOG_EXIT_SYS,"resolve tree source of sv: ",sv) ; else if (!r) { log_info("no tree exist yet") ; goto freed ; } else if (r > 2) { log_die(LOG_EXIT_SYS,sv," is set on different tree -- please use -t options") ; } else if (r == 1) log_die(LOG_EXIT_SYS,"unknown service: ",sv, !info->opt_tree ? " in current tree: " : " in tree: ", info->treename.s) ; if (!ss_resolve_read(&res,sasrc.s,sv)) log_dieusys(LOG_EXIT_SYS,"read resolve file of: ",sv) ; if (!res.srconf) { log_1_warn(sv," do not have configuration file") ; goto freed ; } svconf = res.sa.s + res.srconf ; sasrc.len = 0 ; if (saversion.len) { size_t conflen = strlen(svconf) ; char sym[conflen + SS_SYM_VERSION_LEN + 1] ; auto_strings(sym,svconf,SS_SYM_VERSION) ; if (!env_append_version(&saversion,svconf,saversion.s)) log_dieu(LOG_EXIT_ZERO,"append version") ; r = scan_mode(saversion.s,S_IFDIR) ; if (r == -1 || !r) log_dieusys(LOG_EXIT_USER,"find the versioned directory: ",saversion.s) ; if (!atomic_symlink(saversion.s,sym,"ssexec_env")) log_warnu_return(LOG_EXIT_ZERO,"symlink: ",sym," to: ",saversion.s) ; log_info("symlink switched successfully to version: ",saversion.s) ; } if (import) do_import(sv,svconf,import,res.type) ; if (eversion.len) { if (!env_append_version(&sasrc,svconf,eversion.s)) log_dieu(LOG_EXIT_SYS,"append version") ; src = sasrc.s ; } else { if (!auto_stra(&sasrc,svconf,SS_SYM_VERSION)) log_die_nomem("stralloc") ; if (sareadlink(&satmp,sasrc.s) == -1) log_dieusys(LOG_EXIT_SYS,"readlink: ",sasrc.s) ; if (!stralloc_0(&satmp) || !stralloc_copy(&sasrc,&satmp)) log_die_nomem("stralloc") ; src = sasrc.s ; } satmp.len = 0 ; switch(todo) { case T_VLIST: if (!sastr_dir_get(&satmp,svconf,SS_SYM_VERSION + 1,S_IFDIR)) log_dieu(LOG_EXIT_SYS,"get versioned directory of: ",svconf) ; for (pos = 0 ; pos < satmp.len; pos += strlen(satmp.s + pos) + 1) { if (buffer_puts(buffer_1, svconf) < 0) log_dieusys(LOG_EXIT_SYS, "write to stdout") ; if (buffer_puts(buffer_1, "/") < 0) log_dieusys(LOG_EXIT_SYS, "write to stdout") ; if (buffer_puts(buffer_1, satmp.s + pos) < 0) log_dieusys(LOG_EXIT_SYS, "write to stdout") ; if (check_current_version(svconf,satmp.s + pos)) if (buffer_putsflush(buffer_1, " current") < 0) log_dieusys(LOG_EXIT_SYS, "write to stdout") ; if (buffer_putsflush(buffer_1, "\n") < 0) log_dieusys(LOG_EXIT_SYS, "write to stdout") ; } break ; case T_LIST: if (!sastr_dir_get(&satmp,src,SS_SYM_VERSION + 1,S_IFREG)) log_dieu(LOG_EXIT_SYS,"get versioned directory at: ",src) ; for (pos = 0 ; pos < satmp.len; pos += strlen(satmp.s + pos) + 1) { salist.len = 0 ; char *name = satmp.s + pos ; if (!file_readputsa(&salist,src,name)) log_dieusys(LOG_EXIT_SYS,"read: ",src,"/",name) ; if (!stralloc_0(&salist)) log_die_nomem("stralloc") ; log_info("contents of file: ",src,"/",name,"\n",salist.s) ; } break ; case T_REPLACE: if (!file_readputsa(&salist,src,sv)) log_dieusys(LOG_EXIT_SYS,"read: ",src,"/",sv) ; FOREACH_SASTR(&savar,pos) { char *key = savar.s + pos ; replace_value_of_key(&salist,key) ; } if (!auto_stra(&satmp,src,"/",sv)) log_die_nomem("stralloc") ; if (!openwritenclose_unsafe(satmp.s,salist.s,salist.len - 1)) log_dieusys(LOG_EXIT_SYS,"write file: ",satmp.s) ; break ; case T_EDIT: { size_t srclen = strlen(src), svlen = strlen(sv) ; int r ; char tsrc[srclen + 1 + svlen +1] ; auto_strings(tsrc,src,"/",sv) ; errno = 0 ; if (access(tsrc, F_OK) < 0) { if (errno == ENOENT) { salist.len = 0 ; stralloc sa = STRALLOC_ZERO ; if (!auto_stra(&salist,src,"/.",sv)) log_die_nomem("stralloc") ; if (!file_readputsa_g(&sa,salist.s)) log_dieusys(LOG_EXIT_SYS,"read environment file from: ",salist.s) ; r = str_contain(sa.s,"[ENDWARN]") ; if (r == -1) log_die(LOG_EXIT_SYS,"invalid upstream configuration file! Do you have modified it? Tries to enable it again.") ; if (!write_env(sv,sa.s + r,src)) log_dieusys(LOG_EXIT_SYS,"copy: ",salist.s," to: ",tsrc); stralloc_free(&sa) ; } else log_diesys(LOG_EXIT_SYS,"conflicting format of file: ",tsrc) ; } run_editor(tsrc,envp) ; } /** Can't happens */ default: break ; } freed: stralloc_free(&satmp) ; stralloc_free(&sasrc) ; stralloc_free(&saversion) ; stralloc_free(&eversion) ; stralloc_free(&savar) ; stralloc_free(&salist) ; ss_resolve_free(&res) ; return 0 ; }