-
Eric Vidal authoredEric Vidal authored
66-inservice.c 34.56 KiB
/*
* 66-inservice.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 <string.h>
#include <locale.h>
#include <langinfo.h>
#include <sys/types.h>
#include <wchar.h>
#include <unistd.h>
#include <stdlib.h>
#include <oblibs/sastr.h>
#include <oblibs/log.h>
#include <oblibs/types.h>
#include <oblibs/string.h>
#include <oblibs/files.h>
#include <oblibs/directory.h>
#include <oblibs/graph.h>
#include <skalibs/stralloc.h>
#include <skalibs/genalloc.h>
#include <skalibs/lolstdio.h>
#include <skalibs/bytestr.h>
#include <skalibs/djbunix.h>
#include <skalibs/buffer.h>
#include <skalibs/sgetopt.h>
#include <66/info.h>
#include <66/utils.h>
#include <66/constants.h>
#include <66/tree.h>
#include <66/enum.h>
#include <66/resolve.h>
#include <66/environ.h>
#include <66/state.h>
#include <66/service.h>
#include <66/graph.h>
#include <s6/supervise.h>
static unsigned int REVERSE = 0 ;
static unsigned int NOFIELD = 1 ;
static unsigned int GRAPH = 0 ;
static char const *const *ENVP ;
static unsigned int nlog = 20 ;
static stralloc src = STRALLOC_ZERO ;
static stralloc home = STRALLOC_ZERO ;// /var/lib/66/system or ${HOME}/system
static wchar_t const field_suffix[] = L" :" ;
static char fields[INFO_NKEY][INFO_FIELD_MAXLEN] = {{ 0 }} ;
static void info_display_string(char const *str) ;
static void info_display_name(char const *field, resolve_service_t *res) ;
static void info_display_intree(char const *field, resolve_service_t *res) ;
static void info_display_status(char const *field, resolve_service_t *res) ;
static void info_display_type(char const *field, resolve_service_t *res) ;
static void info_display_description(char const *field, resolve_service_t *res) ;
static void info_display_version(char const *field, resolve_service_t *res) ;
static void info_display_source(char const *field, resolve_service_t *res) ;
static void info_display_live(char const *field, resolve_service_t *res) ;
static void info_display_deps(char const *field, resolve_service_t *res) ;
static void info_display_requiredby(char const *field, resolve_service_t *res) ;
static void info_display_optsdeps(char const *field, resolve_service_t *res) ;
static void info_display_extdeps(char const *field, resolve_service_t *res) ;
static void info_display_start(char const *field, resolve_service_t *res) ;
static void info_display_stop(char const *field, resolve_service_t *res) ;
static void info_display_envat(char const *field, resolve_service_t *res) ;
static void info_display_envfile(char const *field, resolve_service_t *res) ;
static void info_display_logname(char const *field, resolve_service_t *res) ;
static void info_display_logdst(char const *field, resolve_service_t *res) ;
static void info_display_logfile(char const *field, resolve_service_t *res) ;
info_graph_style *STYLE = &graph_default ;
#include <stdio.h>// a effacer
info_opts_map_t const opts_sv_table[] =
{
{ .str = "name", .svfunc = &info_display_name, .id = 0 },
{ .str = "version", .svfunc = &info_display_version, .id = 1 },
{ .str = "intree", .svfunc = &info_display_intree, .id = 2 },
{ .str = "status", .svfunc = &info_display_status, .id = 3 },
{ .str = "type", .svfunc = &info_display_type, .id = 4 },
{ .str = "description", .svfunc = &info_display_description, .id = 5 },
{ .str = "source", .svfunc = &info_display_source, .id = 6 },
{ .str = "live", .svfunc = &info_display_live, .id = 7 },
{ .str = "depends", .svfunc = &info_display_deps, .id = 8 },
{ .str = "requiredby", .svfunc = &info_display_requiredby, .id = 9 },
{ .str = "extdepends", .svfunc = &info_display_extdeps, .id = 10 },
{ .str = "optsdepends", .svfunc = &info_display_optsdeps, .id = 11 },
{ .str = "start", .svfunc = &info_display_start, .id = 12 },
{ .str = "stop", .svfunc = &info_display_stop, .id = 13 },
{ .str = "envat", .svfunc = &info_display_envat, .id = 14 },
{ .str = "envfile", .svfunc = &info_display_envfile, .id = 15 },
{ .str = "logname", .svfunc = &info_display_logname, .id = 16 },
{ .str = "logdst", .svfunc = &info_display_logdst, .id = 17 },
{ .str = "logfile", .svfunc = &info_display_logfile, .id = 18 },
{ .str = 0, .svfunc = 0, .id = -1 }
} ;
#define MAXOPTS 20
#define checkopts(n) if (n >= MAXOPTS) log_die(LOG_EXIT_USER, "too many options")
#define DELIM ','
#define USAGE "66-inservice [ -h ] [ -z ] [ -v verbosity ] [ -n ] [ -o name,intree,status,... ] [ -g ] [ -d depth ] [ -r ] [ -t tree ] [ -p nline ] service"
static inline void info_help (void)
{
DEFAULT_MSG = 0 ;
static char const *help =
"\n"
"options :\n"
" -h: print this help\n"
" -z: use color\n"
" -v: increase/decrease verbosity\n"
" -n: do not display the field name\n"
" -o: comma separated list of field to display\n"
" -g: displays the contents field as graph\n"
" -d: limit the depth of the contents field recursion\n"
" -r: reverse the contents field\n"
" -t: only search service at the specified tree\n"
" -p: print n last lines of the log file\n"
"\n"
"valid fields for -o options are:\n"
"\n"
" name: displays the name\n"
" version: displays the version of the service\n"
" intree: displays the service's tree name\n"
" status: displays the status\n"
" type: displays the service type\n"
" description: displays the description\n"
" source: displays the source of the service's frontend file\n"
" live: displays the service's live directory\n"
" depends: displays the service's dependencies\n"
" requiredby: displays the service(s) which depends on service\n"
" extdepends: displays the service's external dependencies\n"
" optsdepends: displays the service's optional dependencies\n"
" start: displays the service's start script\n"
" stop: displays the service's stop script\n"
" envat: displays the source of the environment file\n"
" envfile: displays the contents of the environment file\n"
" logname: displays the logger's name\n"
" logdst: displays the logger's destination\n"
" logfile: displays the contents of the log file\n"
;
log_info(USAGE,"\n",help) ;
}
char *print_nlog(char *str, int n)
{
int r = 0 ;
int delim ='\n' ;
size_t slen = strlen(str) ;
if (n <= 0) return NULL;
size_t ndelim = 0;
char *target_pos = NULL;
r = get_rlen_until(str,delim,slen) ;
target_pos = str + r ;
if (target_pos == NULL) return NULL;
while (ndelim <= n)
{
while (str < target_pos && *target_pos != delim)
--target_pos;
if (*target_pos == delim)
--target_pos, ++ndelim;
else break;
}
if (str < target_pos)
target_pos += 2;
return target_pos ;
}
static void info_display_string(char const *str)
{
if (!bprintf(buffer_1,"%s",str))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
if (buffer_putsflush(buffer_1,"\n") == -1)
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
static void info_display_name(char const *field, resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(res->sa.s + res->name) ;
}
static void info_display_version(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
/** tempory check here, it not mandatory for the moment*/
if (res->version > 0)
{
info_display_string(res->sa.s + res->version) ;
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
static void info_display_intree(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(res->sa.s + res->treename) ;
}
static void info_get_status(resolve_service_t *res)
{
int r ;
int wstat ;
pid_t pid ;
ss_state_t sta = STATE_ZERO ;
int warn_color = 0 ;
if (res->type == TYPE_CLASSIC || res->type == TYPE_LONGRUN)
{
r = s6_svc_ok(res->sa.s + res->runat) ;
if (r != 1)
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
return ;
}
char const *newargv[3] ;
unsigned int m = 0 ;
newargv[m++] = SS_BINPREFIX "s6-svstat" ;
newargv[m++] = res->sa.s + res->runat ;
newargv[m++] = 0 ;
pid = child_spawn0(newargv[0],newargv,ENVP) ;
if (waitpid_nointr(pid,&wstat, 0) < 0)
log_dieusys(LOG_EXIT_SYS,"wait for ",newargv[0]) ;
if (wstat)
log_dieu(LOG_EXIT_SYS,"status for service: ",res->sa.s + res->name) ;
}
else
{
char *ste = res->sa.s + res->state ;
char *name = res->sa.s + res->name ;
char *status = 0 ;
if (!state_check(ste,name))
{
status = "unitialized" ;
goto dis ;
}
if (!state_read(&sta,ste,name))
log_dieusys(LOG_EXIT_SYS,"read state of: ",name) ;
if (sta.init) {
status = "unitialized" ;
}
else if (!sta.state)
{
status = "down" ;
warn_color = 1 ;
}
else if (sta.state)
{
status = "up" ;
warn_color = 2 ;
}
dis:
if (!bprintf(buffer_1,"%s%s%s\n",warn_color > 1 ? log_color->valid : warn_color ? log_color->error : log_color->warning,status,log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
static void info_display_status(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
if (!bprintf(buffer_1,"%s%s%s%s",res->disen ? log_color->valid : log_color->error,res->disen ? "enabled" : "disabled",log_color->off,", "))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
if (buffer_putsflush(buffer_1,"") == -1)
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
info_get_status(res) ;
}
static void info_display_type(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(get_key_by_enum(ENUM_TYPE,res->type)) ;
}
static void info_display_description(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(res->sa.s + res->description) ;
}
static void info_display_source(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(res->sa.s + res->src) ;
}
static void info_display_live(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
info_display_string(res->sa.s + res->runat) ;
}
/*
int graph_service_compute_deps(stralloc *deps, char const *str)
{
stralloc tmp = STRALLOC_ZERO ;
size_t pos = 0 ;
if (!sastr_clean_string(&tmp,str))
log_warnu_return(LOG_EXIT_ZERO,"rebuild dependencies list") ;
FOREACH_SASTR(&tmp,pos) {
if (!sastr_add_string(deps, tmp.s + pos))
return 0 ;
}
stralloc_free(&tmp) ;
return 1 ;
}
*/
void ss_graph_matrix_add_classic(graph_t *g, genalloc *gares)
{
size_t pos = 0, bpos = 0, ccount = 0 ;
size_t cl[SS_MAX_SERVICE] ;
for (; pos < genalloc_len(resolve_service_t, gares) ; pos++) {
resolve_service_t_ref res = &genalloc_s(resolve_service_t, gares)[pos] ;
if (res->type == TYPE_CLASSIC) {
cl[ccount++] = pos ;
}
}
if (ccount) {
for (pos = 0 ; pos < ccount ; pos++) {
char *str = genalloc_s(resolve_service_t, gares)[cl[pos]].sa.s ;
char *sv = str + genalloc_s(resolve_service_t, gares)[cl[pos]].name ;
printf("sv::%s\n",sv) ;
graph_array_reverse(g->sort, g->sort_count) ;
for (bpos = 0 ; bpos < g->sort_count ; bpos++) {
char *service = g->data.s + genalloc_s(graph_hash_t,&g->hash)[g->sort[bpos]].vertex ;
int idx = resolve_search(gares, service, SERVICE_STRUCT) ;
if (genalloc_s(resolve_service_t, gares)[idx].type == TYPE_CLASSIC ||
!strcmp(service, sv))
continue ;
if (!graph_edge_add_g(g, service, sv))
log_die(LOG_EXIT_SYS,"add edge: ", sv, " at vertex: ", service) ;
graph_free_matrix(g) ;
graph_free_sort(g) ;
if (!graph_matrix_build(g)) {
graph_free_all(g) ;
log_dieu(LOG_EXIT_SYS,"build the graph") ;
}
if (!graph_matrix_analyze_cycle(g))
log_die(LOG_EXIT_SYS,"found cycle") ;
}
graph_array_reverse(g->sort, g->sort_count) ;
}
}
}
/** what = 0 -> only classic
* what = 1 -> only atomic
* what = 2 -> both
* @Return 0 on fail
*
* This function append the logger to @gares is case of classic service. */
int ss_tree_get_sv_resolve(genalloc *gares, char const *dir, uint8_t what)
{
log_flow() ;
stralloc sa = STRALLOC_ZERO ;
resolve_service_t res = RESOLVE_SERVICE_ZERO ;
resolve_wrapper_t_ref wres = resolve_set_struct(SERVICE_STRUCT, &res) ;
resolve_service_t reslog = RESOLVE_SERVICE_ZERO ;
resolve_wrapper_t_ref wreslog = resolve_set_struct(SERVICE_STRUCT, &reslog) ;
int e = 0 ;
size_t dirlen = strlen(dir), pos = 0 ;
char solve[dirlen + SS_RESOLVE_LEN + 1] ;
auto_strings(solve, dir, SS_RESOLVE) ;
char const *exclude[2] = { SS_MASTER + 1, 0 } ;
if (!sastr_dir_get(&sa,solve,exclude,S_IFREG))
goto err ;
FOREACH_SASTR(&sa, pos) {
char *name = sa.s + pos ;
if (!resolve_check(dir,name))
goto err ;
if (!resolve_read(wres,dir,name))
goto err ;
if (resolve_search(gares,name, SERVICE_STRUCT) == -1) {
if ((!what || what == 2) && (res.type == TYPE_CLASSIC)) {
if (res.logger) {
if (!resolve_read(wreslog, dir, res.sa.s + res.logger))
goto err ;
if (resolve_search(gares,res.sa.s + res.logger, SERVICE_STRUCT) == -1) {
if (!resolve_append(gares,wreslog))
goto err ;
}
}
if (!resolve_append(gares,wres))
goto err ;
continue ;
}
if (what) {
if (!resolve_append(gares,wres))
goto err ;
}
}
}
e = 1 ;
err:
stralloc_free(&sa) ;
resolve_free(wres) ;
resolve_free(wreslog) ;
return e ;
}
static void info_display_requiredby(char const *field, resolve_service_t *res)
{
size_t padding = 1 ;
int r ;
graph_t graph = GRAPH_ZERO ;
if (NOFIELD) padding = info_display_field_name(field) ;
else { field = 0 ; padding = 0 ; }
if (!graph_service_build_bytree(&graph, res->sa.s + res->tree, 2))
log_dieu(LOG_EXIT_SYS,"build the graph dependencies") ;
unsigned int list[graph.mlen] ;
int count = graph_matrix_get_requiredby(list, &graph, res->sa.s + res->name , 0) ;
if (count == -1)
log_dieu(LOG_EXIT_SYS,"get the requiredby list for service: ", res->sa.s + res->name) ;
if (!count) goto empty ;
if (GRAPH) {
if (!bprintf(buffer_1,"%s\n","/"))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
depth_t d = info_graph_init() ;
if (!info_walk(&graph, res->sa.s + res->name, res->sa.s + res->treename, &info_graph_display_service, 1, REVERSE, &d, padding, STYLE))
log_dieu(LOG_EXIT_SYS,"display the requiredby list") ;
return ;
} else {
deps.len = 0 ;
r = graph_matrix_get_edge_g_sorted(&deps,&graph, res->sa.s + res->name, 1) ;
if (r == -1)
log_dieu(LOG_EXIT_SYS, "get the requiredby list") ;
if (!r)
goto empty ;
if (REVERSE)
if (!sastr_reverse(&deps))
log_dieu(LOG_EXIT_SYS,"reverse the requiredby list") ;
info_display_list(field,&deps) ;
return ;
}
empty:
if (GRAPH)
{
if (!bprintf(buffer_1,"%s\n","/"))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
if (!bprintf(buffer_1,"%*s%s%s%s%s\n",padding, "", STYLE->last, log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
static void info_display_deps(char const *field, resolve_service_t *res)
{
int r ;
size_t padding = 1 ;
graph_t graph = GRAPH_ZERO ;
stralloc deps = STRALLOC_ZERO ;
if (NOFIELD) padding = info_display_field_name(field) ;
else { field = 0 ; padding = 0 ; }
if (!graph_service_build_bytree(&graph, res->sa.s + res->tree, 2))
log_dieu(LOG_EXIT_SYS,"build the graph dependencies") ;
unsigned int list[graph.mlen] ;
int count = graph_matrix_get_edge(list, &graph, res->sa.s + res->name , 0) ;
if (count == -1)
log_dieu(LOG_EXIT_SYS,"get the dependencies list for service: ", res->sa.s + res->name) ;
if (!count) goto empty ;
if (GRAPH)
{
if (!bprintf(buffer_1,"%s\n","/"))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
depth_t d = info_graph_init() ;
if (!info_walk(&graph, res->sa.s + res->name, res->sa.s + res->treename, &info_graph_display_service, 0, REVERSE, &d, padding, STYLE))
log_dieu(LOG_EXIT_SYS,"display the dependencies list") ;
goto freed ;
}
else
{
r = graph_matrix_get_edge_g_sorted(&deps,&graph, res->sa.s + res->name, 0) ;
if (r == -1)
log_dieu(LOG_EXIT_SYS, "get the dependencies list") ;
if (!r)
goto empty ;
if (REVERSE)
if (!sastr_reverse(&deps))
log_dieu(LOG_EXIT_SYS,"reverse the dependencies list") ;
info_display_list(field,&deps) ;
goto freed ;
}
empty:
if (GRAPH)
{
if (!bprintf(buffer_1,"%s\n","/"))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
if (!bprintf(buffer_1,"%*s%s%s%s%s\n",padding, "", STYLE->last, log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
freed:
stralloc_free(&deps) ;
}
static void info_display_with_source_tree(stralloc *list,resolve_service_t *res)
{
size_t pos = 0, lpos = 0, newlen = 0 ;
stralloc svlist = STRALLOC_ZERO ;
stralloc ntree = STRALLOC_ZERO ;
stralloc src = STRALLOC_ZERO ;
stralloc tmp = STRALLOC_ZERO ;
char const *exclude[3] = { SS_BACKUP + 1, SS_RESOLVE + 1, 0 } ;
char *treename = 0 ;
if (!auto_stra(&src,home.s)) log_die_nomem("stralloc") ;
newlen = src.len ;
if (!sastr_dir_get(&ntree,home.s,exclude,S_IFDIR))
log_dieu(LOG_EXIT_SYS,"get list of trees of: ",home.s) ;
for (pos = 0 ; pos < ntree.len ; pos += strlen(ntree.s + pos) + 1)
{
svlist.len = 0 ;
src.len = newlen ;
treename = ntree.s + pos ;
if (!auto_stra(&src,treename,SS_SVDIRS,SS_RESOLVE))
log_die_nomem("stralloc") ;
exclude[0] = SS_MASTER + 1 ;
exclude[1] = 0 ;
if (!sastr_dir_get(&svlist,src.s,exclude,S_IFREG))
log_dieu(LOG_EXIT_SYS,"get contents of tree: ",src.s) ;
for (lpos = 0 ; lpos < list->len ; lpos += strlen(list->s + lpos) + 1)
{
char *name = list->s + lpos ;
if (sastr_cmp(&svlist,name) >= 0)
{
if (!stralloc_cats(&tmp,name) ||
!stralloc_cats(&tmp,":") ||
!stralloc_catb(&tmp,treename,strlen(treename) +1))
log_die_nomem("stralloc") ;
}
}
}
list->len = 0 ;
for (pos = 0 ; pos < tmp.len ; pos += strlen(tmp.s + pos) + 1)
if (!stralloc_catb(list,tmp.s + pos,strlen(tmp.s + pos) + 1))
log_die_nomem("stralloc") ;
stralloc_free (&svlist) ;
stralloc_free (&ntree) ;
stralloc_free (&src) ;
stralloc_free (&tmp) ;
}
static void info_display_optsdeps(char const *field, resolve_service_t *res)
{
stralloc salist = STRALLOC_ZERO ;
if (NOFIELD) info_display_field_name(field) ;
else field = 0 ;
if (!res->noptsdeps) goto empty ;
if (!sastr_clean_string(&salist,res->sa.s + res->optsdeps))
log_dieu(LOG_EXIT_SYS,"build dependencies list") ;
info_display_with_source_tree(&salist,res) ;
if (REVERSE)
if (!sastr_reverse(&salist))
log_dieu(LOG_EXIT_SYS,"reverse dependencies list") ;
info_display_list(field,&salist) ;
goto freed ;
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
freed:
stralloc_free(&salist) ;
}
static void info_display_extdeps(char const *field, resolve_service_t *res)
{
stralloc salist = STRALLOC_ZERO ;
if (NOFIELD) info_display_field_name(field) ;
else field = 0 ;
if (!res->nextdeps) goto empty ;
if (!sastr_clean_string(&salist,res->sa.s + res->extdeps))
log_dieu(LOG_EXIT_SYS,"build dependencies list") ;
info_display_with_source_tree(&salist,res) ;
if (REVERSE)
if (!sastr_reverse(&salist))
log_dieu(LOG_EXIT_SYS,"reverse dependencies list") ;
info_display_list(field,&salist) ;
goto freed ;
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
freed:
stralloc_free(&salist) ;
}
static void info_display_start(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
else field = 0 ;
if (res->exec_run)
{
info_display_nline(field,res->sa.s + res->exec_run) ;
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
static void info_display_stop(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
else field = 0 ;
if (res->exec_finish)
{
info_display_nline(field,res->sa.s + res->exec_finish) ;
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
static void info_display_envat(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
stralloc salink = STRALLOC_ZERO ;
if (!res->srconf) goto empty ;
{
stralloc salink = STRALLOC_ZERO ;
char *src = res->sa.s + res->srconf ;
size_t srclen = strlen(src) ;
char sym[srclen + SS_SYM_VERSION_LEN + 1] ;
auto_strings(sym,src,SS_SYM_VERSION) ;
if (sareadlink(&salink, sym) == -1)
log_dieusys(LOG_EXIT_SYS,"read link of: ",sym) ;
if (!stralloc_0(&salink))
log_die_nomem("stralloc") ;
info_display_string(salink.s) ;
stralloc_free(&salink) ;
goto freed ;
}
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
freed:
stralloc_free(&salink) ;
}
static void info_display_envfile(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
else field = 0 ;
size_t pos = 0 ;
stralloc sa = STRALLOC_ZERO ;
stralloc salink = STRALLOC_ZERO ;
stralloc list = STRALLOC_ZERO ;
char const *exclude[1] = { 0 } ;
if (res->srconf)
{
char *src = res->sa.s + res->srconf ;
size_t srclen = strlen(src), newlen ;
char sym[srclen + SS_SYM_VERSION_LEN + 1] ;
auto_strings(sym,src,SS_SYM_VERSION) ;
if (sareadlink(&salink, sym) == -1)
log_dieusys(LOG_EXIT_SYS,"read link of: ",sym) ;
if (!stralloc_0(&salink))
log_dieusys(LOG_EXIT_SYS,"stralloc") ;
newlen = salink.len - 1 ;
if (!sastr_dir_get(&list,salink.s,exclude,S_IFREG))
log_dieusys(LOG_EXIT_SYS,"get list of environment file from: ",src) ;
if (!sastr_sort(&list))
log_dieu(LOG_EXIT_SYS,"sort environment file name") ;
FOREACH_SASTR(&list,pos) {
ssize_t upstream = 0 ;
sa.len = 0 ;
salink.len = newlen ;
if (!stralloc_cats(&salink,"/") ||
!stralloc_cats(&salink,list.s + pos) ||
!stralloc_0(&salink)) log_die_nomem("stralloc") ;
if (!file_readputsa_g(&sa,salink.s))
log_dieusys(LOG_EXIT_SYS,"read environment file") ;
/** Remove warning message */
if (list.s[pos] == '.') {
char t[sa.len + 1] ;
upstream = str_contain(sa.s,"[ENDWARN]") ;
if (upstream == -1)
log_die(LOG_EXIT_SYS,"invalid upstream configuration file! Do you have modified it? Tries to enable the service again.") ;
auto_strings(t,sa.s + upstream) ;
sa.len = 0 ;
if (!auto_stra(&sa,t))
log_die_nomem("stralloc") ;
}
if (NOFIELD) {
char *m = "environment variables from: " ;
size_t mlen = strlen(m) ;
char msg[mlen + salink.len + 2] ;
auto_strings(msg,m,salink.s,"\n") ;
if (!stralloc_inserts(&sa,0,msg) ||
!stralloc_0(&sa))
log_die_nomem("stralloc") ;
}
if (pos)
{
if (NOFIELD) {
size_t padding = info_length_from_wchar(field) + 1 ;
if (!bprintf(buffer_1,"%*s",padding,""))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
info_display_nline(field,sa.s) ;
}
else info_display_nline(field,sa.s) ;
if (!bprintf(buffer_1,"%s","\n"))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
}
else
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
stralloc_free(&sa) ;
stralloc_free(&list) ;
stralloc_free(&salink) ;
}
static void info_display_logname(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
if (res->type == TYPE_CLASSIC || res->type == TYPE_LONGRUN)
{
if (res->logger)
{
info_display_string(res->sa.s + res->logger) ;
}
else goto empty ;
}
else goto empty ;
return ;
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
static void info_display_logdst(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
if (res->type != TYPE_BUNDLE || res->type != TYPE_MODULE)
{
if (res->logger || (res->type == TYPE_ONESHOT && res->dstlog))
{
info_display_string(res->sa.s + res->dstlog) ;
}
else goto empty ;
}
else goto empty ;
return ;
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
static void info_display_logfile(char const *field,resolve_service_t *res)
{
if (NOFIELD) info_display_field_name(field) ;
if (res->type != TYPE_BUNDLE || res->type != TYPE_MODULE)
{
if (res->logger || (res->type == TYPE_ONESHOT && res->dstlog))
{
if (nlog)
{
stralloc log = STRALLOC_ZERO ;
/** the file current may not exist if the service was never started*/
size_t dstlen = strlen(res->sa.s + res->dstlog) ;
char scan[dstlen + 9] ;
memcpy(scan,res->sa.s + res->dstlog,dstlen) ;
memcpy(scan + dstlen,"/current",8) ;
scan[dstlen + 8] = 0 ;
int r = scan_mode(scan,S_IFREG) ;
if (r < 0) { errno = EEXIST ; log_diesys(LOG_EXIT_SYS,"conflicting format of: ",scan) ; }
if (!r)
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->error,"unable to find the log file",log_color->off))
goto err ;
}
else
{
if (!file_readputsa(&log,res->sa.s + res->dstlog,"current")) log_dieusys(LOG_EXIT_SYS,"read log file of: ",res->sa.s + res->name) ;
/* we don't need to freed stralloc
* file_readputsa do it if the file is empty*/
if (!log.len) goto empty ;
log.len-- ;
if (!auto_stra(&log,"\n")) log_dieusys(LOG_EXIT_SYS,"append newline") ;
if (log.len < 10 && res->type != TYPE_ONESHOT)
{
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off)) goto err ;
}
else
{
if (!bprintf(buffer_1,"\n")) goto err ;
if (!bprintf(buffer_1,"%s\n",print_nlog(log.s,nlog))) goto err ;
}
}
stralloc_free(&log) ;
}
}else goto empty ;
}
else goto empty ;
return ;
empty:
if (!bprintf(buffer_1,"%s%s%s\n",log_color->warning,"None",log_color->off))
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
return ;
err:
log_dieusys(LOG_EXIT_SYS,"write to stdout") ;
}
static void info_display_all(resolve_service_t *res,int *what)
{
unsigned int i = 0 ;
for (; what[i] >= 0 ; i++)
{
unsigned int idx = what[i] ;
(*opts_sv_table[idx].svfunc)(fields[opts_sv_table[idx].id],res) ;
}
}
static void info_parse_options(char const *str,int *what)
{
size_t pos = 0 ;
stralloc sa = STRALLOC_ZERO ;
if (!sastr_clean_string_wdelim(&sa,str,DELIM)) log_dieu(LOG_EXIT_SYS,"parse options") ;
unsigned int n = sastr_len(&sa), nopts = 0 , old ;
checkopts(n) ;
info_opts_map_t const *t ;
for (;pos < sa.len; pos += strlen(sa.s + pos) + 1)
{
char *o = sa.s + pos ;
t = opts_sv_table ;
old = nopts ;
for (; t->str; t++)
{
if (obstr_equal(o,t->str))
what[nopts++] = t->id ;
}
if (old == nopts) log_die(LOG_EXIT_SYS,"invalid option: ",o) ;
}
stralloc_free(&sa) ;
}
int main(int argc, char const *const *argv, char const *const *envp)
{
unsigned int legacy = 1 ;
int found = 0 ;
int what[MAXOPTS] = { 0 } ;
uid_t owner ;
char ownerstr[UID_FMT] ;
resolve_service_t res = RESOLVE_SERVICE_ZERO ;
resolve_wrapper_t_ref wres = resolve_set_struct(SERVICE_STRUCT, &res) ;
stralloc satree = STRALLOC_ZERO ;
log_color = &log_color_disable ;
char const *svname = 0 ;
char const *tname = 0 ;
ENVP = envp ;
for (int i = 0 ; i < MAXOPTS ; i++)
what[i] = -1 ;
char buf[MAXOPTS][INFO_FIELD_MAXLEN] = {
"Name",
"Version" ,
"In tree",
"Status",
"Type",
"Description",
"Source",
"Live",
"Dependencies",
"Required by",
"External dependencies" ,
"Optional dependencies" ,
"Start script",
"Stop script",
"Environment source",
"Environment file",
"Log name",
"Log destination",
"Log file" } ;
PROG = "66-inservice" ;
{
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
int opt = subgetopt_r(argc,argv, "hzv:cno:grd:t:p:", &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'h' : info_help(); return 0 ;
case 'v' : if (!uint0_scan(l.arg, &VERBOSITY)) log_usage(USAGE) ; break ;
case 'z' : log_color = !isatty(1) ? &log_color_disable : &log_color_enable ; break ;
case 'n' : NOFIELD = 0 ; break ;
case 'o' : legacy = 0 ; info_parse_options(l.arg,what) ; break ;
case 'g' : GRAPH = 1 ; break ;
case 'r' : REVERSE = 1 ; break ;
case 'd' : if (!uint0_scan(l.arg, &MAXDEPTH)) log_usage(USAGE) ; break ;
case 't' : tname = l.arg ; break ;
case 'p' : if (!uint0_scan(l.arg, &nlog)) log_usage(USAGE) ; break ;
default : log_usage(USAGE) ;
}
}
argc -= l.ind ; argv += l.ind ;
}
if (!argc) log_usage(USAGE) ;
svname = *argv ;
if (legacy)
{
unsigned int i = 0 ;
for (; i < MAXOPTS - 1 ; i++)
what[i] = i ;
what[i] = -1 ;
}
owner = getuid() ;
size_t ownerlen = uid_fmt(ownerstr,owner) ;
ownerstr[ownerlen] = 0 ;
info_field_align(buf,fields,field_suffix,MAXOPTS) ;
setlocale(LC_ALL, "");
if(!strcmp(nl_langinfo(CODESET), "UTF-8")) {
STYLE = &graph_utf8;
}
if (!set_ownersysdir(&home,owner)) log_dieusys(LOG_EXIT_SYS, "set owner directory") ;
if (!auto_stra(&home,SS_SYSTEM,"/")) log_die_nomem("stralloc") ;
found = service_intree(&src,svname,tname) ;
if (found == -1) log_dieu(LOG_EXIT_SYS,"resolve tree source of service: ",svname) ;
else if (!found) {
log_info("no tree exist yet") ;
goto freed ;
}
else if (found > 2) {
log_die(LOG_EXIT_SYS,svname," is set on different tree -- please use -t options") ;
}
else if (found == 1) log_die(LOG_EXIT_SYS,"unknown service: ",svname) ;
if (!resolve_read(wres,src.s,svname))
log_dieusys(LOG_EXIT_SYS,"read resolve file of: ",svname) ;
info_display_all(&res,what) ;
if (buffer_putsflush(buffer_1,"\n") == -1)
log_dieusys(LOG_EXIT_SYS, "write to stdout") ;
freed:
resolve_free(wres) ;
stralloc_free(&src) ;
stralloc_free(&home) ;
stralloc_free(&satree) ;
return 0 ;
}