#include <sys_defs.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <argv.h>
#include <vstream.h>
#include <readlline.h>
#include <stringops.h>
#include <split_at.h>
#include <mail_params.h>
#include <master_proto.h>
#include <postconf.h>
const char pcf_daemon_options_expecting_value[] = "o";
typedef struct {
int match_count;
const char *raw_text;
ARGV *service_pattern;
int field_pattern;
const char *param_pattern;
} PCF_MASTER_FLD_REQ;
static const char *pcf_valid_master_types[] = {
MASTER_XPORT_NAME_UNIX,
MASTER_XPORT_NAME_FIFO,
MASTER_XPORT_NAME_INET,
MASTER_XPORT_NAME_PASS,
0,
};
static const char pcf_valid_bool_types[] = "yn-";
#define STR(x) vstring_str(x)
static void pcf_extract_field(ARGV *argv, int field, const char *parens)
{
char *arg = argv->argv[field];
char *err;
if ((err = extpar(&arg, parens, EXTPAR_FLAG_STRIP)) != 0) {
msg_warn("%s: %s", MASTER_CONF_FILE, err);
myfree(err);
}
argv_replace_one(argv, field, arg);
}
static void pcf_normalize_nameval(ARGV *argv, int field)
{
char *arg = argv->argv[field];
char *name;
char *value;
const char *err;
char *normalized;
if ((err = split_nameval(arg, &name, &value)) != 0) {
msg_warn("%s: %s: \"%s\"", MASTER_CONF_FILE, err, arg);
} else {
normalized = concatenate(name, "=", value, (char *) 0);
argv_replace_one(argv, field, normalized);
myfree(normalized);
}
}
static void pcf_normalize_daemon_args(ARGV *argv)
{
int field;
char *arg;
char *cp;
char *junk;
int extract_field;
for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
arg = argv->argv[field];
if (arg[0] != '-' || strcmp(arg, "--") == 0)
break;
for (cp = arg + 1; *cp; cp++) {
if (strchr(pcf_daemon_options_expecting_value, *cp) != 0
&& cp > arg + 1) {
junk = concatenate("-", cp, (char *) 0);
argv_insert_one(argv, field + 1, junk);
myfree(junk);
*cp = 0;
break;
}
}
if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0)
continue;
if (arg[2] != 0) {
argv_insert_one(argv, field + 1, arg + 2);
arg[2] = 0;
field += 1;
extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
} else if (argv->argv[field + 1] != 0) {
field += 1;
extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
} else
extract_field = 0;
if (extract_field) {
pcf_extract_field(argv, field, CHARS_BRACE);
if (argv->argv[field - 1][1] == 'o')
pcf_normalize_nameval(argv, field);
}
}
for ( ; argv->argv[field] != 0; field++)
if (argv->argv[field][0] == CHARS_BRACE[0])
pcf_extract_field(argv, field, CHARS_BRACE);
}
static NORETURN PRINTFLIKE(1, 2) pcf_fix_fatal(const char *fmt,...)
{
VSTRING *buf = vstring_alloc(100);
va_list ap;
va_start(ap, fmt);
vstring_vsprintf(buf, fmt, ap);
va_end(ap);
translit(STR(buf), "\n", " ");
msg_fatal("%s", STR(buf));
}
static void pcf_check_master_entry(ARGV *argv, const char *raw_text)
{
const char **cpp;
char *cp;
int len;
int field;
cp = argv->argv[PCF_MASTER_FLD_TYPE];
for (cpp = pcf_valid_master_types; ; cpp++) {
if (*cpp == 0)
pcf_fix_fatal("invalid " PCF_MASTER_NAME_TYPE " field \"%s\" in \"%s\"",
cp, raw_text);
if (strcmp(*cpp, cp) == 0)
break;
}
for (field = PCF_MASTER_FLD_PRIVATE; field <= PCF_MASTER_FLD_CHROOT; field++) {
cp = argv->argv[field];
if (cp[1] != 0 || strchr(pcf_valid_bool_types, *cp) == 0)
pcf_fix_fatal("invalid %s field \"%s\" in \"%s\"",
pcf_str_field_pattern(field), cp, raw_text);
}
cp = argv->argv[PCF_MASTER_FLD_WAKEUP];
len = strlen(cp);
if (len > 0 && cp[len - 1] == '?')
len--;
if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len)
pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \"%s\" in \"%s\"",
cp, raw_text);
cp = argv->argv[PCF_MASTER_FLD_MAXPROC];
if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0)
pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \"%s\" in \"%s\"",
cp, raw_text);
}
void pcf_free_master_entry(PCF_MASTER_ENT *masterp)
{
myfree(masterp->name_space);
argv_free(masterp->argv);
if (masterp->valid_names)
htable_free(masterp->valid_names, myfree);
if (masterp->all_params)
dict_free(masterp->all_params);
myfree((void *) masterp);
}
const char *pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf)
{
ARGV *argv;
argv = argv_splitq(buf, PCF_MASTER_BLANKS, CHARS_BRACE);
if (argv->argc < PCF_MASTER_MIN_FIELDS) {
argv_free(argv);
return ("bad field count");
}
pcf_check_master_entry(argv, buf);
pcf_normalize_daemon_args(argv);
masterp->name_space =
concatenate(argv->argv[0], PCF_NAMESP_SEP_STR, argv->argv[1], (char *) 0);
masterp->argv = argv;
masterp->valid_names = 0;
masterp->all_params = 0;
return (0);
}
void pcf_read_master(int fail_on_open_error)
{
const char *myname = "pcf_read_master";
char *path;
VSTRING *buf;
VSTREAM *fp;
const char *err;
int entry_count = 0;
int line_count;
int last_line = 0;
if (pcf_master_table != 0)
msg_panic("%s: master table is already initialized", myname);
if (var_config_dir == 0)
pcf_set_config_dir();
path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
pcf_master_table = (PCF_MASTER_ENT *) mymalloc(sizeof(*pcf_master_table));
if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
if (fail_on_open_error)
msg_fatal("open %s: %m", path);
msg_warn("open %s: %m", path);
} else {
buf = vstring_alloc(100);
while (readllines(buf, fp, &last_line, &line_count) != 0) {
pcf_master_table = (PCF_MASTER_ENT *) myrealloc((void *) pcf_master_table,
(entry_count + 2) * sizeof(*pcf_master_table));
if ((err = pcf_parse_master_entry(pcf_master_table + entry_count,
STR(buf))) != 0)
msg_fatal("file %s: line %d: %s", path, line_count, err);
entry_count += 1;
}
vstream_fclose(fp);
vstring_free(buf);
}
pcf_master_table[entry_count].argv = 0;
myfree(path);
}
void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp)
{
char **argv = masterp->argv->argv;
const char *arg;
const char *aval;
int arg_len;
int line_len;
int field;
int in_daemon_options;
int need_parens;
static int column_goal[] = {
0,
11,
17,
25,
33,
41,
49,
57,
};
#define ADD_TEXT(text, len) do { \
vstream_fputs(text, fp); line_len += len; } \
while (0)
#define ADD_SPACE ADD_TEXT(" ", 1)
for (line_len = 0, field = 0; field < PCF_MASTER_MIN_FIELDS; field++) {
arg = argv[field];
if (line_len > 0) {
do {
ADD_SPACE;
} while (line_len < column_goal[field]);
}
ADD_TEXT(arg, strlen(arg));
}
in_daemon_options = 1;
for ( ; (arg = argv[field]) != 0; field++) {
arg_len = strlen(arg);
aval = 0;
need_parens = 0;
if (in_daemon_options) {
if (arg[0] != '-' || strcmp(arg, "--") == 0) {
in_daemon_options = 0;
#if 0
if (mode & PCF_FOLD_LINE)
line_len = PCF_LINE_LIMIT;
#endif
}
else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
&& (aval = argv[field + 1]) != 0) {
line_len = PCF_LINE_LIMIT;
if (strcmp(arg, "-o") == 0
&& (mode & PCF_SHOW_EVAL) != 0)
aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
aval, masterp);
arg_len += strlen(aval) + 3;
if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
arg_len += 2;
}
} else {
need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
}
if (line_len > PCF_INDENT_LEN) {
if ((mode & PCF_FOLD_LINE) == 0
|| line_len + 1 + arg_len < PCF_LINE_LIMIT) {
ADD_SPACE;
} else {
vstream_fputs("\n" PCF_INDENT_TEXT, fp);
line_len = PCF_INDENT_LEN;
}
}
if (in_daemon_options == 0 && need_parens)
ADD_TEXT("{", 1);
ADD_TEXT(arg, strlen(arg));
if (in_daemon_options == 0 && need_parens)
ADD_TEXT("}", 1);
if (aval) {
ADD_TEXT(" ", 1);
if (need_parens)
ADD_TEXT("{", 1);
ADD_TEXT(aval, strlen(aval));
if (need_parens)
ADD_TEXT("}", 1);
field += 1;
line_len = PCF_LINE_LIMIT;
}
}
vstream_fputs("\n", fp);
if (msg_verbose)
vstream_fflush(fp);
}
void pcf_show_master_entries(VSTREAM *fp, int mode, int argc, char **argv)
{
PCF_MASTER_ENT *masterp;
PCF_MASTER_FLD_REQ *field_reqs;
PCF_MASTER_FLD_REQ *req;
if (argc > 0) {
field_reqs = (PCF_MASTER_FLD_REQ *)
mymalloc(sizeof(*field_reqs) * argc);
for (req = field_reqs; req < field_reqs + argc; req++) {
req->match_count = 0;
req->raw_text = *argv++;
req->service_pattern =
pcf_parse_service_pattern(req->raw_text, 1, 2);
if (req->service_pattern == 0)
msg_fatal("-M option requires service_name[/type]");
}
}
for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
masterp->argv->argv[0],
masterp->argv->argv[1])) {
req->match_count++;
pcf_print_master_entry(fp, mode, masterp);
}
}
} else {
pcf_print_master_entry(fp, mode, masterp);
}
}
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (req->match_count == 0)
msg_warn("unmatched request: \"%s\"", req->raw_text);
argv_free(req->service_pattern);
}
myfree((void *) field_reqs);
}
}
static void pcf_print_master_field(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp,
int field)
{
char **argv = masterp->argv->argv;
const char *arg;
const char *aval;
int arg_len;
int line_len;
int in_daemon_options;
int need_parens;
#define ADD_CHAR(ch) ADD_TEXT((ch), 1)
line_len = 0;
if ((mode & PCF_HIDE_NAME) == 0) {
ADD_TEXT(argv[0], strlen(argv[0]));
ADD_CHAR(PCF_NAMESP_SEP_STR);
ADD_TEXT(argv[1], strlen(argv[1]));
ADD_CHAR(PCF_NAMESP_SEP_STR);
ADD_TEXT(pcf_str_field_pattern(field), strlen(pcf_str_field_pattern(field)));
}
if ((mode & (PCF_HIDE_NAME | PCF_HIDE_VALUE)) == 0) {
ADD_TEXT(" = ", 3);
}
if ((mode & PCF_HIDE_VALUE) == 0) {
if (line_len > 0 && line_len + strlen(argv[field]) > PCF_LINE_LIMIT) {
vstream_fputs("\n" PCF_INDENT_TEXT, fp);
line_len = PCF_INDENT_LEN;
}
ADD_TEXT(argv[field], strlen(argv[field]));
}
if (field == PCF_MASTER_FLD_CMD && (mode & PCF_HIDE_VALUE) == 0) {
in_daemon_options = 1;
for (field += 1; (arg = argv[field]) != 0; field++) {
arg_len = strlen(arg);
aval = 0;
need_parens = 0;
if (in_daemon_options) {
if (arg[0] != '-' || strcmp(arg, "--") == 0) {
in_daemon_options = 0;
} else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
&& (aval = argv[field + 1]) != 0) {
line_len = PCF_LINE_LIMIT;
if (strcmp(arg, "-o") == 0
&& (mode & PCF_SHOW_EVAL) != 0)
aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
aval, masterp);
arg_len += strlen(aval) + 1;
if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
arg_len += 2;
}
} else {
need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
}
if (line_len > PCF_INDENT_LEN) {
if ((mode & PCF_FOLD_LINE) == 0
|| line_len + 1 + arg_len < PCF_LINE_LIMIT) {
ADD_SPACE;
} else {
vstream_fputs("\n" PCF_INDENT_TEXT, fp);
line_len = PCF_INDENT_LEN;
}
}
if (in_daemon_options == 0 && need_parens)
ADD_TEXT("{", 1);
ADD_TEXT(arg, strlen(arg));
if (in_daemon_options == 0 && need_parens)
ADD_TEXT("}", 1);
if (aval) {
ADD_SPACE;
if (need_parens)
ADD_TEXT("{", 1);
ADD_TEXT(aval, strlen(aval));
if (need_parens)
ADD_TEXT("}", 1);
field += 1;
line_len = PCF_LINE_LIMIT;
}
}
}
vstream_fputs("\n", fp);
if (msg_verbose)
vstream_fflush(fp);
}
void pcf_show_master_fields(VSTREAM *fp, int mode, int argc, char **argv)
{
const char *myname = "pcf_show_master_fields";
PCF_MASTER_ENT *masterp;
PCF_MASTER_FLD_REQ *field_reqs;
PCF_MASTER_FLD_REQ *req;
int field;
if (argc > 0) {
field_reqs = (PCF_MASTER_FLD_REQ *)
mymalloc(sizeof(*field_reqs) * argc);
for (req = field_reqs; req < field_reqs + argc; req++) {
req->match_count = 0;
req->raw_text = *argv++;
req->service_pattern =
pcf_parse_service_pattern(req->raw_text, 1, 3);
if (req->service_pattern == 0)
msg_fatal("-F option requires service_name[/type[/field]]");
field = req->field_pattern =
pcf_parse_field_pattern(req->service_pattern->argv[2]);
if (pcf_is_magic_field_pattern(field) == 0
&& (field < 0 || field > PCF_MASTER_FLD_CMD))
msg_panic("%s: bad attribute field index: %d",
myname, field);
}
}
for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
masterp->argv->argv[0],
masterp->argv->argv[1])) {
req->match_count++;
field = req->field_pattern;
if (pcf_is_magic_field_pattern(field)) {
for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
pcf_print_master_field(fp, mode, masterp, field);
} else {
pcf_print_master_field(fp, mode, masterp, field);
}
}
}
} else {
for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
pcf_print_master_field(fp, mode, masterp, field);
}
}
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (req->match_count == 0)
msg_warn("unmatched request: \"%s\"", req->raw_text);
argv_free(req->service_pattern);
}
myfree((void *) field_reqs);
}
}
void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field,
const char *new_value)
{
if (field == PCF_MASTER_FLD_CMD) {
argv_truncate(masterp->argv, PCF_MASTER_FLD_CMD);
argv_splitq_append(masterp->argv, new_value, PCF_MASTER_BLANKS, CHARS_BRACE);
pcf_normalize_daemon_args(masterp->argv);
}
else {
argv_replace_one(masterp->argv, field, new_value);
}
pcf_check_master_entry(masterp->argv, new_value);
}
static void pcf_print_master_param(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp,
const char *param_name,
const char *param_value)
{
if (mode & PCF_HIDE_VALUE) {
pcf_print_line(fp, mode, "%s%c%s\n",
masterp->name_space, PCF_NAMESP_SEP_CH,
param_name);
} else {
if ((mode & PCF_SHOW_EVAL) != 0)
param_value = pcf_expand_parameter_value((VSTRING *) 0, mode,
param_value, masterp);
if ((mode & PCF_HIDE_NAME) == 0) {
pcf_print_line(fp, mode, "%s%c%s = %s\n",
masterp->name_space, PCF_NAMESP_SEP_CH,
param_name, param_value);
} else {
pcf_print_line(fp, mode, "%s\n", param_value);
}
}
if (msg_verbose)
vstream_fflush(fp);
}
static int pcf_sort_argv_cb(const void *a, const void *b)
{
return (strcmp(*(char **) a, *(char **) b));
}
static void pcf_show_master_any_param(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp)
{
const char *myname = "pcf_show_master_any_param";
ARGV *argv = argv_alloc(10);
DICT *dict = masterp->all_params;
const char *param_name;
const char *param_value;
int param_count = 0;
int how;
char **cpp;
for (how = DICT_SEQ_FUN_FIRST;
dict->sequence(dict, how, ¶m_name, ¶m_value) == 0;
how = DICT_SEQ_FUN_NEXT) {
argv_add(argv, param_name, ARGV_END);
param_count++;
}
qsort(argv->argv, param_count, sizeof(argv->argv[0]), pcf_sort_argv_cb);
for (cpp = argv->argv; (param_name = *cpp) != 0; cpp++) {
if ((param_value = dict_get(dict, param_name)) == 0)
msg_panic("%s: parameter name not found: %s", myname, param_name);
pcf_print_master_param(fp, mode, masterp, param_name, param_value);
}
argv_free(argv);
}
void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv)
{
PCF_MASTER_ENT *masterp;
PCF_MASTER_FLD_REQ *field_reqs;
PCF_MASTER_FLD_REQ *req;
DICT *dict;
const char *param_value;
if (argc > 0) {
field_reqs = (PCF_MASTER_FLD_REQ *)
mymalloc(sizeof(*field_reqs) * argc);
for (req = field_reqs; req < field_reqs + argc; req++) {
req->match_count = 0;
req->raw_text = *argv++;
req->service_pattern =
pcf_parse_service_pattern(req->raw_text, 1, 3);
if (req->service_pattern == 0)
msg_fatal("-P option requires service_name[/type[/parameter]]");
req->param_pattern = req->service_pattern->argv[2];
}
}
for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
if ((dict = masterp->all_params) != 0) {
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
masterp->argv->argv[0],
masterp->argv->argv[1])) {
if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) {
pcf_show_master_any_param(fp, mode, masterp);
req->match_count += 1;
} else if ((param_value = dict_get(dict,
req->param_pattern)) != 0) {
pcf_print_master_param(fp, mode, masterp,
req->param_pattern,
param_value);
req->match_count += 1;
}
}
}
} else {
pcf_show_master_any_param(fp, mode, masterp);
}
}
}
if (argc > 0) {
for (req = field_reqs; req < field_reqs + argc; req++) {
if (req->match_count == 0)
msg_warn("unmatched request: \"%s\"", req->raw_text);
argv_free(req->service_pattern);
}
myfree((void *) field_reqs);
}
}
void pcf_edit_master_param(PCF_MASTER_ENT *masterp, int mode,
const char *param_name,
const char *param_value)
{
const char *myname = "pcf_edit_master_param";
ARGV *argv = masterp->argv;
const char *arg;
const char *aval;
int param_match = 0;
int name_len = strlen(param_name);
int field;
for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
arg = argv->argv[field];
if (arg[0] != '-' || strcmp(arg, "--") == 0) {
break;
}
else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
&& (aval = argv->argv[field + 1]) != 0) {
if (strcmp(arg, "-o") == 0) {
if (strncmp(aval, param_name, name_len) == 0
&& aval[name_len] == '=') {
param_match = 1;
switch (mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL)) {
case PCF_EDIT_CONF:
aval = concatenate(param_name, "=",
param_value, (char *) 0);
argv_replace_one(argv, field + 1, aval);
myfree((void *) aval);
if (masterp->all_params)
dict_put(masterp->all_params, param_name, param_value);
break;
case PCF_EDIT_EXCL:
argv_delete(argv, field, 2);
if (masterp->all_params)
dict_del(masterp->all_params, param_name);
field -= 2;
break;
default:
msg_panic("%s: unexpected mode: %d", myname, mode);
}
}
}
field += 1;
}
}
if ((mode & PCF_EDIT_CONF) && param_match == 0) {
argv_insert_one(argv, field, "-o");
aval = concatenate(param_name, "=",
param_value, (char *) 0);
argv_insert_one(argv, field + 1, aval);
if (masterp->all_params)
dict_put(masterp->all_params, param_name, param_value);
myfree((void *) aval);
param_match = 1;
}
}