/*++
/* NAME
/* postconf_master 3
/* SUMMARY
/* support for master.cf
/* SYNOPSIS
/* #include <postconf.h>
/*
/* const char pcf_daemon_options_expecting_value[];
/*
/* void pcf_read_master(fail_on_open)
/* int fail_on_open;
/*
/* void pcf_show_master_entries(fp, mode, service_filters)
/* VSTREAM *fp;
/* int mode;
/* char **service_filters;
/*
/* void pcf_show_master_fields(fp, mode, n_filters, field_filters)
/* VSTREAM *fp;
/* int mode;
/* int n_filters;
/* char **field_filters;
/*
/* void pcf_edit_master_field(masterp, field, new_value)
/* PCF_MASTER_ENT *masterp;
/* int field;
/* const char *new_value;
/*
/* void pcf_show_master_params(fp, mode, param_class, argc, **param_filters)
/* VSTREAM *fp;
/* int mode;
/* int param_class;
/* int argc;
/* char **param_filters;
/*
/* void pcf_edit_master_param(masterp, mode, param_name, param_value)
/* PCF_MASTER_ENT *masterp;
/* int mode;
/* const char *param_name;
/* const char *param_value;
/* AUXILIARY FUNCTIONS
/* const char *pcf_parse_master_entry(masterp, buf)
/* PCF_MASTER_ENT *masterp;
/* const char *buf;
/*
/* void pcf_print_master_entry(fp, mode, masterp)
/* VSTREAM *fp;
/* int mode;
/* PCF_MASTER_ENT *masterp;
/*
/* void pcf_free_master_entry(masterp)
/* PCF_MASTER_ENT *masterp;
/* DESCRIPTION
/* pcf_read_master() reads entries from master.cf into memory.
/*
/* pcf_show_master_entries() writes the entries in the master.cf
/* file to the specified stream.
/*
/* pcf_show_master_fields() writes name/type/field=value records
/* to the specified stream.
/*
/* pcf_edit_master_field() updates the value of a single-column
/* or multi-column attribute.
/*
/* pcf_show_master_params() writes name/type/parameter=value
/* records to the specified stream. Like show_parameters(),
/* this may list either selected parameters, or all parameters that
/* match the param_class argument.
/*
/* pcf_edit_master_param() updates, removes or adds the named
/* parameter in a master.cf entry (the remove request ignores
/* the parameter value).
/*
/* pcf_daemon_options_expecting_value[] is an array of master.cf
/* daemon command-line options that expect an option value.
/*
/* pcf_parse_master_entry() parses a (perhaps multi-line)
/* string that contains a complete master.cf entry, and
/* normalizes daemon command-line options to simplify further
/* handling.
/*
/* pcf_print_master_entry() prints a parsed master.cf entry.
/*
/* pcf_free_master_entry() returns storage to the heap that
/* was allocated by pcf_parse_master_entry().
/*
/* Arguments
/* .IP fail_on_open
/* Specify FAIL_ON_OPEN if open failure is a fatal error,
/* WARN_ON_OPEN if a warning should be logged instead.
/* .IP fp
/* Output stream.
/* .IP mode
/* Bit-wise OR of flags. Flags other than the following are
/* ignored.
/* .RS
/* .IP PCF_FOLD_LINE
/* Wrap long output lines.
/* .IP PCF_SHOW_EVAL
/* Expand $name in parameter values.
/* .IP PCF_EDIT_EXCL
/* Request that pcf_edit_master_param() removes the parameter.
/* .RE
/* .IP n_filters
/* The number of command-line filters.
/* .IP field_filters
/* A list of zero or more service field patterns (name/type/field).
/* The output is formatted as "name/type/field = value". If
/* no filters are specified, pcf_show_master_fields() outputs
/* the fields of all master.cf entries in the specified order.
/* .IP param_filters
/* A list of zero or more service parameter patterns
/* (name/type/parameter). The output is formatted as
/* "name/type/parameter = value". If no filters are specified,
/* pcf_show_master_params() outputs the parameters of all
/* master.cf entries in sorted order.
/* .IP service_filters
/* A list of zero or more service patterns (name or name/type).
/* If no filters are specified, pcf_show_master_entries()
/* outputs all master.cf entries in the specified order.
/* .IP field
/* Index into parsed master.cf entry.
/* .IP new_value
/* Replacement value for the specified field. It is split in
/* whitespace in case of a multi-field attribute.
/* DIAGNOSTICS
/* Problems are reported to the standard error stream.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
/* Utility library. */
#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 <dict_ht.h>
/* Global library. */
#include <mail_params.h>
/* Master library. */
#include <master_proto.h>
/* Application-specific. */
#include <postconf.h>
const char pcf_daemon_options_expecting_value[] = "o" ;
/*
* Data structure to capture a command - line service field filter .
*/
typedef struct {
int match_count; /* hit count */
const char *raw_text; /* full pattern text */
ARGV *service_pattern; /* parsed service name, type, ... */
int field_pattern; /* parsed field pattern */
const char *param_pattern; /* parameter pattern */
} PCF_MASTER_FLD_REQ;
/*
* Valid inputs .
*/
static const char *pcf_valid_master_types[] = {
MASTER_XPORT_NAME_UNIX,
MASTER_XPORT_NAME_FIFO,
MASTER_XPORT_NAME_INET,
MASTER_XPORT_NAME_PASS,
MASTER_XPORT_NAME_UXDG,
0 ,
};
static const char pcf_valid_bool_types[] = "yn-" ;
static VSTRING *pcf_exp_buf;
static VSTRING *pcf_json_buf;
#define STR(x) vstring_str(x)
/* pcf_extract_field - extract text from {}, trim leading/trailing blanks */
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);
}
/* pcf_normalize_nameval - normalize name = value from inside {} */
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);
}
}
/* pcf_normalize_daemon_args - bring daemon arguments into canonical form */
static void pcf_normalize_daemon_args(ARGV *argv)
{
int field;
char *arg;
char *cp;
char *junk;
int extract_field;
/*
* Normalize options to simplify later processing .
*/
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 ) {
/* Split "-stuffozz" into "-stuff" and "-ozz". */
junk = concatenate("-" , cp, (char *) 0 );
argv_insert_one(argv, field + 1 , junk);
myfree(junk);
*cp = 0 ; /* XXX argv_replace_one() */
break ;
}
}
if (strchr(pcf_daemon_options_expecting_value, arg[1 ]) == 0 )
/* Option requires no value. */
continue ;
if (arg[2 ] != 0 ) {
/* Split "-oname=value" into "-o" "name=value". */
argv_insert_one(argv, field + 1 , arg + 2 );
arg[2 ] = 0 ; /* XXX argv_replace_one() */
field += 1 ;
extract_field = (argv->argv[field][0 ] == CHARS_BRACE[0 ]);
} else if (argv->argv[field + 1 ] != 0 ) {
/* Already in "-o" "name=value" form. */
field += 1 ;
extract_field = (argv->argv[field][0 ] == CHARS_BRACE[0 ]);
} else
extract_field = 0 ;
/* Extract text inside {}, optionally convert to name=value. */
if (extract_field) {
pcf_extract_field(argv, field, CHARS_BRACE);
if (argv->argv[field - 1 ][1 ] == 'o' )
pcf_normalize_nameval(argv, field);
}
}
/* Normalize non-option arguments. */
for ( /* void */ ; argv->argv[field] != 0; field++)
/* Extract text inside {}. */
if (argv->argv[field][0 ] == CHARS_BRACE[0 ])
pcf_extract_field(argv, field, CHARS_BRACE);
}
/* pcf_fix_fatal - fix multiline text before release */
static NORETURN PRINTFLIKE(1 , 2 ) pcf_fix_fatal(const char *fmt,...)
{
VSTRING *buf = vstring_alloc(100 );
va_list ap;
/*
* Replace newline with whitespace .
*/
va_start(ap, fmt);
vstring_vsprintf(buf, fmt, ap);
va_end(ap);
translit(STR(buf), "\n" , " " );
msg_fatal("%s" , STR(buf));
/* NOTREACHED */
}
/* pcf_check_master_entry - sanity check master.cf entry */
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; /* see below */ ; 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);
}
/* pcf_free_master_entry - destroy parsed entry */
void pcf_free_master_entry(PCF_MASTER_ENT *masterp)
{
/* XX Fixme: allocation/deallocation asymmetry. */
myfree(masterp->name_space);
argv_free(masterp->argv);
if (masterp->valid_names)
htable_free(masterp->valid_names, myfree);
if (masterp->ro_params)
dict_close(masterp->ro_params);
if (masterp->all_params)
dict_close(masterp->all_params);
myfree((void *) masterp);
}
/* pcf_parse_master_entry - parse one master line */
const char *pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf)
{
ARGV *argv;
char *ro_name_space;
char *process_name;
/*
* We can ' t use the master daemon ' s master_ent routines in their current
* form . They convert everything to internal form , and they skip disabled
* services .
*
* The postconf command needs to show default fields as " - " , and needs to
* know about all service names so that it can generate service - dependent
* parameter names ( transport - dependent etc . ) .
*
* XXX Do per - field sanity checks .
*/
argv = argv_splitq(buf, PCF_MASTER_BLANKS, CHARS_BRACE);
if (argv->argc < PCF_MASTER_MIN_FIELDS) {
argv_free(argv); /* Coverity 201311 */
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 );
ro_name_space =
concatenate("ro" , PCF_NAMESP_SEP_STR, masterp->name_space, (char *) 0 );
masterp->argv = argv;
masterp->valid_names = 0 ;
masterp->ro_params = dict_ht_open(ro_name_space, O_CREAT | O_RDWR, 0 );
process_name = basename(argv->argv[PCF_MASTER_FLD_CMD]);
dict_put(masterp->ro_params, VAR_PROCNAME, process_name);
dict_put(masterp->ro_params, VAR_SERVNAME,
strcmp(process_name, argv->argv[0 ]) != 0 ?
argv->argv[0 ] : process_name);
myfree(ro_name_space);
masterp->all_params = 0 ;
return (0 );
}
/* pcf_read_master - read and digest the master.cf file */
void pcf_read_master(int fail_on_open_error)
{
const char *myname = "pcf_read_master" ;
const char *path;
VSTRING *buf;
VSTREAM *fp;
const char *err;
int entry_count = 0 ;
int line_count;
int last_line = 0 ;
/*
* Sanity check .
*/
if (pcf_master_table != 0 )
msg_panic("%s: master table is already initialized" , myname);
/*
* Get the location of master . cf .
*/
path = pcf_get_master_path();
/*
* Initialize the in - memory master table .
*/
pcf_master_table = (PCF_MASTER_ENT *) mymalloc(sizeof (*pcf_master_table));
/*
* Skip blank lines and comment lines . Degrade gracefully if master . cf is
* not available , and master . cf is not the primary target .
*/
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);
}
/*
* Null - terminate the master table and clean up .
*/
pcf_master_table[entry_count].argv = 0 ;
}
/* pcf_print_master_entry_as_json - JSON formatter */
static void pcf_print_master_entry_as_json(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp)
{
char **argv = masterp->argv->argv;
const char *arg;
const char *aval;
int field;
int in_daemon_options;
int need_parens;
if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL))
pcf_exp_buf = vstring_alloc(100 );
if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON))
pcf_json_buf = vstring_alloc(100 );
/*
* Output the namespace part first , so that we can reuse a buffer .
*/
vstream_fprintf(fp, "{\" %s\": " ,
quote_for_json(pcf_json_buf, masterp->name_space, -1 ));
/*
* Show the standard fields with one - space column separation .
*/
#define APPEND_JSON_STR(s, l) quote_for_json_append(pcf_json_buf, (s), (l))
#define APPEND_JSON_STR0(s) APPEND_JSON_STR((s), -1 )
#define APPEND_JSON_CHAR(s) APPEND_JSON_STR((s), 1 )
VSTRING_RESET(pcf_json_buf);
for (field = 0 ; field < PCF_MASTER_MIN_FIELDS; field++) {
arg = argv[field];
if (field > 0 )
APPEND_JSON_CHAR(" " );
APPEND_JSON_STR0(arg);
}
/*
* Format the daemon command - line options and non - option arguments .
*/
in_daemon_options = 1 ;
for ( /* void */ ; (arg = argv[field]) != 0; field++) {
aval = 0 ;
need_parens = 0 ;
if (in_daemon_options) {
if (arg[0 ] != '-' || strcmp(arg, "--" ) == 0 ) {
in_daemon_options = 0 ;
}
/*
* Special processing for options that require a value .
*/
else if (strchr(pcf_daemon_options_expecting_value, arg[1 ]) != 0
&& (aval = argv[field + 1 ]) != 0 ) {
/*
* Optionally , expand $ name in parameter value .
*/
if (strcmp(arg, "-o" ) == 0
&& (mode & PCF_SHOW_EVAL) != 0 )
aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
aval, masterp);
need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)];
}
} else {
need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
}
APPEND_JSON_CHAR(" " );
if (in_daemon_options == 0 && need_parens)
APPEND_JSON_CHAR("{" );
APPEND_JSON_STR0(arg);
if (in_daemon_options == 0 && need_parens)
APPEND_JSON_CHAR("}" );
if (aval) {
APPEND_JSON_CHAR(" " );
if (need_parens)
APPEND_JSON_CHAR("{" );
APPEND_JSON_STR0(aval);
if (need_parens)
APPEND_JSON_CHAR("}" );
field += 1 ;
}
}
VSTRING_TERMINATE(pcf_json_buf);
vstream_fprintf(fp, "\" %s\"}\n" , STR(pcf_json_buf));
if (msg_verbose)
vstream_fflush(fp);
}
/* pcf_print_master_entry - print one master line */
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 , /* service */
11 , /* type */
17 , /* private */
25 , /* unpriv */
33 , /* chroot */
41 , /* wakeup */
49 , /* maxproc */
57 , /* command */
};
if (mode & PCF_SHOW_JSON) {
pcf_print_master_entry_as_json(fp, mode, masterp);
return ;
}
#define ADD_TEXT(text, len) do { \
vstream_fputs(text, fp); line_len += len; } \
while (0 )
#define ADD_SPACE ADD_TEXT(" " , 1 )
if (pcf_exp_buf == 0 )
pcf_exp_buf = vstring_alloc(100 );
/*
* Show the standard fields at their preferred column position . Use at
* least one - space column separation .
*/
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));
}
/*
* Format the daemon command - line options and non - option arguments . Here ,
* we have no data - dependent preference for column positions , but we do
* have argument grouping preferences .
*/
in_daemon_options = 1 ;
for ( /* void */ ; (arg = argv[field]) != 0; field++) {
arg_len = strlen(arg);
aval = 0 ;
need_parens = 0 ;
if (in_daemon_options) {
/*
* Try to show the generic options ( - v - D ) on the first line , and
* non - options on a later line .
*/
if (arg[0 ] != '-' || strcmp(arg, "--" ) == 0 ) {
in_daemon_options = 0 ;
#if 0
if (mode & PCF_FOLD_LINE)
/* Force line wrap. */
line_len = PCF_LINE_LIMIT;
#endif
}
/*
* Special processing for options that require a value .
*/
else if (strchr(pcf_daemon_options_expecting_value, arg[1 ]) != 0
&& (aval = argv[field + 1 ]) != 0 ) {
/* Force line wrap before option with value. */
line_len = PCF_LINE_LIMIT;
/*
* Optionally , expand $ name in parameter value .
*/
if (strcmp(arg, "-o" ) == 0
&& (mode & PCF_SHOW_EVAL) != 0 )
aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
aval, masterp);
/*
* Keep option and value on the same line .
*/
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)];
}
/*
* Insert a line break when the next item won ' t fit .
*/
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 ;
/* Force line wrap after option with value. */
line_len = PCF_LINE_LIMIT;
}
}
vstream_fputs("\n" , fp);
if (msg_verbose)
vstream_fflush(fp);
}
/* pcf_show_master_entries - show master.cf entries */
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;
/*
* Parse the filter expressions .
*/
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]" );
}
}
/*
* Iterate over the master table .
*/
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);
}
}
/*
* Cleanup .
*/
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);
}
}
/* pcf_print_master_field_as_json - scaffolding for JSON */
static void pcf_print_master_field_as_json(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp,
int field)
{
char **argv = masterp->argv->argv;
const char *arg;
const char *aval;
int in_daemon_options;
int need_parens;
if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL) != 0 )
pcf_exp_buf = vstring_alloc(100 );
if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON) != 0 )
pcf_json_buf = vstring_alloc(100 );
/*
* Output the name part first , so that we can reuse a buffer .
*/
vstream_fprintf(fp, "{\" %s\": " ,
quote_for_json_var(pcf_json_buf, masterp->name_space,
PCF_NAMESP_SEP_STR,
pcf_str_field_pattern(field),
(const char *) 0 ));
/*
* Show the field value , or the first value in the case of a multi - column
* field .
*/
VSTRING_RESET(pcf_json_buf);
APPEND_JSON_STR0(argv[field]);
/*
* Format the daemon command - line options and non - option arguments . Here ,
* we have no data - dependent preference for column positions , but we do
* have argument grouping preferences .
*/
if (field == PCF_MASTER_FLD_CMD) {
in_daemon_options = 1 ;
for (field += 1 ; (arg = argv[field]) != 0 ; field++) {
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 ) {
/*
* Optionally , expand $ name in parameter value .
*/
if (strcmp(arg, "-o" ) == 0
&& (mode & PCF_SHOW_EVAL) != 0 )
aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
aval, masterp);
need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)];
}
} else {
need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
}
APPEND_JSON_CHAR(" " );
if (in_daemon_options == 0 && need_parens)
APPEND_JSON_CHAR("{" );
APPEND_JSON_STR0(arg);
if (in_daemon_options == 0 && need_parens)
APPEND_JSON_CHAR("}" );
if (aval) {
APPEND_JSON_CHAR(" " );
if (need_parens)
APPEND_JSON_CHAR("{" );
APPEND_JSON_STR0(aval);
if (need_parens)
APPEND_JSON_CHAR("}" );
field += 1 ;
}
}
}
VSTRING_TERMINATE(pcf_json_buf);
vstream_fprintf(fp, "\" %s\"}\n" , STR(pcf_json_buf));
if (msg_verbose)
vstream_fflush(fp);
}
/* pcf_print_master_field - scaffolding */
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;
if (mode & PCF_SHOW_JSON) {
pcf_print_master_field_as_json(fp, mode, masterp, field);
return ;
}
if (pcf_exp_buf == 0 )
pcf_exp_buf = vstring_alloc(100 );
/*
* Show the field value , or the first value in the case of a multi - column
* field .
*/
#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]));
}
/*
* Format the daemon command - line options and non - option arguments . Here ,
* we have no data - dependent preference for column positions , but we do
* have argument grouping preferences .
*/
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) {
/*
* We make no special case for generic options ( - v - D )
* 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 ) {
/* Force line break before option with value. */
line_len = PCF_LINE_LIMIT;
/*
* Optionally , expand $ name in parameter value .
*/
if (strcmp(arg, "-o" ) == 0
&& (mode & PCF_SHOW_EVAL) != 0 )
aval = pcf_expand_parameter_value(pcf_exp_buf, mode,
aval, masterp);
/*
* Keep option and value on the same line .
*/
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)];
}
/*
* Insert a line break when the next item won ' t fit .
*/
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 ;
/* Force line break after option with value. */
line_len = PCF_LINE_LIMIT;
}
}
}
vstream_fputs("\n" , fp);
if (msg_verbose)
vstream_fflush(fp);
}
/* pcf_show_master_fields - show master.cf fields */
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;
/*
* Parse the filter expressions .
*/
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);
}
}
/*
* Iterate over the master table .
*/
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);
}
}
/*
* Cleanup .
*/
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);
}
}
/* pcf_edit_master_field - replace master.cf field value. */
void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field,
const char *new_value)
{
/*
* Replace multi - column attribute .
*/
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);
}
/*
* Replace single - column attribute .
*/
else {
argv_replace_one(masterp->argv, field, new_value);
}
/*
* Do per - field sanity checks .
*/
pcf_check_master_entry(masterp->argv, new_value);
}
/* pcf_print_master_param - scaffolding */
static void pcf_print_master_param(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp,
const char *param_name,
const char *param_value)
{
if (pcf_exp_buf == 0 && (mode & PCF_SHOW_EVAL))
pcf_exp_buf = vstring_alloc(100 );
if (pcf_json_buf == 0 && (mode & PCF_SHOW_JSON))
pcf_json_buf = vstring_alloc(100 );
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(pcf_exp_buf, mode,
param_value, masterp);
if (mode & PCF_SHOW_JSON) {
vstream_fprintf(fp, "{\" %s\": " ,
quote_for_json_var(pcf_json_buf, masterp->name_space,
PCF_NAMESP_SEP_STR, param_name,
(const char *) 0 ));
vstream_fprintf(fp, "\" %s\"}\n" ,
quote_for_json(pcf_json_buf, param_value, -1 ));
} else 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);
}
/* pcf_sort_argv_cb - sort argv call-back */
static int pcf_sort_argv_cb(const void *a, const void *b)
{
return (strcmp(*(char **) a, *(char **) b));
}
/* merge_main_master_parameters - all parameters as sen by this service */
static DICT *merge_main_master_parameters(int mode, PCF_MASTER_ENT *masterp,
int param_class)
{
DICT *dict;
PCF_PARAM_INFO **main_list;
PCF_PARAM_INFO **main_ht;
const char *dict_spec = "merged_dict" ;
const char *param_name;
const char *param_value;
int how;
/*
* With - PP , use the merged main / master . cf settings instead of
* masterp - > allparams ( the master . cf settings for this service ) .
*/
dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0 );
dict_register(dict_spec, dict);
/*
* For each parameter in the main . cf namespace , look up its effective
* value ( from master . cf or main . cf , or use the default ) .
*/
main_list = PCF_PARAM_TABLE_LIST(pcf_param_table);
for (main_ht = main_list; *main_ht; main_ht++) {
param_name = PCF_PARAM_INFO_NAME(*main_ht);
if (param_class && !(PCF_PARAM_INFO_NODE(*main_ht)->flags & param_class))
continue ;
if ((param_value =
pcf_lookup_parameter_value(mode, param_name, masterp,
PCF_PARAM_INFO_NODE(*main_ht))) == 0 )
msg_panic("%s: parameter name not found: %s" , __func__, param_name);
dict->update(dict, param_name, param_value);
}
myfree((void *) main_list);
/*
* Add master . cf settings with service - specific custom names . Skip
* settings that were already copied in the above loop .
*/
if (masterp->all_params) {
DICT *all_params = masterp->all_params;
for (how = DICT_SEQ_FUN_FIRST;
all_params->sequence(all_params, how, ¶m_name,
¶m_value) == 0 ;
how = DICT_SEQ_FUN_NEXT) {
if (dict->lookup(dict, param_name) == 0 )
dict->update(dict, param_name, param_value);
}
}
return (dict);
}
/* pcf_show_master_any_param - show any parameter in master.cf service entry */
static void pcf_show_master_any_param(VSTREAM *fp, int mode,
PCF_MASTER_ENT *masterp,
DICT *dict)
{
const char *myname = "pcf_show_master_any_param" ;
ARGV *argv = argv_alloc(10 );
const char *param_name;
const char *param_value;
int param_count = 0 ;
int how;
char **cpp;
/*
* Print parameters in sorted order . The number of parameters per
* master . cf entry is small , so we optimize for code simplicity and don ' t
* worry about the cost of double lookup .
*/
/* Look up the parameter names and ignore the values. */
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++;
}
/* Print the parameters in sorted order. */
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);
}
/*
* Clean up .
*/
argv_free(argv);
}
/* pcf_show_master_params - show master.cf params */
void pcf_show_master_params(VSTREAM *fp, int mode, int param_class,
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;
/*
* Parse the filter expressions .
*/
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 ];
}
}
/*
* Iterate over the master table .
*/
for (masterp = pcf_master_table; masterp->argv != 0 ; masterp++) {
if (mode & PCF_MASTER_PP) {
dict = merge_main_master_parameters(mode, masterp,
argc > 0 ? 0 : param_class);
} else {
dict = masterp->all_params;
}
if (dict != 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, dict);
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, dict);
}
}
if (mode & PCF_MASTER_PP)
dict_close(dict);
}
/*
* Cleanup .
*/
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);
}
}
/* pcf_edit_master_param - update, add or remove -o parameter=value */
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];
/*
* Stop at the first non - option argument or end - of - list .
*/
if (arg[0 ] != '-' || strcmp(arg, "--" ) == 0 ) {
break ;
}
/*
* Zoom in on command - line options with a value .
*/
else if (strchr(pcf_daemon_options_expecting_value, arg[1 ]) != 0
&& (aval = argv->argv[field + 1 ]) != 0 ) {
/*
* Zoom in on " - o parameter = value " .
*/
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)) {
/*
* Update parameter = value .
*/
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);
/* XXX Update parameter "used/defined" status. */
break ;
/*
* Delete parameter = value .
*/
case PCF_EDIT_EXCL:
argv_delete(argv, field, 2 );
if (masterp->all_params)
dict_del(masterp->all_params, param_name);
/* XXX Update parameter "used/defined" status. */
field -= 2 ;
break ;
default :
msg_panic("%s: unexpected mode: %d" , myname, mode);
}
}
}
/*
* Skip over the command - line option value .
*/
field += 1 ;
}
}
/*
* Add unmatched parameter .
*/
if ((mode & PCF_EDIT_CONF) && param_match == 0 ) {
/* XXX Generalize: argv_insert(argv, where, list...) */
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);
/* XXX May affect parameter "used/defined" status. */
myfree((void *) aval);
param_match = 1 ;
}
}
Messung V0.5 in Prozent C=82 H=84 G=82
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland