opt_serv.c
#define _GNU_SOURCE 1
#include "opt_serv.h"
#define EX_UTILS_NO_FUNCS 1
#include "ex_utils.h"
#include "opt_policy.h"
#include "mk.h"
#include "opt_conf.h"
#include <stddef.h>
#include <sys/resource.h>
#include <signal.h>
#include <sys/utsname.h>
#ifndef CONF_FULL_STATIC
# include <pwd.h>
# include <grp.h>
# include <sys/types.h>
#endif
#ifndef HAVE_STRSIGNAL
# define strsignal(x) ""
#endif
#ifdef RLIMIT_AS
# define USE_RLIMIT_AS TRUE
#else
# define USE_RLIMIT_AS FALSE
# define RLIMIT_AS 0
#endif
#include <syslog.h>
static Vlg *vlg = NULL;
int opt_serv_sc_append_hostname(Vstr_base *s1, size_t pos)
{
static char buf[256];
static size_t len = 0;
if (!len)
{
if (gethostname(buf, sizeof(buf)) == -1)
err(EXIT_FAILURE, "gethostname");
buf[sizeof(buf) - 1] = 0;
len = strlen(buf);
}
return (vstr_add_ptr(s1, pos, buf, len));
}
static int opt_serv__init_append_hostname(void)
{
Vstr_base *tmp = vstr_make_base(NULL);
if (!tmp)
return (FALSE);
opt_serv_sc_append_hostname(tmp, 0);
vstr_free_base(tmp);
return (TRUE);
}
int opt_serv_sc_append_cwd(Vstr_base *s1, size_t pos)
{
static size_t sz = PATH_MAX;
char *ptr = MK(sz);
int ret = FALSE;
if (ptr)
{
const size_t maxsz = 8 * PATH_MAX;
char *tmp = NULL;
tmp = getcwd(ptr, sz);
while (!tmp && (errno == ERANGE) && (sz < maxsz))
{
sz += PATH_MAX;
if (!MV(ptr, tmp, sz))
break;
tmp = getcwd(ptr, sz);
}
if (!tmp)
ret = vstr_add_cstr_ptr(s1, pos, "/");
else
ret = vstr_add_cstr_buf(s1, pos, tmp);
}
F(ptr);
return (ret);
}
static int opt_serv_sc_append_env(Vstr_base *s1, size_t pos,
const Conf_parse *conf, Conf_token *token)
{
const Vstr_sect_node *pv = NULL;
const char *env_name = NULL;
const char *env_data = NULL;
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
if (!(pv = conf_token_value(token)))
return (FALSE);
if (!(env_name = vstr_export_cstr_ptr(conf->data, pv->pos, pv->len)))
return (FALSE);
env_data = getenv(env_name);
if (!env_data) env_data = "";
return (vstr_add_cstr_buf(s1, pos, env_data));
}
#ifdef CONF_FULL_STATIC
# define OPT_SERV_SC_APPEND_HOMEDIR(w, x, y, z) CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE)
#else
static int opt_serv_sc_append_homedir(Vstr_base *s1, size_t pos,
const Conf_parse *conf, Conf_token *token)
{
const Vstr_sect_node *pv = NULL;
const char *usr_name = NULL;
const char *usr_data = "";
struct passwd *pw = NULL;
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
if (!(pv = conf_token_value(token)))
return (FALSE);
if (!(usr_name = vstr_export_cstr_ptr(conf->data, pv->pos, pv->len)))
return (FALSE);
if ((pw = getpwnam(usr_name)))
usr_data = pw->pw_dir;
return (vstr_add_cstr_buf(s1, pos, usr_data));
}
# define OPT_SERV_SC_APPEND_HOMEDIR(w, x, y, z) \
opt_serv_sc_append_homedir(w, x, y, z)
#endif
#define OPT_SERV_SC_APPEND_UNAME(x) do { \
if (!vstr_add_cstr_buf(conf->tmp, conf->tmp->len, utsname-> x)) \
return (FALSE); \
} while (FALSE)
int opt_serv_sc_tst(Conf_parse *conf, Conf_token *token,
int *matches, int prev_match,
int (*tst_func)(Conf_parse *, Conf_token *,
int *, int, void *), void *data)
{
if (0) { }
else if (OPT_SERV_SYM_EQ("else") || OPT_SERV_SYM_EQ("ELSE"))
*matches = !prev_match;
else if (OPT_SERV_SYM_EQ("true") || OPT_SERV_SYM_EQ("TRUE"))
*matches = TRUE;
else if (OPT_SERV_SYM_EQ("false") || OPT_SERV_SYM_EQ("FALSE"))
*matches = FALSE;
else if (OPT_SERV_SYM_EQ("not") || OPT_SERV_SYM_EQ("NOT") ||
OPT_SERV_SYM_EQ("!"))
{
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
if (!(*tst_func)(conf, token, matches, prev_match, data))
return (FALSE);
*matches = !*matches;
}
else if (OPT_SERV_SYM_EQ("or") || OPT_SERV_SYM_EQ("OR") ||
OPT_SERV_SYM_EQ("||"))
{
unsigned int depth = token->depth_num;
while (conf_token_list_num(token, depth))
{
int or_matches = TRUE;
CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth, FALSE);
if (!(*tst_func)(conf, token, &or_matches, prev_match, data))
return (FALSE);
if (or_matches)
{
int ret = conf_parse_end_token(conf, token, depth);
ASSERT(ret);
return (TRUE);
}
}
*matches = FALSE;
}
else if (OPT_SERV_SYM_EQ("and") || OPT_SERV_SYM_EQ("AND") ||
OPT_SERV_SYM_EQ("&&"))
{
unsigned int depth = token->depth_num;
while (conf_token_list_num(token, depth))
{
CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth, FALSE);
if (!(*tst_func)(conf, token, matches, prev_match, data))
return (FALSE);
if (!*matches)
{
int ret = conf_parse_end_token(conf, token, depth);
ASSERT(ret);
return (TRUE);
}
}
}
else
return (FALSE);
return (TRUE);
}
#define OPT_SERV__IOLIM_VAL(x) do { \
\
OPT_SERV_X_SYM_ULONG_BEG(x); \
else if (OPT_SERV_SYM_EQ("<unlimited>")) (x) = 0; \
else if (OPT_SERV_SYM_EQ("<default>")) (x) = 0; \
OPT_SERV_X_SYM_NUM_END(); \
} while (FALSE)
static int opt_serv__conf_main_policy_d1(Opt_serv_policy_opts *opts,
Conf_parse *conf, Conf_token *token,
int clist)
{
unsigned int dummy;
if (0) { }
else if (OPT_SERV_SYM_EQ("match-init"))
OPT_SERV_SC_MATCH_INIT(opts->beg,
opt_serv__conf_main_policy_d1(opts, conf, token,
clist));
else if (OPT_SERV_SYM_EQ("timeout"))
{
CONF_SC_MAKE_CLIST_BEG(timeout, clist);
else if (OPT_SERV_SYM_EQ("idle"))
OPT_SERV_X_UINT(opts->idle_timeout);
else if (OPT_SERV_SYM_EQ("total"))
OPT_SERV_X_SINGLE_UINT(dummy);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("instant-close"))
OPT_SERV_X_TOGGLE(opts->use_insta_close);
else if (OPT_SERV_SYM_EQ("lingering-close"))
OPT_SERV_X_NEG_TOGGLE(opts->use_insta_close);
else if (OPT_SERV_SYM_EQ("limit"))
{
CONF_SC_MAKE_CLIST_BEG(limit, clist);
else if (OPT_SERV_SYM_EQ("connections"))
OPT_SERV_X_UINT(opts->max_connections);
else if (OPT_SERV_SYM_EQ("io"))
{
unsigned int clist_ioo = FALSE;
unsigned int clist_ioi = FALSE;
CONF_SC_MAKE_CLIST_BEG(limit_ioo, clist_ioo);
else if (OPT_SERV_SYM_EQ("policy-process/s") ||
OPT_SERV_SYM_EQ("policy-process-per-second"))
{
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(opts->io_limit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(opts->io_limit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("policy-connection/s") ||
OPT_SERV_SYM_EQ("policy-connection-per-second"))
{
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(opts->io_nslimit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(opts->io_nslimit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
CONF_SC_MAKE_CLIST_END();
}
CONF_SC_MAKE_CLIST_END();
}
else
return (FALSE);
return (TRUE);
}
static int opt_serv__conf_main_policy(Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token)
{
Opt_serv_policy_opts *popts = NULL;
Conf_token *ntoken = NULL;
unsigned int cur_depth = opt_policy_sc_conf_parse(opts, conf, token,
&popts, &ntoken);
if (!cur_depth)
return (FALSE);
do
{
int clist = FALSE;
CONF_SC_MAKE_CLIST_MID(cur_depth, clist);
else if (opt_serv__conf_main_policy_d1(popts, conf, token, clist))
{ }
CONF_SC_MAKE_CLIST_END();
} while (ntoken &&
(cur_depth = opt_policy_sc_conf_parse(opts, conf, token,
&popts, &ntoken)));
return (TRUE);
}
static int opt_serv__match_init_tst_d1(struct Opt_serv_opts *,
Conf_parse *, Conf_token *,
int *, int);
static int opt_serv__match_init_tst_op_d1(Conf_parse *conf, Conf_token *token,
int *matches, int prev_match,
void *passed_data)
{
struct Opt_serv_opts *opts = passed_data;
return (opt_serv__match_init_tst_d1(opts, conf, token, matches, prev_match));
}
static int opt_serv__match_init_tst_d1(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
int *matches, int prev_match)
{
int clist = FALSE;
ASSERT(matches);
CONF_SC_TOGGLE_CLIST_VAR(clist);
if (0) {}
else if (OPT_SERV_SYM_EQ("version<=") || OPT_SERV_SYM_EQ("vers<="))
{
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
*matches = (vstr_cmp_vers_buf(conf->tmp, 1, conf->tmp->len,
opts->vers_cstr, opts->vers_len) >= 0);
}
else if (OPT_SERV_SYM_EQ("version>=") || OPT_SERV_SYM_EQ("vers>="))
{
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
*matches = (vstr_cmp_vers_buf(conf->tmp, 1, conf->tmp->len,
opts->vers_cstr, opts->vers_len) <= 0);
}
else if (OPT_SERV_SYM_EQ("version-eq") || OPT_SERV_SYM_EQ("vers=="))
{
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
*matches = (vstr_cmp_vers_buf(conf->tmp, 1, conf->tmp->len,
opts->vers_cstr, opts->vers_len) == 0);
}
else if (OPT_SERV_SYM_EQ("name-eq") || OPT_SERV_SYM_EQ("name=="))
{
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
*matches = vstr_cmp_buf_eq(conf->tmp, 1, conf->tmp->len,
opts->name_cstr, opts->name_len);
}
else if (OPT_SERV_SYM_EQ("hostname-eq") ||
OPT_SERV_SYM_EQ("hostname=="))
{
size_t lpos = 0;
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
lpos = conf->tmp->len;
if (!opt_serv_sc_append_hostname(conf->tmp, lpos))
return (FALSE);
if (lpos == conf->tmp->len)
*matches = !!conf->tmp->len;
else
{
size_t len = vstr_sc_posdiff(lpos + 1, conf->tmp->len);
*matches = vstr_cmp_case_eq(conf->tmp, 1, lpos, conf->tmp, lpos + 1, len);
}
}
else if (OPT_SERV_SYM_EQ("uid-eq") ||
OPT_SERV_SYM_EQ("uid=="))
{
unsigned int dummy = 0;
OPT_SERV_X_SINGLE_UINT(dummy);
*matches = (dummy == getuid());
}
else if (OPT_SERV_SYM_EQ("euid-eq") ||
OPT_SERV_SYM_EQ("euid=="))
{
unsigned int dummy = 0;
OPT_SERV_X_SINGLE_UINT(dummy);
*matches = (dummy == geteuid());
}
else if (OPT_SERV_SYM_EQ("debug"))
*matches = COMPILE_DEBUG;
else
return (opt_serv_sc_tst(conf, token, matches, prev_match,
opt_serv__match_init_tst_op_d1, opts));
return (TRUE);
}
int opt_serv_match_init(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token)
{
static int prev_match = TRUE;
int matches = TRUE;
unsigned int depth = token->depth_num;
CONF_SC_PARSE_SLIST_DEPTH_TOKEN_RET(conf, token, depth, FALSE);
++depth;
while (conf_token_list_num(token, depth))
{
CONF_SC_PARSE_DEPTH_TOKEN_RET(conf, token, depth, FALSE);
if (!opt_serv__match_init_tst_d1(opts, conf, token, &matches, prev_match))
return (FALSE);
if (!matches)
conf_parse_end_token(conf, token, depth - 1);
}
prev_match = matches;
return (TRUE);
}
#define OPT_SERV__RLIM_VAL(x, y) do { \
(y) = TRUE; \
\
OPT_SERV_X_SYM_ULONG_BEG(x); \
else if (OPT_SERV_SYM_EQ("<infinity>")) (x) = RLIM_INFINITY; \
else if (OPT_SERV_SYM_EQ("<unlimited>")) (x) = RLIM_INFINITY; \
else if (OPT_SERV_SYM_EQ("<default>")) (y) = FALSE; \
OPT_SERV_X_SYM_NUM_END(); \
} while (FALSE)
#define OPT_SERV__TMP_EQ(x) vstr_cmp_cstr_eq(conf->tmp, 1, conf->tmp->len, x)
#define OPT_SERV_ADDR_DUP_VSTR(x, y, z) \
(((x)-> z) = vstr_dup_vstr((y)-> z ->conf , \
(y)-> z , 1, (y)-> z ->len, \
VSTR_TYPE_SUB_BUF_REF))
static void opt_serv__free_addr(Opt_serv_addr_opts *addr)
{
if (!addr)
return;
vstr_free_base(addr->acpt_filter_file);
vstr_free_base(addr->acpt_address);
vstr_free_base(addr->acpt_cong);
vstr_free_base(addr->def_policy);
F(addr);
}
static Opt_serv_addr_opts *opt_serv_make_addr(Opt_serv_opts *)
COMPILE_ATTR_NONNULL_A() COMPILE_ATTR_WARN_UNUSED_RET();
static Opt_serv_addr_opts *opt_serv_make_addr(Opt_serv_opts *opts)
{
Opt_serv_addr_opts *addr = NULL;
ASSERT(opts && opts->addr_beg);
if (opts->no_conf_listen)
{
opts->no_conf_listen = FALSE;
return (opts->addr_beg);
}
if (!(addr = MK(sizeof(Opt_serv_addr_opts))))
goto mk_addr_fail;
OPT_SERV_ADDR_DUP_VSTR(addr, opts->addr_beg, acpt_filter_file);
OPT_SERV_ADDR_DUP_VSTR(addr, opts->addr_beg, acpt_address);
OPT_SERV_ADDR_DUP_VSTR(addr, opts->addr_beg, acpt_cong);
OPT_SERV_ADDR_DUP_VSTR(addr, opts->addr_beg, def_policy);
if (!addr->acpt_filter_file ||
!addr->acpt_address ||
!addr->acpt_cong ||
!addr->def_policy ||
FALSE)
goto addr_init_fail;
addr->next = NULL;
addr->tcp_port = opts->addr_beg->tcp_port;
addr->defer_accept = opts->addr_beg->defer_accept;
addr->q_listen_len = opts->addr_beg->q_listen_len;
addr->max_connections = opts->addr_beg->max_connections;
addr->next = opts->addr_beg;
opts->addr_beg = addr;
return (addr);
addr_init_fail:
F(addr);
mk_addr_fail:
return (FALSE);
}
static Opt_serv_addr_opts *opt_serv__srch_addr(Opt_serv_addr_opts *scan,
Opt_serv_addr_opts *addr)
{
while (scan)
{
if ((scan->tcp_port == addr->tcp_port) &&
vstr_cmp_eq(scan->acpt_address, 1, scan->acpt_address->len,
addr->acpt_address, 1, addr->acpt_address->len))
break;
scan = scan->next;
}
return (scan);
}
#define OPT_SERV__ADDR_MERGE_VSTR(z) do { \
if (!vstr_sub_vstr(oaddr-> z, 1, oaddr-> z ->len, \
naddr-> z, 1, naddr-> z ->len, VSTR_TYPE_SUB_DEF)) \
return (FALSE); \
} while (FALSE)
static int opt_serv_merge_addrs(Opt_serv_opts *)
COMPILE_ATTR_NONNULL_A() COMPILE_ATTR_WARN_UNUSED_RET();
static int opt_serv_merge_addrs(Opt_serv_opts *opts)
{
Opt_serv_addr_opts *oaddr = NULL;
Opt_serv_addr_opts *naddr = NULL;
ASSERT(opts && opts->addr_beg);
naddr = opts->addr_beg;
oaddr = opt_serv__srch_addr(naddr->next, naddr);
if (!oaddr)
return (TRUE);
ASSERT(!opt_serv__srch_addr(oaddr->next, naddr));
OPT_SERV__ADDR_MERGE_VSTR(acpt_filter_file);
ASSERT(vstr_cmp_eq(oaddr->acpt_address, 1, oaddr->acpt_address->len,
naddr->acpt_address, 1, naddr->acpt_address->len));
OPT_SERV__ADDR_MERGE_VSTR(acpt_cong);
OPT_SERV__ADDR_MERGE_VSTR(def_policy);
ASSERT(oaddr->tcp_port == naddr->tcp_port);
oaddr->defer_accept = naddr->defer_accept;
oaddr->q_listen_len = naddr->q_listen_len;
oaddr->max_connections = naddr->max_connections;
opts->addr_beg = naddr->next;
opt_serv__free_addr(naddr);
return (TRUE);
}
static int opt_serv__conf_d1(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
int clist)
{
if (0) { }
else if (OPT_SERV_SYM_EQ("match-init"))
OPT_SERV_SC_MATCH_INIT(opts, opt_serv__conf_d1(opts, conf, token, clist));
else if (OPT_SERV_SYM_EQ("policy"))
{
if (!opt_serv__conf_main_policy(opts, conf, token))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("chroot"))
return (opt_serv_sc_make_static_path(opts, conf, token, opts->chroot_dir));
else if (OPT_SERV_SYM_EQ("cntl-file") ||
OPT_SERV_SYM_EQ("control-file"))
return (opt_serv_sc_make_static_path(opts, conf, token, opts->cntl_file));
else if (OPT_SERV_SYM_EQ("daemonize") || OPT_SERV_SYM_EQ("daemonise"))
OPT_SERV_X_TOGGLE(opts->become_daemon);
else if (OPT_SERV_SYM_EQ("drop-privs"))
{
unsigned int depth = token->depth_num;
int val = opts->drop_privs;
int ern = conf_sc_token_parse_toggle(conf, token, &val);
unsigned int num = conf_token_list_num(token, token->depth_num);
if (ern == CONF_SC_TYPE_RET_ERR_NO_MATCH)
return (FALSE);
if (!val && num)
return (FALSE);
opts->drop_privs = val;
CONF_SC_MAKE_CLIST_MID(depth, clist);
else if (OPT_SERV_SYM_EQ("uid"))
OPT_SERV_X_SINGLE_UINT(opts->priv_uid);
else if (OPT_SERV_SYM_EQ("usrname") ||
OPT_SERV_SYM_EQ("username"))
OPT_SERV_X_VSTR(opts, opts->vpriv_uid);
else if (OPT_SERV_SYM_EQ("gid"))
OPT_SERV_X_SINGLE_UINT(opts->priv_gid);
else if (OPT_SERV_SYM_EQ("grpname") ||
OPT_SERV_SYM_EQ("groupname"))
OPT_SERV_X_VSTR(opts, opts->vpriv_gid);
else if (OPT_SERV_SYM_EQ("keep-CAP_FOWNER") ||
OPT_SERV_SYM_EQ("keep-cap-fowner"))
OPT_SERV_X_TOGGLE(opts->keep_cap_fowner);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("dumpable"))
OPT_SERV_X_TOGGLE(opts->make_dumpable);
else if (OPT_SERV_SYM_EQ("listen"))
{
Opt_serv_addr_opts *addr = opt_serv_make_addr(opts);
CONF_SC_MAKE_CLIST_BEG(listen, clist);
else if (!addr) return (FALSE);
else if (OPT_SERV_SYM_EQ("defer-accept"))
OPT_SERV_X_UINT(addr->defer_accept);
else if (OPT_SERV_SYM_EQ("port"))
OPT_SERV_X_UINT(addr->tcp_port);
else if (OPT_SERV_SYM_EQ("addr") || OPT_SERV_SYM_EQ("address"))
OPT_SERV_X_VSTR(opts, addr->acpt_address);
else if (OPT_SERV_SYM_EQ("cong") || OPT_SERV_SYM_EQ("congestion"))
OPT_SERV_X_VSTR(opts, addr->acpt_cong);
else if (OPT_SERV_SYM_EQ("policy"))
OPT_SERV_X_VSTR(opts, addr->def_policy);
else if (OPT_SERV_SYM_EQ("queue-length"))
{
OPT_SERV_X_SYM_UINT_BEG(addr->q_listen_len);
else if (OPT_SERV_SYM_EQ("<max>")) addr->q_listen_len = SOMAXCONN;
OPT_SERV_X_SYM_NUM_END();
}
else if (OPT_SERV_SYM_EQ("filter"))
{
if (!opt_serv_sc_make_static_path(opts, conf, token,
addr->acpt_filter_file))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("max-connections"))
OPT_SERV_X_UINT(addr->max_connections);
CONF_SC_MAKE_CLIST_END();
if (!opt_serv_merge_addrs(opts))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("parent-death-signal"))
OPT_SERV_X_TOGGLE(opts->use_pdeathsig);
else if (OPT_SERV_SYM_EQ("pid-file"))
return (opt_serv_sc_make_static_path(opts, conf, token, opts->pid_file));
else if (OPT_SERV_SYM_EQ("processes") ||
OPT_SERV_SYM_EQ("procs"))
{
OPT_SERV_X_SYM_UINT_BEG(opts->num_procs);
else if (OPT_SERV_SYM_EQ("<sysconf-number-processors-configured>") ||
OPT_SERV_SYM_EQ("<sysconf-num-procs-configured>") ||
OPT_SERV_SYM_EQ("<sysconf-num-procs-conf>"))
opts->num_procs = sysconf(_SC_NPROCESSORS_CONF);
else if (OPT_SERV_SYM_EQ("<sysconf-number-processors-online>") ||
OPT_SERV_SYM_EQ("<sysconf-num-procs-online>") ||
OPT_SERV_SYM_EQ("<sysconf-num-procs-onln>"))
opts->num_procs = sysconf(_SC_NPROCESSORS_ONLN);
OPT_SERV_X_SYM_NUM_END();
}
else if (OPT_SERV_SYM_EQ("logging"))
{
CONF_SC_MAKE_CLIST_BEG(logging_main, clist);
else if (OPT_SERV_SYM_EQ("syslog"))
{
CONF_SC_MAKE_CLIST_BEG(logging_syslog, clist);
else if (OPT_SERV_SYM_EQ("native"))
OPT_SERV_X_TOGGLE(opts->vlg_syslog_native);
else if (OPT_SERV_SYM_EQ("limit"))
{
CONF_SC_MAKE_CLIST_BEG(logging_limit, clist);
else if (OPT_SERV_SYM_EQ("size"))
{
opts->vlg_tweaked_size = TRUE;
OPT_SERV_X_ULONG(opts->vlg_size);
}
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("facility"))
{
OPT_SERV_X_SINGLE_VSTR(conf->tmp);
if (0) { }
else if (OPT_SERV__TMP_EQ("AUTHPRIV") ||
OPT_SERV__TMP_EQ("AUTH"))
opts->syslog_facility = LOG_AUTHPRIV;
else if (OPT_SERV__TMP_EQ("CRON")) opts->syslog_facility = LOG_CRON;
else if (OPT_SERV__TMP_EQ("DAEMON")) opts->syslog_facility = LOG_DAEMON;
else if (OPT_SERV__TMP_EQ("FTP")) opts->syslog_facility = LOG_FTP;
else if (OPT_SERV__TMP_EQ("LOCAL"))
{
unsigned int num = 0;
OPT_SERV_X_SINGLE_UINT(num);
switch (num)
{
default: return (FALSE);
case 0: opts->syslog_facility = LOG_LOCAL0;
case 1: opts->syslog_facility = LOG_LOCAL1;
case 2: opts->syslog_facility = LOG_LOCAL2;
case 3: opts->syslog_facility = LOG_LOCAL3;
case 4: opts->syslog_facility = LOG_LOCAL4;
case 5: opts->syslog_facility = LOG_LOCAL5;
case 6: opts->syslog_facility = LOG_LOCAL6;
case 7: opts->syslog_facility = LOG_LOCAL7;
}
}
else if (OPT_SERV__TMP_EQ("LOCAL0")) opts->syslog_facility = LOG_LOCAL0;
else if (OPT_SERV__TMP_EQ("LOCAL1")) opts->syslog_facility = LOG_LOCAL1;
else if (OPT_SERV__TMP_EQ("LOCAL2")) opts->syslog_facility = LOG_LOCAL2;
else if (OPT_SERV__TMP_EQ("LOCAL3")) opts->syslog_facility = LOG_LOCAL3;
else if (OPT_SERV__TMP_EQ("LOCAL4")) opts->syslog_facility = LOG_LOCAL4;
else if (OPT_SERV__TMP_EQ("LOCAL5")) opts->syslog_facility = LOG_LOCAL5;
else if (OPT_SERV__TMP_EQ("LOCAL6")) opts->syslog_facility = LOG_LOCAL6;
else if (OPT_SERV__TMP_EQ("LOCAL7")) opts->syslog_facility = LOG_LOCAL7;
else if (OPT_SERV__TMP_EQ("LPR")) opts->syslog_facility = LOG_LPR;
else if (OPT_SERV__TMP_EQ("MAIL")) opts->syslog_facility = LOG_MAIL;
else if (OPT_SERV__TMP_EQ("NEWS")) opts->syslog_facility = LOG_NEWS;
else if (OPT_SERV__TMP_EQ("USER")) opts->syslog_facility = LOG_USER;
else if (OPT_SERV__TMP_EQ("UUCP")) opts->syslog_facility = LOG_UUCP;
else
return (FALSE);
}
CONF_SC_MAKE_CLIST_END();
}
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("resource-limits") ||
OPT_SERV_SYM_EQ("rlimit"))
{
CONF_SC_MAKE_CLIST_BEG(rlimit, clist);
else if (OPT_SERV_SYM_EQ("AS") || OPT_SERV_SYM_EQ("address-space"))
OPT_SERV__RLIM_VAL(opts->rlim_as_num, opts->rlim_as_call);
else if (OPT_SERV_SYM_EQ("CORE") || OPT_SERV_SYM_EQ("core"))
OPT_SERV__RLIM_VAL(opts->rlim_core_num, opts->rlim_core_call);
else if (OPT_SERV_SYM_EQ("NOFILE") || OPT_SERV_SYM_EQ("fd-num") ||
OPT_SERV_SYM_EQ("file-descriptor-number"))
OPT_SERV__RLIM_VAL(opts->rlim_file_num, opts->rlim_file_call);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("cache-limits"))
{
CONF_SC_MAKE_CLIST_BEG(cache_limits, clist);
else if (OPT_SERV_SYM_EQ("spare-vstr-bases"))
OPT_SERV_X_UINT(opts->max_spare_bases);
else if (OPT_SERV_SYM_EQ("spare-vstr-nodes-buf"))
OPT_SERV_X_UINT(opts->max_spare_buf_nodes);
else if (OPT_SERV_SYM_EQ("spare-vstr-nodes-ptr"))
OPT_SERV_X_UINT(opts->max_spare_ptr_nodes);
else if (OPT_SERV_SYM_EQ("spare-vstr-nodes-ref"))
OPT_SERV_X_UINT(opts->max_spare_ref_nodes);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("limit"))
{
CONF_SC_MAKE_CLIST_BEG(limit, clist);
else if (OPT_SERV_SYM_EQ("io"))
{
unsigned int clist_ioo = FALSE;
unsigned int clist_ioi = FALSE;
CONF_SC_MAKE_CLIST_BEG(limit_ioo, clist_ioo);
else if (OPT_SERV_SYM_EQ("process/s") ||
OPT_SERV_SYM_EQ("process-per-second"))
{
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(opts->io_limit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(opts->io_limit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("policy-process/s") ||
OPT_SERV_SYM_EQ("policy-process-per-second"))
{
Opt_serv_policy_opts *popts = NULL;
if (!(popts = opt_policy_conf_find(opts, conf, token)))
return (FALSE);
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(popts->io_limit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(popts->io_limit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("policy-connection/s") ||
OPT_SERV_SYM_EQ("policy-connection-per-second"))
{
Opt_serv_policy_opts *popts = NULL;
if (!(popts = opt_policy_conf_find(opts, conf, token)))
return (FALSE);
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(popts->io_nslimit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(popts->io_nslimit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
else if (OPT_SERV_SYM_EQ("connection/s") ||
OPT_SERV_SYM_EQ("connection-per-second"))
{
CONF_SC_MAKE_CLIST_BEG(limit_ioi, clist_ioi);
else if (OPT_SERV_SYM_EQ("read") || OPT_SERV_SYM_EQ("recv"))
OPT_SERV__IOLIM_VAL(opts->io_nslimit.io_r_max);
else if (OPT_SERV_SYM_EQ("write") || OPT_SERV_SYM_EQ("send"))
OPT_SERV__IOLIM_VAL(opts->io_nslimit.io_w_max);
CONF_SC_MAKE_CLIST_END();
}
CONF_SC_MAKE_CLIST_END();
}
CONF_SC_MAKE_CLIST_END();
}
else
return (FALSE);
return (TRUE);
}
#undef OPT_SERV__RLIM_VAL
int opt_serv_conf(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token)
{
unsigned int cur_depth = token->depth_num;
int clist = FALSE;
ASSERT(opts && conf && token);
if (!conf_token_cmp_sym_cstr_eq(conf, token, "org.and.daemon-conf-1.0"))
return (FALSE);
CONF_SC_MAKE_CLIST_MID(cur_depth, clist);
else if (opt_serv__conf_d1(opts, conf, token, clist))
{ }
CONF_SC_MAKE_CLIST_END();
if (conf->data->conf->malloc_bad)
return (FALSE);
return (TRUE);
}
int opt_serv_conf_parse_cstr(Vstr_base *out,
Opt_serv_opts *opts, const char *data)
{
Conf_parse *conf = conf_parse_make(NULL);
Conf_token token[1] = {CONF_TOKEN_INIT};
ASSERT(opts && data);
if (!conf)
goto conf_malloc_fail;
if (!vstr_add_cstr_ptr(conf->data, conf->data->len,
"(org.and.daemon-conf-1.0 "))
goto read_malloc_fail;
if (!vstr_add_cstr_ptr(conf->data, conf->data->len, data))
goto read_malloc_fail;
if (!vstr_add_cstr_ptr(conf->data, conf->data->len,
")"))
goto read_malloc_fail;
if (!conf_parse_lex(conf, 1, conf->data->len))
goto conf_fail;
if (!conf_parse_token(conf, token))
goto conf_fail;
if ((token->type != CONF_TOKEN_TYPE_CLIST) || (token->depth_num != 1))
goto conf_fail;
if (!conf_parse_token(conf, token))
goto conf_fail;
ASSERT(conf_token_cmp_sym_cstr_eq(conf, token, "org.and.daemon-conf-1.0"));
if (!opt_serv_conf(opts, conf, token))
goto conf_fail;
if (token->num != conf->sects->num)
goto conf_fail;
conf_parse_free(conf);
return (TRUE);
conf_fail:
conf_parse_backtrace(out, data, conf, token);
read_malloc_fail:
conf_parse_free(conf);
conf_malloc_fail:
return (FALSE);
}
int opt_serv_conf_parse_file(Vstr_base *out,
Opt_serv_opts *opts, const char *fname)
{
Conf_parse *conf = conf_parse_make(NULL);
Conf_token token[1] = {CONF_TOKEN_INIT};
ASSERT(opts && fname);
if (!conf)
goto conf_malloc_fail;
if (!vstr_sc_read_len_file(conf->data, 0, fname, 0, 0, NULL))
goto read_malloc_fail;
if (!conf_parse_lex(conf, 1, conf->data->len))
goto conf_fail;
while (conf_parse_token(conf, token))
{
if ((token->type != CONF_TOKEN_TYPE_CLIST) || (token->depth_num != 1))
goto conf_fail;
if (!conf_parse_token(conf, token))
goto conf_fail;
if (!conf_token_cmp_sym_cstr_eq(conf, token, "org.and.daemon-conf-1.0"))
goto conf_fail;
if (!opt_serv_conf(opts, conf, token))
goto conf_fail;
}
conf_parse_free(conf);
return (TRUE);
conf_fail:
conf_parse_backtrace(out, fname, conf, token);
errno = 0;
read_malloc_fail:
if (errno && out)
vstr_add_fmt(out, out->len, "open(%s): %m", fname);
conf_parse_free(conf);
conf_malloc_fail:
return (FALSE);
}
void opt_serv_conf_free_beg(struct Opt_serv_opts *opts)
{
Opt_serv_addr_opts *scan = NULL;
if (!opts)
return;
vstr_free_base(opts->pid_file); opts->pid_file = NULL;
vstr_free_base(opts->cntl_file); opts->cntl_file = NULL;
vstr_free_base(opts->chroot_dir); opts->chroot_dir = NULL;
vstr_free_base(opts->vpriv_uid); opts->vpriv_uid = NULL;
vstr_free_base(opts->vpriv_gid); opts->vpriv_gid = NULL;
scan = opts->addr_beg;
opts->addr_beg = NULL;
while (scan)
{
Opt_serv_addr_opts *scan_next = scan->next;
opt_serv__free_addr(scan);
scan = scan_next;
}
}
void opt_serv_conf_free_end(struct Opt_serv_opts *opts)
{
if (!opts)
return;
ASSERT( opts->ref_io_limit);
vstr_ref_del(opts->ref_io_limit);
ASSERT(!opts->ref_io_limit);
}
static void opt_serv__io_lim_ref_cb(struct Vstr_ref *ref)
{
Opt_serv_opts *opts = NULL;
struct Evnt_limit *lim = NULL;
if (!ref)
return;
lim = ref->ptr;
ASSERT(lim);
opts = (Opt_serv_opts *)(((char *)lim) - offsetof(Opt_serv_opts, io_limit));
opts->ref_io_limit = NULL;
}
int opt_serv_conf_init(Opt_serv_opts *opts)
{
struct Opt_serv_policy_opts *popts = NULL;
Opt_serv_addr_opts *addr = MK(sizeof(Opt_serv_addr_opts));
ASSERT(opts && opts->make_policy && opts->copy_policy);
if (!addr)
goto mk_addr_fail;
if (!opt_serv__init_append_hostname())
goto mk_append_hostname_fail;
if (!(popts = (*opts->make_policy)(opts)))
goto mk_policy_fail;
opts->def_policy = popts;
vstr_add_cstr_ptr(popts->policy_name, 0, OPT_POLICY_CONF_DEF_POLICY_NAME);
if (popts->policy_name->conf->malloc_bad)
goto policy_init_fail;
opts->pid_file = vstr_make_base(NULL);
opts->cntl_file = vstr_make_base(NULL);
opts->chroot_dir = vstr_make_base(NULL);
opts->vpriv_uid = vstr_make_base(NULL);
opts->vpriv_gid = vstr_make_base(NULL);
addr->acpt_filter_file = vstr_make_base(NULL);
addr->acpt_address = vstr_make_base(NULL);
addr->acpt_cong = vstr_make_base(NULL);
addr->def_policy = vstr_make_base(NULL);
opts->ref_io_limit = vstr_ref_make_ptr(&opts->io_limit,
opt_serv__io_lim_ref_cb);
if (!opts->pid_file ||
!opts->cntl_file ||
!opts->chroot_dir ||
!opts->vpriv_uid ||
!opts->vpriv_gid ||
!addr->acpt_filter_file ||
!addr->acpt_address ||
!addr->acpt_cong ||
!addr->def_policy ||
!opts->ref_io_limit ||
FALSE)
goto opts_init_fail;
addr->next = NULL;
addr->tcp_port = 0;
addr->defer_accept = OPT_SERV_CONF_DEF_TCP_DEFER_ACCEPT;
addr->q_listen_len = OPT_SERV_CONF_DEF_Q_LISTEN_LEN;
addr->max_connections = OPT_SERV_CONF_DEF_MAX_CONNECTIONS;
opts->addr_beg = addr;
opts->no_conf_listen = TRUE;
if (!opts->ref_io_limit)
goto opts_init_fail;
return (TRUE);
opts_init_fail:
opt_serv_conf_free_beg(opts);
opt_serv_conf_free_end(opts);
policy_init_fail:
vstr_ref_del(popts->ref);
mk_policy_fail:
mk_append_hostname_fail:
F(addr);
mk_addr_fail:
return (FALSE);
}
void opt_serv_logger(Vlg *passed_vlg)
{
vlg = passed_vlg;
}
void opt_serv_sc_drop_privs(Opt_serv_opts *opts)
{
if (setgroups(1, &opts->priv_gid) == -1)
vlg_err(vlg, EXIT_FAILURE, "setgroups(%ld): %m\n", (long)opts->priv_gid);
if (setgid(opts->priv_gid) == -1)
vlg_err(vlg, EXIT_FAILURE, "setgid(%ld): %m\n", (long)opts->priv_gid);
if (setuid(opts->priv_uid) == -1)
vlg_err(vlg, EXIT_FAILURE, "setuid(%ld): %m\n", (long)opts->priv_uid);
}
static void opt_serv__sc_rlim_num(const char *name, int resource,
unsigned int num)
{
struct rlimit rlim[1];
if (getrlimit(resource, rlim) == -1)
vlg_err(vlg, EXIT_FAILURE, "getrlimit(RLIMIT_%s): %m\n", name);
if (num == RLIM_INFINITY)
{
if ((rlim->rlim_max != RLIM_INFINITY) && !getuid())
rlim->rlim_max = num;
}
else
{
if ((num > rlim->rlim_max) && !getuid())
rlim->rlim_max = num;
if (num < rlim->rlim_max)
rlim->rlim_max = num;
}
rlim->rlim_cur = rlim->rlim_max;
if (setrlimit(resource, rlim) == -1)
vlg_err(vlg, EXIT_FAILURE, "setrlimit(RLIMIT_%s): %m\n", name);
}
void opt_serv_sc_rlim_as_num(unsigned int rlim_as_num)
{
if (USE_RLIMIT_AS)
opt_serv__sc_rlim_num("AS", RLIMIT_AS, rlim_as_num);
}
void opt_serv_sc_rlim_core_num(unsigned int rlim_core_num)
{
opt_serv__sc_rlim_num("CORE", RLIMIT_CORE, rlim_core_num);
}
void opt_serv_sc_rlim_file_num(unsigned int rlim_file_num)
{
opt_serv__sc_rlim_num("NOFILE", RLIMIT_NOFILE, rlim_file_num);
}
int opt_serv_sc_acpt_end(const Opt_serv_policy_opts *popts,
struct Evnt *from_evnt, struct Evnt *evnt)
{
Acpt_listener *acpt_listener = (Acpt_listener *)from_evnt;
unsigned int acpt_num = acpt_listener->ref->ref - 1;
unsigned int acpt_max = acpt_listener->max_connections;
vlg_dbg1(vlg, "acpt: %u/%u\n", acpt_num, acpt_max);
if (acpt_max && (acpt_num >= acpt_max))
{
vlg_dbg1(vlg, "ACPT DEL ($<sa:%p>,%u,%u)\n", EVNT_SA(from_evnt),
acpt_num, acpt_max);
evnt_wait_cntl_del(from_evnt, POLLIN);
}
if (popts->max_connections && (evnt_num_all() > popts->max_connections))
{
vlg_info(vlg, "LIMIT-BLOCKED from[$<sa:%p>]: policy $<vstr.all:%p>\n",
EVNT_SA(evnt), popts->policy_name);
return (FALSE);
}
vlg_info(vlg, "CONNECT from[$<sa:%p>]\n", EVNT_SA(evnt));
return (TRUE);
}
void opt_serv_sc_free_beg(struct Evnt *evnt, const char *info_rep)
{
if (EVNT_ACPT_EXISTS(evnt) && EVNT_ACPT_DATA(evnt)->evnt)
{
Acpt_data *acpt_data = EVNT_ACPT_DATA(evnt);
Acpt_listener *acpt_listener = (Acpt_listener *)acpt_data->evnt;
unsigned int acpt_num = acpt_listener->ref->ref - 1;
unsigned int acpt_max = acpt_listener->max_connections;
if (acpt_max && (acpt_num <= acpt_max))
{
vlg_dbg1(vlg, "ACPT ADD ($<sa:%p>,%u,%u)\n", EVNT_SA(acpt_data->evnt),
acpt_num, acpt_max);
evnt_wait_cntl_add(acpt_data->evnt, POLLIN);
}
evnt_stats_add(acpt_data->evnt, evnt);
}
if (evnt->flag_fully_acpt)
evnt_vlg_stats_info(evnt, info_rep);
}
#define OPT_SERV__SIG_OR_ERR(x) \
if (sigaction(x, &sa, NULL) == -1) \
err(EXIT_FAILURE, "signal(%d:%s)", x, strsignal(x))
static void opt_serv__sig_crash(int s_ig_num)
{
vlg_sig_abort(vlg, "SIG[%d]: %s\n", s_ig_num, strsignal(s_ig_num));
}
static void opt_serv__sig_raise_cont(int s_ig_num)
{
struct sigaction sa;
if (sigemptyset(&sa.sa_mask) == -1)
err(EXIT_FAILURE, "signal init");
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
OPT_SERV__SIG_OR_ERR(s_ig_num);
vlg_sig_warn(vlg, "SIG[%d]: %s\n", s_ig_num, strsignal(s_ig_num));
raise(s_ig_num);
}
static void opt_serv__sig_cont(int s_ig_num)
{
if (0)
{
struct sigaction sa;
if (sigemptyset(&sa.sa_mask) == -1)
err(EXIT_FAILURE, "signal init");
sa.sa_flags = SA_RESTART;
sa.sa_handler = opt_serv__sig_raise_cont;
OPT_SERV__SIG_OR_ERR(SIGTSTP);
}
vlg_sig_info(vlg, "SIG[%d]: %s\n", s_ig_num, strsignal(s_ig_num));
}
static void opt_serv__sig_child(int s_ig_num)
{
ASSERT(s_ig_num == SIGCHLD);
evnt_child_exited = TRUE;
}
void opt_serv_sc_signals(void)
{
struct sigaction sa;
if (sigemptyset(&sa.sa_mask) == -1)
err(EXIT_FAILURE, "signal init %s", "sigemptyset");
sa.sa_flags = 0;
sa.sa_handler = SIG_IGN;
OPT_SERV__SIG_OR_ERR(SIGPIPE);
sa.sa_handler = opt_serv__sig_crash;
OPT_SERV__SIG_OR_ERR(SIGSEGV);
OPT_SERV__SIG_OR_ERR(SIGBUS);
OPT_SERV__SIG_OR_ERR(SIGILL);
OPT_SERV__SIG_OR_ERR(SIGFPE);
OPT_SERV__SIG_OR_ERR(SIGXFSZ);
sa.sa_flags = SA_RESTART;
sa.sa_handler = opt_serv__sig_child;
OPT_SERV__SIG_OR_ERR(SIGCHLD);
sa.sa_flags = SA_RESTART;
sa.sa_handler = opt_serv__sig_cont;
OPT_SERV__SIG_OR_ERR(SIGUSR1);
OPT_SERV__SIG_OR_ERR(SIGUSR2);
OPT_SERV__SIG_OR_ERR(SIGHUP);
OPT_SERV__SIG_OR_ERR(SIGCONT);
sa.sa_handler = opt_serv__sig_raise_cont;
OPT_SERV__SIG_OR_ERR(SIGTSTP);
OPT_SERV__SIG_OR_ERR(SIGTERM);
}
#undef SERV__SIG_OR_ERR
void opt_serv_sc_check_children(void)
{
if (evnt_child_exited)
{
vlg_warn(vlg, "Child exited.\n");
evnt_acpt_close_all();
evnt_scan_q_close();
evnt_child_exited = FALSE;
}
}
void opt_serv_sc_cntl_resources(const Opt_serv_opts *opts)
{
vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_RANGE_SPARE_BASE,
0, opts->max_spare_bases);
vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_RANGE_SPARE_BUF,
0, opts->max_spare_buf_nodes);
vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_RANGE_SPARE_PTR,
0, opts->max_spare_ptr_nodes);
vstr_cntl_conf(NULL, VSTR_CNTL_CONF_SET_NUM_RANGE_SPARE_REF,
0, opts->max_spare_ref_nodes);
}
int opt_serv_build_single_str(struct Opt_serv_opts *opts,
const Conf_parse *conf, Conf_token *token,
int clist, int doing_init)
{
if (0) { }
else if (OPT_SERV_SYM_EQ("ENV") || OPT_SERV_SYM_EQ("ENVIRONMENT") ||
OPT_SERV_SYM_EQ("<ENV>") || OPT_SERV_SYM_EQ("<ENVIRONMENT>"))
{
if (!opt_serv_sc_append_env(conf->tmp, conf->tmp->len, conf, token))
return (FALSE);
}
else if (doing_init &&
(OPT_SERV_SYM_EQ("HOME") || OPT_SERV_SYM_EQ("<HOME>")))
{
if (!OPT_SERV_SC_APPEND_HOMEDIR(conf->tmp, conf->tmp->len, conf, token))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("<hostname>"))
opt_serv_sc_append_hostname(conf->tmp, conf->tmp->len);
else if (OPT_SERV_SYM_EQ("<cwd>"))
opt_serv_sc_append_cwd(conf->tmp, conf->tmp->len);
else if (OPT_SERV_SYM_EQ("<uname>"))
{
static struct utsname utsname[1];
static int done = FALSE;
if (!done && (uname(utsname) == -1)) return (FALSE);
done = TRUE;
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
CONF_SC_TOGGLE_CLIST_VAR(clist);
if (0) { }
else if (OPT_SERV_SYM_EQ("sysname")) OPT_SERV_SC_APPEND_UNAME(sysname);
else if (OPT_SERV_SYM_EQ("nodename")) OPT_SERV_SC_APPEND_UNAME(nodename);
else if (OPT_SERV_SYM_EQ("release")) OPT_SERV_SC_APPEND_UNAME(release);
else if (OPT_SERV_SYM_EQ("version")) OPT_SERV_SC_APPEND_UNAME(version);
else if (OPT_SERV_SYM_EQ("machine")) OPT_SERV_SC_APPEND_UNAME(machine);
else
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("<version>") || OPT_SERV_SYM_EQ("<vers>"))
{
if (!vstr_add_ptr(conf->tmp, conf->tmp->len,
opts->vers_cstr, opts->vers_len))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("<program-name>"))
{
if (!vstr_add_ptr(conf->tmp, conf->tmp->len,
opts->name_cstr, opts->name_len))
return (FALSE);
}
else if (!clist)
{
size_t pos = conf->tmp->len + 1;
const Vstr_sect_node *pv = conf_token_value(token);
if (!pv || !vstr_add_vstr(conf->tmp, conf->tmp->len,
conf->data, pv->pos, pv->len,
VSTR_TYPE_ADD_BUF_REF))
return (FALSE);
OPT_SERV_X__ESC_VSTR(conf->tmp, pos, pv->len);
}
else
return (FALSE);
return (TRUE);
}
static int opt_serv__build_str(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
int doing_init)
{
unsigned int cur_depth = token->depth_num;
int clist = FALSE;
vstr_del(conf->tmp, 1, conf->tmp->len);
while (conf_token_list_num(token, cur_depth))
{
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
CONF_SC_TOGGLE_CLIST_VAR(clist);
if (!opt_serv_build_single_str(opts, conf, token, clist, doing_init))
return (FALSE);
}
return (!conf->tmp->conf->malloc_bad);
}
static int opt_serv__sc_make_str(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
Vstr_base *s1, size_t pos, size_t len,
int doing_init)
{
int clist = FALSE;
ASSERT(s1 != conf->tmp);
CONF_SC_PARSE_TOP_TOKEN_RET(conf, token, FALSE);
CONF_SC_TOGGLE_CLIST_VAR(clist);
if (0) { }
else if (OPT_SERV_SYM_EQ("<none>") || OPT_SERV_SYM_EQ("<empty>"))
{
return (vstr_del(s1, pos, len));
}
else if (OPT_SERV_SYM_EQ("assign") || OPT_SERV_SYM_EQ("="))
{
if (!opt_serv__build_str(opts, conf, token, doing_init))
return (FALSE);
return (vstr_sub_vstr(s1, pos, len,
conf->tmp, 1, conf->tmp->len, VSTR_TYPE_SUB_BUF_REF));
}
else if (OPT_SERV_SYM_EQ("append") || OPT_SERV_SYM_EQ("+=") ||
OPT_SERV_SYM_EQ(".=") || OPT_SERV_SYM_EQ(">>="))
{
if (!opt_serv__build_str(opts, conf, token, doing_init))
return (FALSE);
return (vstr_add_vstr(s1, vstr_sc_poslast(pos, len),
conf->tmp, 1, conf->tmp->len, VSTR_TYPE_ADD_BUF_REF));
}
else if (OPT_SERV_SYM_EQ("prepend") || OPT_SERV_SYM_EQ("<<="))
{
if (!opt_serv__build_str(opts, conf, token, doing_init))
return (FALSE);
return (vstr_add_vstr(s1, pos - 1,
conf->tmp, 1, conf->tmp->len, VSTR_TYPE_ADD_BUF_REF));
}
else if (!clist)
{
const Vstr_sect_node *pv = conf_token_value(token);
if (!pv || !vstr_sub_vstr(s1, pos, len, conf->data, pv->pos, pv->len,
VSTR_TYPE_SUB_BUF_REF))
return (FALSE);
OPT_SERV_X__ESC_VSTR(s1, pos, pv->len);
return (TRUE);
}
return (FALSE);
}
int opt_serv_sc_make_str(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
Vstr_base *s1, size_t pos, size_t len)
{
return (opt_serv__sc_make_str(opts, conf, token, s1, pos, len, FALSE));
}
int opt_serv_sc_make_static_path(struct Opt_serv_opts *opts,
Conf_parse *conf, Conf_token *token,
Vstr_base *s1)
{
return (opt_serv__sc_make_str(opts, conf, token, s1, 1, s1->len, TRUE));
}
static int opt_serv__build_uintmax(Conf_parse *conf, Conf_token *token,
uintmax_t num, uintmax_t *ret)
{
ASSERT(ret);
OPT_SERV_X_SYM_SINGLE_UINTMAX_BEG(*ret);
else if (OPT_SERV_SYM_EQ("<num>")) *ret = num;
OPT_SERV_X_SYM_NUM_END();
return (TRUE);
}
int opt_serv_sc_make_uintmax(Conf_parse *conf, Conf_token *token,
uintmax_t *num)
{
OPT_SERV_X_SYM_SINGLE_UINTMAX_BEG(*num);
else if (OPT_SERV_SYM_EQ("none") || OPT_SERV_SYM_EQ("zero") ||
OPT_SERV_SYM_EQ("NONE") || OPT_SERV_SYM_EQ("ZERO") ||
OPT_SERV_SYM_EQ("<none>") || OPT_SERV_SYM_EQ("<zero>") ||
OPT_SERV_SYM_EQ("<NONE>") || OPT_SERV_SYM_EQ("<ZERO>"))
{
*num = 0;
}
else if (OPT_SERV_SYM_EQ("assign") || OPT_SERV_SYM_EQ("="))
{
if (!opt_serv__build_uintmax(conf, token, *num, num))
return (FALSE);
}
else if (OPT_SERV_SYM_EQ("add") || OPT_SERV_SYM_EQ("+="))
{
uintmax_t tmp = 0;
if (!opt_serv__build_uintmax(conf, token, *num, &tmp))
return (FALSE);
if ((tmp + *num) < tmp)
return (FALSE);
*num += tmp;
}
else if (OPT_SERV_SYM_EQ("remove") || OPT_SERV_SYM_EQ("-="))
{
uintmax_t tmp = 0;
if (!opt_serv__build_uintmax(conf, token, *num, &tmp))
return (FALSE);
if (tmp > *num)
return (FALSE);
*num -= tmp;
}
else if (OPT_SERV_SYM_EQ("multiply") || OPT_SERV_SYM_EQ("*="))
{
uintmax_t tmp = 0;
if (!opt_serv__build_uintmax(conf, token, *num, &tmp))
return (FALSE);
if ((tmp * *num) < tmp)
return (FALSE);
*num *= tmp;
}
else if (OPT_SERV_SYM_EQ("divide") || OPT_SERV_SYM_EQ("/="))
{
uintmax_t tmp = 0;
if (!opt_serv__build_uintmax(conf, token, *num, &tmp))
return (FALSE);
if (!tmp)
return (FALSE);
*num /= tmp;
}
else if (OPT_SERV_SYM_EQ("modulus") || OPT_SERV_SYM_EQ("%="))
{
uintmax_t tmp = 0;
if (!opt_serv__build_uintmax(conf, token, *num, &tmp))
return (FALSE);
if (!tmp)
return (FALSE);
*num %= tmp;
}
OPT_SERV_X_SYM_NUM_END();
return (TRUE);
}
int opt_serv_sc_make_uint(Conf_parse *conf, Conf_token *token,
unsigned int *num)
{
uintmax_t tmp = *num;
if (!opt_serv_sc_make_uintmax(conf, token, &tmp))
return (FALSE);
if (tmp > UINT_MAX)
return (FALSE);
*num = tmp;
return (TRUE);
}
int opt_serv_sc_make_ulong(Conf_parse *conf, Conf_token *token,
unsigned long *num)
{
uintmax_t tmp = *num;
if (!opt_serv_sc_make_uintmax(conf, token, &tmp))
return (FALSE);
if (tmp > ULONG_MAX)
return (FALSE);
*num = tmp;
return (TRUE);
}
#ifndef CONF_FULL_STATIC
void opt_serv_sc_resolve_uid(struct Opt_serv_opts *opts,
const char *program_name,
void (*usage)(const char *, int, const char *))
{
const char *name = NULL;
struct passwd *pw = NULL;
if (!opts->vpriv_uid->len)
return;
OPT_SC_EXPORT_CSTR(name, opts->vpriv_uid, TRUE, "privilage uid");
if (!(pw = getpwnam(name)))
{
Vstr_base *s1 = vstr_make_base(NULL);
const char *msg = NULL;
if (s1 &&
vstr_add_fmt(s1, s1->len, " Username -- %s -- can't be found.\n",
name) &&
(msg = vstr_export_cstr_ptr(s1, 1, s1->len)))
usage(program_name, EXIT_FAILURE, msg);
usage(program_name, EXIT_FAILURE, " Username can't be found.\n");
}
opts->priv_uid = pw->pw_uid;
}
void opt_serv_sc_resolve_gid(struct Opt_serv_opts *opts,
const char *program_name,
void (*usage)(const char *, int, const char *))
{
const char *name = NULL;
struct group *gr = NULL;
if (!opts->vpriv_gid->len)
return;
OPT_SC_EXPORT_CSTR(name, opts->vpriv_gid, FALSE, "privilage gid");
if (!(gr = getgrnam(name)))
{
Vstr_base *s1 = vstr_make_base(NULL);
const char *msg = NULL;
if (s1 &&
vstr_add_fmt(s1, s1->len, " Groupname -- %s -- can't be found.\n",
name) &&
(msg = vstr_export_cstr_ptr(s1, 1, s1->len)))
usage(program_name, EXIT_FAILURE, msg);
usage(program_name, EXIT_FAILURE, " Groupname can't be found.\n");
}
opts->priv_gid = gr->gr_gid;
}
#endif
int opt_serv_sc_config_dir(Vstr_base *s1, void *data, const char *dir,
int (*func)(Vstr_base *, void *, const char *))
{
struct dirent **dents = NULL;
int num = -1;
int scan = 0;
int ret = TRUE;
Vstr_base *bld = vstr_make_base(s1->conf);
if (!bld || bld->conf->malloc_bad)
{
errno = ENOMEM;
goto fail;
}
num = opt_conf_sc_scan_dir(dir, &dents);
if (num == -1)
goto fail;
while (scan < num)
{
if (ret)
{
const char *fname = NULL;
const struct dirent *dent = dents[scan];
vstr_del(bld, 1, bld->len);
vstr_add_cstr_ptr(bld, bld->len, dir);
vstr_add_cstr_ptr(bld, bld->len, "/");
vstr_add_ptr(bld, bld->len, dent->d_name, _D_EXACT_NAMLEN(dent));
if (bld->conf->malloc_bad)
{
bld->conf->malloc_bad = FALSE;
ret = FALSE;
}
if (ret && !(fname = vstr_export_cstr_ptr(bld, 1, bld->len)))
ret = FALSE;
if (ret && !(*func)(s1, data, fname))
ret = FALSE;
}
free(dents[scan]);
++scan;
}
free(dents);
vstr_free_base(bld);
return (ret);
fail:
vstr_add_fmt(s1, s1->len, "scandir(%s): %m", dir);
vstr_free_base(bld);
return (FALSE);
}