/* * parse_service.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 <stdint.h> #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> //access #include <oblibs/log.h> #include <oblibs/string.h> #include <oblibs/sastr.h> #include <oblibs/types.h> #include <oblibs/directory.h> #include <skalibs/djbunix.h> #include <66/enum.h> #include <66/constants.h> #include <66/parse.h> #include <66/ssexec.h> #include <66/config.h> #include <66/write.h> #include <66/state.h> #include <66/resolve.h> #include <66/service.h> #include <66/tree.h> #include <66/graph.h> #include <66/sanitize.h> #include <66/symlink.h> #include <66/hash.h> lexer_config LEXER_CONFIG_SECTION = { \ .str = 0,\ .slen = 0,\ .open = "[",\ .olen = 1,\ .close = "]",\ .clen = 1,\ .skip = " \t\r",\ .skiplen = 3,\ .kopen = 0,\ .kclose = 0,\ .style = 0,\ .forceopen = 0,\ .forceclose = 0,\ .pos = 0,\ .opos = 0,\ .cpos = 0,\ .invalue = 0,\ .found = 0,\ .exitcode = 0,\ } ; lexer_config LEXER_CONFIG_QUOTE = { \ .str = 0,\ .slen = 0,\ .open = "\"",\ .olen = 1,\ .close = "\n\"",\ .clen = 2,\ .skip = 0,\ .skiplen = 0,\ .kopen = 0,\ .kclose = 0,\ .style = 0,\ .forceopen = 0,\ .forceclose = 0,\ .pos = 0,\ .opos = 0,\ .cpos = 0,\ .invalue = 0,\ .found = 0,\ .exitcode = 0,\ } ; lexer_config LEXER_CONFIG_INLINE = { \ .str = 0,\ .slen = 0,\ .open = "=",\ .olen = 1,\ .close = "\n",\ .clen = 1,\ .skip = " \t\r",\ .skiplen = 3,\ .kopen = 0,\ .kclose = 0,\ .style = 0,\ .forceopen = 0,\ .forceclose = 0,\ .pos = 0,\ .opos = 0,\ .cpos = 0,\ .invalue = 0,\ .found = 0,\ .exitcode = 0,\ } ; lexer_config LEXER_CONFIG_LIST = { \ .str = 0,\ .slen = 0,\ .open = 0,\ .olen = 0,\ .close = " \n",\ .clen = 2,\ .skip = " \t\r\n",\ .skiplen = 4,\ .kopen = 1,\ .kclose = 0,\ .style = 0,\ .forceopen = 1,\ .forceclose = 1,\ .pos = 0,\ .opos = 0,\ .cpos = 0,\ .invalue = 0,\ .found = 0,\ .exitcode = 0,\ } ; lexer_config LEXER_CONFIG_KEY = { \ .str = 0,\ .slen = 0,\ .open = "ABCDEFGHIJKLMNOPQRSTUVWXYZ",\ .olen = 26,\ .close = "=\n",\ .clen = 2,\ .skip = " \t\r",\ .skiplen = 3,\ .kopen = 1,\ .kclose = 0,\ .style = 0,\ .forceopen = 0,\ .forceclose = 0,\ .pos = 0,\ .opos = 0,\ .cpos = 0,\ .invalue = 0,\ .found = 0,\ .exitcode = 0,\ } ; void parse_cleanup(resolve_service_t *res, char const *tmpdir, uint8_t force) { log_flow() ; size_t namelen = strlen(res->sa.s + res->name), homelen = strlen(res->sa.s + res->path.home) ; char dir[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 1] ; if (!dir_rm_rf(tmpdir)) log_warnu("remove temporary directory: ", tmpdir) ; if (!force) { auto_strings(dir, res->sa.s + res->path.home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", res->sa.s + res->name) ; if (!dir_rm_rf(dir)) log_warnu("remove service directory: ", dir) ; } } void parse_copy_to_source(char const *dst, char const *src, resolve_service_t *res, uint8_t force) { log_flow() ; size_t pos = 0, srclen = strlen(src) ; if (!access(dst, F_OK)) { stralloc sa = STRALLOC_ZERO ; char const *exclude[6] = { SS_EVENTDIR + 1, SS_SUPERVISEDIR + 1, SS_SCANDIR, SS_STATE + 1, SS_RESOLVE + 1, 0 } ; if (!sastr_dir_get_recursive(&sa, dst, exclude, S_IFDIR|S_IFREG, 1)) { parse_cleanup(res, src, force) ; stralloc_free(&sa) ; log_dieusys(LOG_EXIT_SYS, "get file list of: ", dst) ; } FOREACH_SASTR(&sa, pos) { char base[strlen(sa.s + pos) + 1] ; if (!ob_basename(base, sa.s + pos)) { parse_cleanup(res, src, force) ; stralloc_free(&sa) ; log_dieusys(LOG_EXIT_SYS, "basename of: ", sa.s + pos) ; } char element[srclen + 1 + strlen(base) + 1] ; auto_strings(element, src, "/", base) ; if (access(element, F_OK) < 0) { log_trace("remove element: ", sa.s + pos) ; if (!dir_rm_rf(sa.s + pos)) { parse_cleanup(res, src, force) ; stralloc_free(&sa) ; log_dieusys(LOG_EXIT_SYS, "remove element: ", sa.s + pos) ; } } } stralloc_free(&sa) ; } if (access(dst, F_OK) < 0) { log_trace("create directory: ", dst) ; if (!dir_create_parent(dst, 0755)) { parse_cleanup(res, src, force) ; log_dieusys(LOG_EXIT_SYS, "create directory: ", dst) ; } } log_trace("copy:", src, " to: ", dst) ; if (!hiercopy(src, dst)) { parse_cleanup(res, src, force) ; log_dieusys(LOG_EXIT_SYS, "copy: ", src, " to: ", dst) ; } /** be paranoid after the use of hiercopy and be sure * to have dst in 0755 mode. If not the log cannot be * executed with other permissions than root */ if (chmod(dst, 0755)< 0) { parse_cleanup(res, src, force) ; log_dieusys(LOG_EXIT_SYS,"chmod: ", dst) ; } } static void parse_write_state(resolve_service_t *res, char const *dst, uint8_t force) { log_flow() ; ss_state_t sta = STATE_ZERO ; char dir[strlen(dst) + SS_STATE_LEN + 1] ; auto_strings(dir, dst, SS_STATE) ; state_set_flag(&sta, STATE_FLAGS_TOINIT, STATE_FLAGS_TRUE) ; state_set_flag(&sta, STATE_FLAGS_TOPARSE, STATE_FLAGS_FALSE) ; state_set_flag(&sta, STATE_FLAGS_ISPARSED, STATE_FLAGS_TRUE) ; if (!state_write_remote(&sta, dir)) { parse_cleanup(res, dst, force) ; log_dieu(LOG_EXIT_SYS, "write state file of: ", res->sa.s + res->name) ; } } void parse_service(struct resolve_hash_s **hres, char const *sv, ssexec_t *info, uint8_t force, uint8_t conf) { log_flow(); int r ; uint8_t rforce = 0 ; _alloc_sa_(sa) ; struct resolve_hash_s *c, *tmp ; char main[strlen(sv) + 1] ; if (!ob_basename(main, sv)) log_dieu(LOG_EXIT_SYS, "get basename of: ", sv) ; r = parse_frontend(sv, hres, info, force, conf, 0, main, 0, 0) ; if (r == 2) /** already parsed */ return ; HASH_ITER(hh, *hres, c, tmp) { if (!c->visit) { sa.len = 0 ; /** be paranoid */ rforce = 0 ; size_t namelen = strlen(c->res.sa.s + c->res.name), homelen = strlen(c->res.sa.s + c->res.path.home) ; char servicedir[homelen + SS_SYSTEM_LEN + SS_SERVICE_LEN + SS_SVC_LEN + 1 + namelen + 1] ; auto_strings(servicedir, c->res.sa.s + c->res.path.home, SS_SYSTEM, SS_SERVICE, SS_SVC, "/", c->res.sa.s + c->res.name) ; if (sanitize_write(&c->res, force)) rforce = 1 ; if (!auto_stra(&sa, "/tmp/", c->res.sa.s + c->res.name, ":XXXXXX")) log_die_nomem("stralloc") ; if (!mkdtemp(sa.s)) log_dieusys(LOG_EXIT_SYS, "create temporary directory") ; write_services(&c->res, sa.s, rforce) ; parse_write_state(&c->res, sa.s, rforce) ; service_resolve_write_remote(&c->res, sa.s, rforce) ; parse_copy_to_source(servicedir, sa.s, &c->res, rforce) ; /** do not die here, just warn the user */ log_trace("remove temporary directory: ", sa.s) ; if (!dir_rm_rf(sa.s)) log_warnu("remove temporary directory: ", sa.s) ; tree_service_add(c->res.sa.s + c->res.treename, c->res.sa.s + c->res.name, info) ; if (!symlink_make(&c->res)) log_dieusys(LOG_EXIT_SYS, "make service symlink") ; /** symlink may exist already, be sure to point to the correct location */ if (!symlink_switch(&c->res, SYMLINK_SOURCE)) log_dieusys(LOG_EXIT_SYS, "sanitize_symlink") ; log_info("Parsed successfully: ", c->res.sa.s + c->res.name, " at tree: ", c->res.sa.s + c->res.treename) ; c->visit = 1 ; } } }