diff options
Diffstat (limited to '')
93 files changed, 7669 insertions, 0 deletions
diff --git a/src/postconf/.indent.pro b/src/postconf/.indent.pro new file mode 120000 index 0000000..5c837ec --- /dev/null +++ b/src/postconf/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro
\ No newline at end of file diff --git a/src/postconf/.printfck b/src/postconf/.printfck new file mode 100644 index 0000000..66016ed --- /dev/null +++ b/src/postconf/.printfck @@ -0,0 +1,25 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 4 0 +post_mail_fprintf 1 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/src/postconf/Makefile.in b/src/postconf/Makefile.in new file mode 100644 index 0000000..b19b230 --- /dev/null +++ b/src/postconf/Makefile.in @@ -0,0 +1,1286 @@ +SHELL = /bin/sh +SRCS = postconf.c postconf_builtin.c postconf_edit.c postconf_main.c \ + postconf_master.c postconf_misc.c postconf_node.c postconf_other.c \ + postconf_service.c postconf_unused.c postconf_user.c postconf_dbms.c \ + postconf_lookup.c postconf_match.c postconf_print.c +OBJS = postconf.o postconf_builtin.o postconf_edit.o postconf_main.o \ + postconf_master.o postconf_misc.o postconf_node.o postconf_other.o \ + postconf_service.o postconf_unused.o postconf_user.o postconf_dbms.o \ + postconf_lookup.o postconf_match.o postconf_print.o +HDRS = postconf.h +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) -DLEGACY_DBMS_SUPPORT +TESTPROG= +MAKES = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \ + str_vars.h time_table.h time_vars.h raw_table.h raw_vars.h \ + nint_table.h nint_vars.h nbool_table.h nbool_vars.h long_table.h \ + long_vars.h str_fn_table.h str_fn_vars.h +DB_MAKES= pcf_ldap_suffixes.h pcf_memcache_suffixes.h pcf_mysql_suffixes.h \ + pcf_pgsql_suffixes.h pcf_sqlite_suffixes.h +TEST_TMP= main.cf master.cf test*.tmp +DUMMIES = makes_dummy # for "make -j" +PROG = postconf +SAMPLES = ../../conf/main.cf.default +INC_DIR = ../../include +LIBS = ../../lib/libxsasl.a \ + ../../lib/lib$(LIB_PREFIX)tls$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)dns$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +../../conf/main.cf.default: $(PROG) Makefile + rm -f $@ + (echo "# DO NOT EDIT THIS FILE. EDIT THE MAIN.CF FILE INSTEAD. THE"; \ + echo "# TEXT HERE JUST SHOWS DEFAULT SETTINGS BUILT INTO POSTFIX."; \ + echo "#"; $(SHLIB_ENV) ./$(PROG) -d -c ../../conf) | \ + egrep -v '^(myhostname|mydomain|mynetworks|process_name|process_id) ' >$@ + +$(OBJS): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \ + test12 test13 test14 test15 test16 test17 test18 test19 test20 test21 \ + test22 test23 test24 test25 test26 test27 test28 test29 test30 test4b \ + test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \ + test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \ + test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \ + test62 test63 test64 test65 test66 test67 test68 test69 + +root_tests: + +update: ../../bin/$(PROG) $(SAMPLES) + +../../bin/$(PROG): $(PROG) + cp $(PROG) ../../bin + +$(MAKES): makes_dummy + +makes_dummy: $(INC_DIR)/mail_params.h ../global/mail_params.c extract.awk Makefile.in + $(AWK) -f extract.awk ../*/*.c | $(SHELL) + touch makes_dummy + +$(DB_MAKES): extract_cfg.sh Makefile.in + +pcf_ldap_suffixes.h: ../global/dict_ldap.c + sh extract_cfg.sh -d ../global/dict_ldap.c > $@ + +pcf_memcache_suffixes.h: ../global/dict_memcache.c + sh extract_cfg.sh -d ../global/dict_memcache.c > $@ + +pcf_mysql_suffixes.h: ../global/dict_mysql.c + sh extract_cfg.sh -d -s ../global/dict_mysql.c > $@ + +pcf_pgsql_suffixes.h: ../global/dict_pgsql.c + sh extract_cfg.sh -d -s ../global/dict_pgsql.c > $@ + +pcf_sqlite_suffixes.h: ../global/dict_sqlite.c + sh extract_cfg.sh -d -s ../global/dict_sqlite.c > $@ + +# Define two parameters with smtpd_restriction_classes. One will be ignored. + +test1: $(PROG) test1.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo smtpd_restriction_classes = foo bar >> main.cf + echo foo = yes >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test1.tmp 2>&1 + diff test1.ref test1.tmp + rm -f main.cf master.cf test1.tmp + +# Define two unused parameters. Expect two warnings. + +test2: $(PROG) test2.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo restriction_classes = foo bar >> main.cf + echo foo = yes >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test2.tmp 2>&1 + diff test2.ref test2.tmp + rm -f main.cf master.cf test2.tmp + +# Define one parameter in main.cf, validate it with main.cf. + +test3: $(PROG) test3.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo = yes >> main.cf + echo 'bar = $$foo' >> main.cf + echo 'always_bcc = $$bar' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test3.tmp 2>&1 + diff test3.ref test3.tmp + rm -f main.cf master.cf test3.tmp + +# Define one parameter in main.cf, validate it with master.cf. + +test4: $(PROG) test4.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo = yes >> main.cf + echo 'bar = $$foo' >> main.cf + echo smtpd unix - n n - 0 smtpd >> master.cf + echo ' -o always_bcc=$$bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test4.tmp 2>&1 + diff test4.ref test4.tmp + rm -f main.cf master.cf test4.tmp + +# Define one parameter in master.cf, validate it with main.cf. + +test4b: $(PROG) test4b.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'always_bcc = $$foo' >> main.cf + echo 'biff = $$bar' >> main.cf + echo 'bar = aaa' >> main.cf + echo smtpd1 unix - n n - 0 smtpd >> master.cf + echo ' -o foo=xxx -o bar=yyy -o baz=zzz' >> master.cf + echo '#smtpd2 unix - n n - 0 smtpd' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test4b.tmp 2>&1 + diff test4b.ref test4b.tmp + rm -f main.cf master.cf test4b.tmp + +# Define one user-defined parameter with name=value in master.cf, +# validate it with known_parameter=$$name in master.cf. + +test5: $(PROG) test5.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo smtpd unix - n n - 0 smtpd >> master.cf + echo ' -o bar=yes -o always_bcc=$$bar -o' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test5.tmp 2>&1 + diff test5.ref test5.tmp + rm -f main.cf master.cf test5.tmp + +# Basic functionality test: service parameters for delivery agents. + +test6: $(PROG) test6.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings unix - n n - 0 pipe >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test6.tmp + diff test6.ref test6.tmp + rm -f main.cf master.cf test6.tmp + +# Basic functionality test: service parameters for spawn programs. + +test7: $(PROG) test7.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings unix - n n - 0 spawn >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test7.tmp + diff test7.ref test7.tmp + rm -f main.cf master.cf test7.tmp + +test8: $(PROG) test8.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings inet - n n - 0 spawn >> master.cf + echo whatevershebrings_time_limit=1 >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test8.tmp + diff test8.ref test8.tmp + rm -f main.cf master.cf test8.tmp + +test9: $(PROG) test9.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo inet - n n - 0 spawn >> master.cf + echo bar unix - n n - 0 spawn >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -M '*'/inet >test9.tmp 2>&1 + diff test9.ref test9.tmp + rm -f main.cf master.cf test9.tmp + +test10: $(PROG) test10.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo inet - n n - 0 spawn >> master.cf + echo bar unix - n n - 0 spawn >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -M bar/inet foo/unix >test10.tmp 2>&1 + diff test10.ref test10.tmp + rm -f main.cf master.cf test10.tmp + +test11: $(PROG) test11.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo inet - n n - 0 spawn >> master.cf + echo bar unix - n n - 0 spawn >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -M >test11.tmp 2>&1 + diff test11.ref test11.tmp + rm -f main.cf master.cf test11.tmp + +# Duplicate service entry. + +test12: $(PROG) test12.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo bar=yes >> main.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o always_bcc=$$bar -o' >> master.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o always_bcc=$$bar -o' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -M >test12.tmp 2>&1 + diff test12.ref test12.tmp + rm -f main.cf master.cf test12.tmp + +# Define parameter with restriction_classes in master.cf, validate in main.cf. + +test13: $(PROG) test13.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo bar=yes >> main.cf + echo baz=xx >> main.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o smtpd_restriction_classes=bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test13.tmp 2>&1 + diff test13.ref test13.tmp + rm -f main.cf master.cf test13.tmp + +# Define parameter with restriction_classes in main.cf, validate in master.cf. + +test14: $(PROG) test14.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo smtpd_restriction_classes=bar >> main.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o bar=yes -o baz=xx' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test14.tmp 2>&1 + diff test14.ref test14.tmp + rm -f main.cf master.cf test14.tmp + +# Define two parameters, one is hidden by master.cf. + +test15: $(PROG) test15.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo bar=xx >> main.cf + echo baz=yy >> main.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o bar=yes -o always_bcc=$$bar$$baz' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test15.tmp 2>&1 + diff test15.ref test15.tmp + rm -f main.cf master.cf test15.tmp + +# Test graceful degradation if master.cf is unavailable. + +test16: $(PROG) test16.ref + rm -f main.cf master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test16.tmp 2>&1 + diff test16.ref test16.tmp + rm -f main.cf master.cf test16.tmp + +test17: $(PROG) test17.ref + rm -f main.cf master.cf + touch -t 197101010000 main.cf + -$(SHLIB_ENV) ./$(PROG) -Mc . >test17.tmp 2>&1; exit 0 + diff test17.ref test17.tmp + rm -f main.cf master.cf test17.tmp + +# Test legacy $name in built-in defaults. + +test18: $(PROG) test18.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo virtual_maps=xxx >> main.cf + echo smtpd_client_connection_limit_exceptions=yyy >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test18.tmp 2>&1 + diff test18.ref test18.tmp + rm -f main.cf master.cf test18.tmp + +# Test $name in "raw" parameters. + +test19: $(PROG) test19.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo forward_path='$$'aaaa >> main.cf + echo default_rbl_reply='$$'bbbb >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test19.tmp 2>&1 + diff test19.ref test19.tmp + rm -f main.cf master.cf test19.tmp + +# Test master.cf line folding. + +test20: $(PROG) test20.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo inet - n n - 0 spawn >> master.cf + echo ' -o always_bcc=$$bar$$baz' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc . >test20.tmp 2>&1 + diff test20.ref test20.tmp + rm -f main.cf master.cf test20.tmp + +# Test main.cf line folding. + +test21: $(PROG) test21.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx \ + xxxxxxxxxxxxx xxxxxxxxxxxxxx >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nfc . >test21.tmp 2>&1 + diff test21.ref test21.tmp + rm -f main.cf master.cf test21.tmp + +# Like test6, but using a delivery agent that has no _time_limit magic. + +test22: $(PROG) test22.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . 2>&1 | grep whatevershebrings >test22.tmp + diff test22.ref test22.tmp + rm -f main.cf master.cf test22.tmp + +# Test the -C flag for each category. + +test23: $(PROG) test23.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo always_bcc = yes >> main.cf + echo name = value >> main.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + echo ' -o always_bcc=$$name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -nC builtin >test23.tmp 2>&1 + diff test23.ref test23.tmp + rm -f main.cf master.cf test23.tmp + +test24: $(PROG) test24.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo always_bcc = yes >> main.cf + echo name = value >> main.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + echo ' -o always_bcc=$$name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -nC user >test24.tmp 2>&1 + diff test24.ref test24.tmp + rm -f main.cf master.cf test24.tmp + +test25: $(PROG) test25.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo always_bcc = yes >> main.cf + echo name = value >> main.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + echo ' -o always_bcc=$$name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -C service 2>&1 | grep whatevershebrings >test25.tmp + diff test25.ref test25.tmp + rm -f main.cf master.cf test25.tmp + +# Test completeness of "-C all". + +test26: $(PROG) test26.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo always_bcc = yes >> main.cf + echo name = value >> main.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + echo ' -o always_bcc=$$name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . -C all >test26.tmp 2>&1 + diff test26.ref test26.tmp + rm -f main.cf master.cf test26.tmp + +test27: $(PROG) test27.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo always_bcc = yes >> main.cf + echo name = value >> main.cf + echo whatevershebrings unix - n n - 0 smtp >> master.cf + echo ' -o always_bcc=$$name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c . -C all 2>&1 | grep whatevershebrings >test27.tmp + diff test27.ref test27.tmp + rm -f main.cf master.cf test27.tmp + +# Test macro expansion, type:table parsing and scoping. + +test28: $(PROG) test28.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'xx = proxy:ldap:foo' >> main.cf + echo 'foo_domain = bar' >> main.cf + echo 'header_checks = ldap:hh' >> main.cf + echo 'hh_domain = whatever' >> main.cf + echo 'zz = $$yy' >> main.cf + echo 'yy = aap' >> main.cf + echo 'db = memcache' >> main.cf + echo whatevershebrings unix - n n - 0 other >> master.cf + echo ' -o body_checks=$$db:$$zz' >> master.cf + echo 'aap_domain = whatever' >> main.cf + echo 'aa_domain = whatever' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test28.tmp 2>&1 + diff test28.ref test28.tmp + rm -f main.cf master.cf test28.tmp + +# Test the handling of known and unknown database-defined suffixes. + +test29: $(PROG) test29.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'ldapxx = proxy:ldap:ldapfoo' >> main.cf + echo 'ldapfoo_domain = bar' >> main.cf + echo 'ldapfoo_domainx = bar' >> main.cf + echo 'mysqlxx = proxy:mysql:mysqlfoo' >> main.cf + echo 'mysqlfoo_domain = bar' >> main.cf + echo 'mysqlfoo_domainx = bar' >> main.cf + echo 'pgsqlxx = proxy:pgsql:pgsqlfoo' >> main.cf + echo 'pgsqlfoo_domain = bar' >> main.cf + echo 'pgsqlfoo_domainx = bar' >> main.cf + echo 'sqlitexx = proxy:sqlite:sqlitefoo' >> main.cf + echo 'sqlitefoo_domain = bar' >> main.cf + echo 'sqlitefoo_domainx = bar' >> main.cf + echo 'memcachexx = proxy:memcache:memcachefoo' >> main.cf + echo 'memcachefoo_domain = bar' >> main.cf + echo 'memcachefoo_domainx = bar' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test29.tmp 2>&1 + diff test29.ref test29.tmp + rm -f main.cf master.cf test29.tmp + +test30: $(PROG) test30.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo p1=xx >> main.cf + echo p2=xx >> main.cf + echo p3=xx >> main.cf + echo p4=xx >> main.cf + echo whatevershebrings unix - n n - 0 other >> master.cf + echo ' -o body_checks=$$p1' >> master.cf + echo ' -o bodyx_checks=$$p2' >> master.cf + echo ' -oheader_checks=$$p3' >> master.cf + echo ' -oheaderx_checks=$$p4' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test30.tmp 2>&1 + diff test30.ref test30.tmp + rm -f main.cf master.cf test30.tmp + +# Does a non-default setting propagate to a non-default value? + +test31: $(PROG) test31.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'smtpd_helo_restrictions=whatever' >> main.cf + echo 'smtpd_sender_restrictions=$$smtpd_helo_restrictions' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nxc . >test31.tmp 2>&1 + diff test31.ref test31.tmp + rm -f main.cf master.cf test31.tmp + +# Does a non-default setting propagate to a default value? + +test32: $(PROG) test32.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'relay_domains=whatever' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -xc . fast_flush_domains >test32.tmp 2>&1 + diff test32.ref test32.tmp + rm -f main.cf master.cf test32.tmp + +# Does a default setting propagate to a non-default value? + +test33: $(PROG) test33.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'mydestination=whatever' >> main.cf + echo 'always_bcc=$$relay_domains' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -xc . always_bcc >test33.tmp 2>&1 + diff test33.ref test33.tmp + rm -f main.cf master.cf test33.tmp + +test34: $(PROG) test34.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'mydestination=whatever' >> main.cf + echo 'process_name=xxx' >> main.cf + echo 'process_id=yyy' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -xc . mydestination process_name >test34.tmp 2>&1 + diff test34.ref test34.tmp + rm -f main.cf master.cf test34.tmp + +test35: $(PROG) test35.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings unix - n n - 0 other >> master.cf + echo ' -o body_checks=whatever' >> master.cf + echo ' -o process_name=aaa' >> master.cf + echo ' -o process_id=bbb' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -xc . process_name >test35.tmp 2>&1 + diff test35.ref test35.tmp + rm -f main.cf master.cf test35.tmp + +test36: $(PROG) test36.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'mydestination=$$virtual_mapx' >> main.cf + echo 'virtual_alias_maps=$$virtual_maps' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nxc . >test36.tmp 2>&1 + diff test36.ref test36.tmp + rm -f main.cf master.cf test36.tmp + +test37: $(PROG) test37.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'xxx=yyy' >> main.cf + echo 'aaa=bbb' >> main.cf + echo whatever unix - n n - 0 other >> master.cf + echo ' -o mydestination=$$xxx' >> master.cf + echo ' -o always_bcc=$$aaa' >> master.cf + echo ' -o aaa=ccc' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfxc . >test37.tmp 2>&1 + diff test37.ref test37.tmp + rm -f main.cf master.cf test37.tmp + +test39: $(PROG) test39.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc . '*'/unix >test39.tmp 2>&1 + diff test39.ref test39.tmp + rm -f main.cf master.cf test39.tmp + +test40: $(PROG) test40.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo ' -voaaa=bbb' >> master.cf + echo ' -vo ccc=$$aaa' >> master.cf + echo ' -v -oddd=$$ccc' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfxc . '*'/unix >test40.tmp 2>&1 + diff test40.ref test40.tmp + rm -f main.cf master.cf test40.tmp + +test41: $(PROG) test41.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar unix - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test41.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test41.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=YYY bar/unix/aaa=BBB >>test41.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test41.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Pc . >>test41.tmp 2>&1 + diff test41.ref test41.tmp + rm -f main.cf master.cf test41.tmp + +test42: $(PROG) test42.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar unix - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test42.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test42.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Pc . >>test42.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -PXc. bar/unix/xxx bar/unix/aaa >>test42.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test42.tmp 2>&1 + diff test42.ref test42.tmp + rm -f main.cf master.cf test42.tmp + +test43: $(PROG) test43.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar unix - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Fc . bar/unix/chroot=y bar/unix/command='aa -stuffobb=cc dd' >test43.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test43.tmp 2>&1 + diff test43.ref test43.tmp + rm -f main.cf master.cf test43.tmp + +test44: $(PROG) test44.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar unix - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mc . bar/unix='xx inet - n n - 0 aa -stuffobb=cc dd' >test44.tmp 2>&1 + $(SHLIB_ENV) ./$(PROG) -Mfc. >>test44.tmp 2>&1 + diff test44.ref test44.tmp + rm -f main.cf master.cf test44.tmp + +test45: $(PROG) test45.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar xxxx - n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test45.tmp 2>&1 || true + diff test45.ref test45.tmp + rm -f main.cf master.cf test45.tmp + +test46: $(PROG) test46.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet X n n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test46.tmp 2>&1 || true + diff test46.ref test46.tmp + rm -f main.cf master.cf test46.tmp + +test47: $(PROG) test47.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - X n - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test47.tmp 2>&1 || true + diff test47.ref test47.tmp + rm -f main.cf master.cf test47.tmp + +test48: $(PROG) test48.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n X - 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test48.tmp 2>&1 || true + diff test48.ref test48.tmp + rm -f main.cf master.cf test48.tmp + +test49: $(PROG) test49.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n X 0 other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test49.tmp 2>&1 || true + diff test49.ref test49.tmp + rm -f main.cf master.cf test49.tmp + +test50: $(PROG) test50.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n - X other >> master.cf + echo baz unix - n n - 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test50.tmp 2>&1 || true + diff test50.ref test50.tmp + rm -f main.cf master.cf test50.tmp + +test51: $(PROG) test51.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n -? 0 other >> master.cf + echo bar inet - n n X? 0 other >> master.cf + echo baz unix - n n 0? 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test51.tmp 2>&1 || true + diff test51.ref test51.tmp + rm -f main.cf master.cf test51.tmp + +test52: $(PROG) test52.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -MXc. bar/inet foo/unix xxx/yyy + $(SHLIB_ENV) ./$(PROG) -Mfc. >test52.tmp 2>&1 || true + diff test52.ref test52.tmp + rm -f main.cf master.cf test52.tmp + +test53: $(PROG) test53.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet xxx/yyy + diff test53.ref master.cf + rm -f main.cf master.cf test53.tmp + +test54: $(PROG) test54.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet foo/unix + diff test54.ref master.cf + rm -f main.cf master.cf test54.tmp + +test55: $(PROG) test55.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet baz/unix + diff test55.ref master.cf + rm -f main.cf master.cf test55.tmp + +test56: $(PROG) test56.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo " -o first" >> master.cf + echo " -o second" >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -M#c. bar/inet xxx/yyy + diff test56.ref master.cf + rm -f main.cf master.cf test56.tmp + +# Many more tests in util/mac_expand.in. + +test57: $(PROG) test57.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'x = $${{1} == {2}?{error}:x-value}' >> main.cf + echo 'y = y-value' >> main.cf + echo 'bar = $${x?{$$y}:$$z}' >> main.cf + echo 'baz = $${x?{$$z}:$$y}' >> main.cf + echo 'foo = $$bar$$baz' >> main.cf + echo 't1 = Postfix 2.11 $${{$${x?bug:x}} == {bug}?in}compatible' >> main.cf + echo 't2 = $$t1' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nxc. >test57.tmp 2>&1 + diff test57.ref test57.tmp + rm -f main.cf master.cf test57.tmp + +test58: $(PROG) test58.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'mydestination = foo bar pipemap:{ldap:xxx, memcache:yy}x randmap:{xx' >> main.cf + echo 'xxx_domain = foo' >> main.cf + echo 'xxx_bogus = foo' >> main.cf + echo 'yy_backup = bbb' >> main.cf + echo 'yy_bogus = bbb' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./postconf -nc. >test58.tmp 2>&1 || true + diff test58.ref test58.tmp + rm -f main.cf master.cf test58.tmp + +test59: $(PROG) test59.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo bar inet - n n 0 0 other >> master.cf + echo " -o name1=value1" >> master.cf + echo " -o { name2 = value2a value2b }" >> master.cf + echo " { arg1a arg1b }" >> master.cf + echo " { arg2a arg2b }x" >> master.cf + echo " { arg3a arg3b " >> master.cf + echo baz unix - n n 0 0 other >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Mfc. >test59.tmp 2>&1 || true + diff test59.ref test59.tmp + rm -f main.cf master.cf test59.tmp + +test60: $(PROG) test60.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo ' -o always_bcc=bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Fhc. >test60.tmp 2>&1 || true + diff test60.ref test60.tmp + rm -f main.cf master.cf test60.tmp + +test61: $(PROG) test61.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo ' -o always_bcc=bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -Phc. >test61.tmp 2>&1 || true + diff test61.ref test61.tmp + rm -f main.cf master.cf test61.tmp + +test62: $(PROG) test62.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo ' -o always_bcc=bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -FHc. >test62.tmp 2>&1 || true + diff test62.ref test62.tmp + rm -f main.cf master.cf test62.tmp + +test63: $(PROG) test63.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo foo unix - n n - 0 other >> master.cf + echo ' -o always_bcc=bar' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -PHc. >test63.tmp 2>&1 || true + diff test63.ref test63.tmp + rm -f main.cf master.cf test63.tmp + +# main.cf overrides built-in default. + +test64: $(PROG) test64.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'relayhost = relay-from-main.cf' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c. relayhost >test64.tmp 2>&1 + diff test64.ref test64.tmp + rm -f main.cf master.cf test64.tmp + +# '-o name=value' overrides main.cf. + +test65: $(PROG) test65.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo 'relayhost = relay-from-main.cf' >> main.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c. -o relayhost=relay-from-cmd-line relayhost >test65.tmp 2>&1 + diff test65.ref test65.tmp + rm -f main.cf master.cf test65.tmp + +# unknown parameters in database configuration file (absolute pathname). + +test66: $(PROG) test66.ref + rm -f main.cf master.cf + touch master.cf + echo alias_maps = ldap:`pwd`/test66.cf >> main.cf + echo " " mysql:`pwd`/test66.cf >> main.cf + echo " " pgsql:`pwd`/test66.cf >> main.cf + echo " " sqlite:`pwd`/test66.cf >> main.cf + echo " " memcache:`pwd`/test66.cf >> main.cf + echo junk = junk >> test66.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c. 2>test66.tmp >/dev/null + sed "s;PWD;`pwd`;" test66.ref | diff - test66.tmp + rm -f main.cf master.cf test66.tmp test66.cf + +# expand process name and service name in master.cf. + +test67: $(PROG) test67.ref + rm -f main.cf master.cf + touch master.cf + echo 'smtp inet n - n - - smtpd' >>master.cf + echo ' -o test1_process_name=$$process_name' >> master.cf + echo ' -o test1_service_name=$$service_name' >> master.cf + echo 'smtp unix n - n - - smtp' >>master.cf + echo ' -o test2_process_name=$$process_name' >> master.cf + echo ' -o test2_service_name=$$service_name' >> master.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -xMfc. >test67.tmp 2>&1 + diff test67.ref test67.tmp + rm -f main.cf master.cf test67.tmp + +test68: $(PROG) test68.ref + rm -f main.cf master.cf + touch master.cf + echo foo = ldap:`pwd` >> main.cf + echo 'alias_maps = $$foo/test68.cf' >> main.cf + echo " " mysql:`pwd`/test68.cf >> main.cf + echo " " pgsql:`pwd`/test68.cf >> main.cf + echo " " sqlite:`pwd`/test68.cf >> main.cf + echo " " memcache:`pwd`/test68.cf >> main.cf + echo junk = junk >> test68.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -c. 2>test68.tmp >/dev/null + sed "s;PWD;`pwd`;" test68.ref | diff - test68.tmp + rm -f main.cf master.cf test68.tmp test68.cf + +# See also test28 for user-defined parameters defined in main.cf. + +test69: $(PROG) test69.ref + rm -f main.cf master.cf + touch main.cf master.cf + echo whatevershebrings unix - n n - 0 other >> master.cf + echo " -o ldap=ldap:`pwd`" >> master.cf + echo ' -o body_checks=$$ldap/test69.cf' >> master.cf + echo junk = junk >> test69.cf + touch -t 197101010000 main.cf + $(SHLIB_ENV) ./$(PROG) -nc . >test69.tmp 2>&1 + sed "s;PWD;`pwd`;" test69.ref | diff - test69.tmp + rm -f main.cf master.cf test69.tmp test69.cf + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core $(PROG) $(TESTPROG) junk $(MAKES) $(AUTOS) $(DUMMIES) \ + $(TEST_TMP) $(DB_MAKES) + rm -rf printfck + +tidy: clean + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +postconf.o: ../../include/argv.h +postconf.o: ../../include/check_arg.h +postconf.o: ../../include/dict.h +postconf.o: ../../include/htable.h +postconf.o: ../../include/mail_conf.h +postconf.o: ../../include/mail_dict.h +postconf.o: ../../include/mail_params.h +postconf.o: ../../include/mail_run.h +postconf.o: ../../include/mail_version.h +postconf.o: ../../include/msg.h +postconf.o: ../../include/msg_vstream.h +postconf.o: ../../include/myflock.h +postconf.o: ../../include/mymalloc.h +postconf.o: ../../include/name_code.h +postconf.o: ../../include/name_mask.h +postconf.o: ../../include/stringops.h +postconf.o: ../../include/sys_defs.h +postconf.o: ../../include/vbuf.h +postconf.o: ../../include/vstream.h +postconf.o: ../../include/vstring.h +postconf.o: ../../include/warn_stat.h +postconf.o: postconf.c +postconf.o: postconf.h +postconf_builtin.o: ../../include/argv.h +postconf_builtin.o: ../../include/attr.h +postconf_builtin.o: ../../include/check_arg.h +postconf_builtin.o: ../../include/dict.h +postconf_builtin.o: ../../include/get_hostname.h +postconf_builtin.o: ../../include/htable.h +postconf_builtin.o: ../../include/inet_proto.h +postconf_builtin.o: ../../include/iostuff.h +postconf_builtin.o: ../../include/mail_addr.h +postconf_builtin.o: ../../include/mail_conf.h +postconf_builtin.o: ../../include/mail_params.h +postconf_builtin.o: ../../include/mail_proto.h +postconf_builtin.o: ../../include/mail_version.h +postconf_builtin.o: ../../include/msg.h +postconf_builtin.o: ../../include/myflock.h +postconf_builtin.o: ../../include/mymalloc.h +postconf_builtin.o: ../../include/mynetworks.h +postconf_builtin.o: ../../include/name_code.h +postconf_builtin.o: ../../include/nvtable.h +postconf_builtin.o: ../../include/server_acl.h +postconf_builtin.o: ../../include/stringops.h +postconf_builtin.o: ../../include/sys_defs.h +postconf_builtin.o: ../../include/vbuf.h +postconf_builtin.o: ../../include/vstream.h +postconf_builtin.o: ../../include/vstring.h +postconf_builtin.o: bool_table.h +postconf_builtin.o: bool_vars.h +postconf_builtin.o: install_table.h +postconf_builtin.o: install_vars.h +postconf_builtin.o: int_table.h +postconf_builtin.o: int_vars.h +postconf_builtin.o: long_table.h +postconf_builtin.o: long_vars.h +postconf_builtin.o: nbool_table.h +postconf_builtin.o: nbool_vars.h +postconf_builtin.o: nint_table.h +postconf_builtin.o: nint_vars.h +postconf_builtin.o: postconf.h +postconf_builtin.o: postconf_builtin.c +postconf_builtin.o: raw_table.h +postconf_builtin.o: raw_vars.h +postconf_builtin.o: str_fn_table.h +postconf_builtin.o: str_fn_vars.h +postconf_builtin.o: str_table.h +postconf_builtin.o: str_vars.h +postconf_builtin.o: time_table.h +postconf_builtin.o: time_vars.h +postconf_dbms.o: ../../include/argv.h +postconf_dbms.o: ../../include/check_arg.h +postconf_dbms.o: ../../include/dict.h +postconf_dbms.o: ../../include/dict_ht.h +postconf_dbms.o: ../../include/dict_ldap.h +postconf_dbms.o: ../../include/dict_memcache.h +postconf_dbms.o: ../../include/dict_mysql.h +postconf_dbms.o: ../../include/dict_pgsql.h +postconf_dbms.o: ../../include/dict_proxy.h +postconf_dbms.o: ../../include/dict_sqlite.h +postconf_dbms.o: ../../include/htable.h +postconf_dbms.o: ../../include/mac_expand.h +postconf_dbms.o: ../../include/mac_parse.h +postconf_dbms.o: ../../include/mail_conf.h +postconf_dbms.o: ../../include/mail_params.h +postconf_dbms.o: ../../include/msg.h +postconf_dbms.o: ../../include/myflock.h +postconf_dbms.o: ../../include/mymalloc.h +postconf_dbms.o: ../../include/name_code.h +postconf_dbms.o: ../../include/split_at.h +postconf_dbms.o: ../../include/stringops.h +postconf_dbms.o: ../../include/sys_defs.h +postconf_dbms.o: ../../include/vbuf.h +postconf_dbms.o: ../../include/vstream.h +postconf_dbms.o: ../../include/vstring.h +postconf_dbms.o: pcf_ldap_suffixes.h +postconf_dbms.o: pcf_memcache_suffixes.h +postconf_dbms.o: pcf_mysql_suffixes.h +postconf_dbms.o: pcf_pgsql_suffixes.h +postconf_dbms.o: pcf_sqlite_suffixes.h +postconf_dbms.o: postconf.h +postconf_dbms.o: postconf_dbms.c +postconf_edit.o: ../../include/argv.h +postconf_edit.o: ../../include/check_arg.h +postconf_edit.o: ../../include/dict.h +postconf_edit.o: ../../include/edit_file.h +postconf_edit.o: ../../include/htable.h +postconf_edit.o: ../../include/mail_params.h +postconf_edit.o: ../../include/msg.h +postconf_edit.o: ../../include/myflock.h +postconf_edit.o: ../../include/mymalloc.h +postconf_edit.o: ../../include/name_code.h +postconf_edit.o: ../../include/readlline.h +postconf_edit.o: ../../include/split_at.h +postconf_edit.o: ../../include/stringops.h +postconf_edit.o: ../../include/sys_defs.h +postconf_edit.o: ../../include/vbuf.h +postconf_edit.o: ../../include/vstream.h +postconf_edit.o: ../../include/vstring.h +postconf_edit.o: ../../include/vstring_vstream.h +postconf_edit.o: postconf.h +postconf_edit.o: postconf_edit.c +postconf_lookup.o: ../../include/argv.h +postconf_lookup.o: ../../include/check_arg.h +postconf_lookup.o: ../../include/dict.h +postconf_lookup.o: ../../include/htable.h +postconf_lookup.o: ../../include/mac_expand.h +postconf_lookup.o: ../../include/mac_parse.h +postconf_lookup.o: ../../include/mail_conf.h +postconf_lookup.o: ../../include/msg.h +postconf_lookup.o: ../../include/myflock.h +postconf_lookup.o: ../../include/mymalloc.h +postconf_lookup.o: ../../include/name_code.h +postconf_lookup.o: ../../include/stringops.h +postconf_lookup.o: ../../include/sys_defs.h +postconf_lookup.o: ../../include/vbuf.h +postconf_lookup.o: ../../include/vstream.h +postconf_lookup.o: ../../include/vstring.h +postconf_lookup.o: postconf.h +postconf_lookup.o: postconf_lookup.c +postconf_main.o: ../../include/argv.h +postconf_main.o: ../../include/check_arg.h +postconf_main.o: ../../include/dict.h +postconf_main.o: ../../include/htable.h +postconf_main.o: ../../include/mac_expand.h +postconf_main.o: ../../include/mac_parse.h +postconf_main.o: ../../include/mail_conf.h +postconf_main.o: ../../include/mail_params.h +postconf_main.o: ../../include/msg.h +postconf_main.o: ../../include/myflock.h +postconf_main.o: ../../include/mymalloc.h +postconf_main.o: ../../include/name_code.h +postconf_main.o: ../../include/readlline.h +postconf_main.o: ../../include/stringops.h +postconf_main.o: ../../include/sys_defs.h +postconf_main.o: ../../include/vbuf.h +postconf_main.o: ../../include/vstream.h +postconf_main.o: ../../include/vstring.h +postconf_main.o: postconf.h +postconf_main.o: postconf_main.c +postconf_master.o: ../../include/argv.h +postconf_master.o: ../../include/check_arg.h +postconf_master.o: ../../include/dict.h +postconf_master.o: ../../include/htable.h +postconf_master.o: ../../include/mail_params.h +postconf_master.o: ../../include/master_proto.h +postconf_master.o: ../../include/msg.h +postconf_master.o: ../../include/myflock.h +postconf_master.o: ../../include/mymalloc.h +postconf_master.o: ../../include/name_code.h +postconf_master.o: ../../include/readlline.h +postconf_master.o: ../../include/split_at.h +postconf_master.o: ../../include/stringops.h +postconf_master.o: ../../include/sys_defs.h +postconf_master.o: ../../include/vbuf.h +postconf_master.o: ../../include/vstream.h +postconf_master.o: ../../include/vstring.h +postconf_master.o: postconf.h +postconf_master.o: postconf_master.c +postconf_match.o: ../../include/argv.h +postconf_match.o: ../../include/check_arg.h +postconf_match.o: ../../include/dict.h +postconf_match.o: ../../include/htable.h +postconf_match.o: ../../include/msg.h +postconf_match.o: ../../include/myflock.h +postconf_match.o: ../../include/mymalloc.h +postconf_match.o: ../../include/name_code.h +postconf_match.o: ../../include/split_at.h +postconf_match.o: ../../include/sys_defs.h +postconf_match.o: ../../include/vbuf.h +postconf_match.o: ../../include/vstream.h +postconf_match.o: ../../include/vstring.h +postconf_match.o: postconf.h +postconf_match.o: postconf_match.c +postconf_misc.o: ../../include/argv.h +postconf_misc.o: ../../include/check_arg.h +postconf_misc.o: ../../include/dict.h +postconf_misc.o: ../../include/htable.h +postconf_misc.o: ../../include/mail_conf.h +postconf_misc.o: ../../include/mail_params.h +postconf_misc.o: ../../include/myflock.h +postconf_misc.o: ../../include/mymalloc.h +postconf_misc.o: ../../include/name_code.h +postconf_misc.o: ../../include/safe.h +postconf_misc.o: ../../include/sys_defs.h +postconf_misc.o: ../../include/vbuf.h +postconf_misc.o: ../../include/vstream.h +postconf_misc.o: ../../include/vstring.h +postconf_misc.o: postconf.h +postconf_misc.o: postconf_misc.c +postconf_node.o: ../../include/argv.h +postconf_node.o: ../../include/check_arg.h +postconf_node.o: ../../include/dict.h +postconf_node.o: ../../include/htable.h +postconf_node.o: ../../include/msg.h +postconf_node.o: ../../include/myflock.h +postconf_node.o: ../../include/mymalloc.h +postconf_node.o: ../../include/name_code.h +postconf_node.o: ../../include/sys_defs.h +postconf_node.o: ../../include/vbuf.h +postconf_node.o: ../../include/vstream.h +postconf_node.o: ../../include/vstring.h +postconf_node.o: postconf.h +postconf_node.o: postconf_node.c +postconf_other.o: ../../include/argv.h +postconf_other.o: ../../include/check_arg.h +postconf_other.o: ../../include/dict.h +postconf_other.o: ../../include/dns.h +postconf_other.o: ../../include/htable.h +postconf_other.o: ../../include/mbox_conf.h +postconf_other.o: ../../include/msg.h +postconf_other.o: ../../include/myaddrinfo.h +postconf_other.o: ../../include/myflock.h +postconf_other.o: ../../include/name_code.h +postconf_other.o: ../../include/name_mask.h +postconf_other.o: ../../include/sock_addr.h +postconf_other.o: ../../include/sys_defs.h +postconf_other.o: ../../include/tls.h +postconf_other.o: ../../include/vbuf.h +postconf_other.o: ../../include/vstream.h +postconf_other.o: ../../include/vstring.h +postconf_other.o: ../../include/xsasl.h +postconf_other.o: postconf.h +postconf_other.o: postconf_other.c +postconf_print.o: ../../include/argv.h +postconf_print.o: ../../include/check_arg.h +postconf_print.o: ../../include/dict.h +postconf_print.o: ../../include/htable.h +postconf_print.o: ../../include/msg.h +postconf_print.o: ../../include/myflock.h +postconf_print.o: ../../include/name_code.h +postconf_print.o: ../../include/sys_defs.h +postconf_print.o: ../../include/vbuf.h +postconf_print.o: ../../include/vstream.h +postconf_print.o: ../../include/vstring.h +postconf_print.o: postconf.h +postconf_print.o: postconf_print.c +postconf_service.o: ../../include/argv.h +postconf_service.o: ../../include/check_arg.h +postconf_service.o: ../../include/dict.h +postconf_service.o: ../../include/htable.h +postconf_service.o: ../../include/mail_params.h +postconf_service.o: ../../include/msg.h +postconf_service.o: ../../include/myflock.h +postconf_service.o: ../../include/mymalloc.h +postconf_service.o: ../../include/name_code.h +postconf_service.o: ../../include/stringops.h +postconf_service.o: ../../include/sys_defs.h +postconf_service.o: ../../include/vbuf.h +postconf_service.o: ../../include/vstream.h +postconf_service.o: ../../include/vstring.h +postconf_service.o: postconf.h +postconf_service.o: postconf_service.c +postconf_unused.o: ../../include/argv.h +postconf_unused.o: ../../include/check_arg.h +postconf_unused.o: ../../include/dict.h +postconf_unused.o: ../../include/htable.h +postconf_unused.o: ../../include/mail_conf.h +postconf_unused.o: ../../include/mail_params.h +postconf_unused.o: ../../include/msg.h +postconf_unused.o: ../../include/myflock.h +postconf_unused.o: ../../include/name_code.h +postconf_unused.o: ../../include/sys_defs.h +postconf_unused.o: ../../include/vbuf.h +postconf_unused.o: ../../include/vstream.h +postconf_unused.o: ../../include/vstring.h +postconf_unused.o: postconf.h +postconf_unused.o: postconf_unused.c +postconf_user.o: ../../include/argv.h +postconf_user.o: ../../include/check_arg.h +postconf_user.o: ../../include/dict.h +postconf_user.o: ../../include/htable.h +postconf_user.o: ../../include/mac_expand.h +postconf_user.o: ../../include/mac_parse.h +postconf_user.o: ../../include/mail_conf.h +postconf_user.o: ../../include/mail_params.h +postconf_user.o: ../../include/msg.h +postconf_user.o: ../../include/myflock.h +postconf_user.o: ../../include/mymalloc.h +postconf_user.o: ../../include/name_code.h +postconf_user.o: ../../include/stringops.h +postconf_user.o: ../../include/sys_defs.h +postconf_user.o: ../../include/vbuf.h +postconf_user.o: ../../include/vstream.h +postconf_user.o: ../../include/vstring.h +postconf_user.o: postconf.h +postconf_user.o: postconf_user.c diff --git a/src/postconf/extract.awk b/src/postconf/extract.awk new file mode 100644 index 0000000..13b1f91 --- /dev/null +++ b/src/postconf/extract.awk @@ -0,0 +1,190 @@ +# Extract initialization tables from actual source code. + +# XXX: Associated variable aliasing: +# +# Some parameters bind to different variables in different contexts, +# And other parameters map to associated variables in a many-to-1 +# fashion. This is mostly the result of the SMTP+LMTP integration +# and the overloading of parameters that have identical semantics, +# for the corresponding context. +# +# The "++table[...]" below ignores the associated variable name +# when doing duplicate elimination. Differences in the default value +# or lower/upper bounds still result in "postconf -d" duplicates, +# which are a sign of an error somewhere... +# +# XXX Work around ancient AWK implementations with a 10 file limit +# and no working close() operator (e.g. Solaris). Some systems +# have a more modern implementation that is XPG4-compatible, but it +# is too much bother to find out where each system keeps these. + +/^(static| )*(const +)?CONFIG_INT_TABLE .*\{/,/\};/ { + if ($1 ~ /VAR/) { + int_vars["int " substr($3,2,length($3)-2) ";"] = 1 + if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + int_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_STR_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + str_vars["char *" substr($3,2,length($3)-2) ";"] = 1 + if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + str_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_STR_FN_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + str_fn_vars["char *" substr($3,2,length($3)-2) ";"] = 1 + $2 = "pcf_" $2 + if (++stab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + str_fn_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_RAW_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + raw_vars["char *" substr($3,2,length($3)-2) ";"] = 1 + if (++rtab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + raw_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_BOOL_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + bool_vars["int " substr($3,2,length($3)-2) ";"] = 1 + if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + bool_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_TIME_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + time_vars["int " substr($3,2,length($3)-2) ";"] = 1 + if (++ttab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + time_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_NINT_TABLE .*\{/,/\};/ { + if ($1 ~ /VAR/) { + nint_vars["int " substr($3,2,length($3)-2) ";"] = 1 + if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + nint_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_NBOOL_TABLE .*\{/,/\};/ { + if ($1 ~ /^VAR/) { + nbool_vars["int " substr($3,2,length($3)-2) ";"] = 1 + if (++btab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + nbool_table[$0] = 1 + } + } +} +/^(static| )*(const +)?CONFIG_LONG_TABLE .*\{/,/\};/ { + if ($1 ~ /VAR/) { + long_vars["long " substr($3,2,length($3)-2) ";"] = 1 + if (++itab[$1 $2 $4 $5 $6 $7 $8 $9] == 1) { + long_table[$0] = 1 + } + } +} + +END { + # Print parameter declarations without busting old AWK's file limit. + print "cat >int_vars.h <<'EOF'" + for (key in int_vars) + print key + print "EOF" + + print "cat >str_vars.h <<'EOF'" + for (key in str_vars) + print key + print "EOF" + + print "cat >str_fn_vars.h <<'EOF'" + for (key in str_fn_vars) + print key + print "EOF" + + print "cat >raw_vars.h <<'EOF'" + for (key in raw_vars) + print key + print "EOF" + + print "cat >bool_vars.h <<'EOF'" + for (key in bool_vars) + print key + print "EOF" + + print "cat >time_vars.h <<'EOF'" + for (key in time_vars) + print key + print "EOF" + + print "cat >nint_vars.h <<'EOF'" + for (key in nint_vars) + print key + print "EOF" + + print "cat >nbool_vars.h <<'EOF'" + for (key in nbool_vars) + print key + print "EOF" + + print "cat >long_vars.h <<'EOF'" + for (key in long_vars) + print key + print "EOF" + + # Print parameter initializations without busting old AWK's file limit. + print "sed 's/[ ][ ]*/ /g' >int_table.h <<'EOF'" + for (key in int_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >str_table.h <<'EOF'" + for (key in str_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >str_fn_table.h <<'EOF'" + for (key in str_fn_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >raw_table.h <<'EOF'" + for (key in raw_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >bool_table.h <<'EOF'" + for (key in bool_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >time_table.h <<'EOF'" + for (key in time_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >nint_table.h <<'EOF'" + for (key in nint_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >nbool_table.h <<'EOF'" + for (key in nbool_table) + print key + print "EOF" + + print "sed 's/[ ][ ]*/ /g' >long_table.h <<'EOF'" + for (key in long_table) + print key + print "EOF" + + # Flush output nicely. + exit(0); +} diff --git a/src/postconf/extract_cfg.sh b/src/postconf/extract_cfg.sh new file mode 100644 index 0000000..5901e95 --- /dev/null +++ b/src/postconf/extract_cfg.sh @@ -0,0 +1,92 @@ +#!/bin/sh + +# To view the formatted manual page of this file, type: +# POSTFIXSOURCE/mantools/srctoman - extract_cfg.sh | nroff -man + +#++ +# NAME +# extract_cfg 1 +# SUMMARY +# extract database parameter names from cfg_get_xxx() calls +# SYNOPSIS +# \fBextract_cfg [-d|-s] [\fIfile...\fB]\fR +# DESCRIPTION +# The \fBextract_cfg\fR command extracts the parameter names +# from cfg_get_{str,int,bool}() calls in dict_xxx.c files. The +# output is one parameter name per line, formatted as a C string +# followed by comma. +# +# Options: +# .IP \fB-d\fR +# Add the "domain" parameter to the output. This is used by +# the LDAP, memcache, and *SQL* tables. +# .IP \fB-s\fR +# Add the legacy SQL query parameters: "select_field", "table", +# "where_field", and "additional_conditions". +# LICENSE +# .ad +# .fi +# The Secure Mailer license must be distributed with this software. +# HISTORY +# .ad +# .fi +# This command was introduced with Postfix 3.3. +# AUTHOR(S) +# Wietse Venema +# Google, Inc. +# 111 8th Avenue +# New York, NY 10011, USA +#-- + +# In case not installed. +m4 </dev/null || exit 1 + +# Flags to add db_common parameter names. +add_legacy_sql_query_params= +add_domain_param= + +# Parse JCL. + +while : +do + case "$1" in + -d) add_domain_param=1;; + -s) add_legacy_sql_query_params=1;; + -*) echo Bad option: $1 1>&2; exit 1;; + *) break;; + esac + shift +done + +# We use m4 macros to extract arguments from cfg_get_xxx() calls that +# may span multiple lines. We sandwich information of interest between +# control-A characters. Multiple cfg_get_xxx() calls on the same line +# should be OK, as long as the calls don't nest. + +( +cat <<'EOF' +define(`cfg_get_str',`$2 +')dnl +define(`cfg_get_int',`$2 +')dnl +define(`cfg_get_bool',`$2 +')dnl +EOF +# Convert selected C macro definitions into m4 macro definitions. +sed 's/^#define[ ]*\([DICT_MC_NAME_A-Za-z0-9_]*\)[ ]*\("[^"]*"\)/define(`\1'"'"',`\2'"'"')/' "$@" +) | m4 | awk -F '// { print $2 }' | ( +test -n "$add_domain_param" && { +cat <<EOF +"domain" +EOF +} +test -n "$add_legacy_sql_query_params" && { +cat <<EOF +"table" +"select_field" +"where_field" +"additional_conditions" +EOF +} +cat - +) | sort -u | sed 's/$/,/' diff --git a/src/postconf/install_table.h b/src/postconf/install_table.h new file mode 100644 index 0000000..0e0ea0a --- /dev/null +++ b/src/postconf/install_table.h @@ -0,0 +1,2 @@ + VAR_CONFIG_DIR, DEF_CONFIG_DIR, &var_config_dir, 1, 0, + VAR_DEBUG_COMMAND, "", &var_debug_command, 1, 0, diff --git a/src/postconf/install_vars.h b/src/postconf/install_vars.h new file mode 100644 index 0000000..c46882b --- /dev/null +++ b/src/postconf/install_vars.h @@ -0,0 +1,2 @@ +char *var_config_dir; +char *var_debug_command; diff --git a/src/postconf/postconf.c b/src/postconf/postconf.c new file mode 100644 index 0000000..026639e --- /dev/null +++ b/src/postconf/postconf.c @@ -0,0 +1,1096 @@ +/*++ +/* NAME +/* postconf 1 +/* SUMMARY +/* Postfix configuration utility +/* SYNOPSIS +/* .fi +/* .ti -4 +/* \fBManaging main.cf:\fR +/* +/* \fBpostconf\fR [\fB-dfhHnopvx\fR] [\fB-c \fIconfig_dir\fR] +/* [\fB-C \fIclass,...\fR] [\fIparameter ...\fR] +/* +/* \fBpostconf\fR [\fB-epv\fR] [\fB-c \fIconfig_dir\fR] +/* \fIparameter\fB=\fIvalue ...\fR +/* +/* \fBpostconf\fR \fB-#\fR [\fB-pv\fR] [\fB-c \fIconfig_dir\fR] +/* \fIparameter ...\fR +/* +/* \fBpostconf\fR \fB-X\fR [\fB-pv\fR] [\fB-c \fIconfig_dir\fR] +/* \fIparameter ...\fR +/* +/* .ti -4 +/* \fBManaging master.cf service entries:\fR +/* +/* \fBpostconf\fR \fB-M\fR [\fB-fovx\fR] [\fB-c \fIconfig_dir\fR] +/* [\fIservice\fR[\fB/\fItype\fR]\fI ...\fR] +/* +/* \fBpostconf\fR \fB-M\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype\fB=\fIvalue ...\fR +/* +/* \fBpostconf\fR \fB-M#\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype ...\fR +/* +/* \fBpostconf\fR \fB-MX\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype ...\fR +/* +/* .ti -4 +/* \fBManaging master.cf service fields:\fR +/* +/* \fBpostconf\fR \fB-F\fR [\fB-fhHovx\fR] [\fB-c \fIconfig_dir\fR] +/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIfield\fR]]\fI ...\fR] +/* +/* \fBpostconf\fR \fB-F\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype\fB/\fIfield\fB=\fIvalue ...\fR +/* +/* .ti -4 +/* \fBManaging master.cf service parameters:\fR +/* +/* \fBpostconf\fR \fB-P\fR [\fB-fhHovx\fR] [\fB-c \fIconfig_dir\fR] +/* [\fIservice\fR[\fB/\fItype\fR[\fB/\fIparameter\fR]]\fI ...\fR] +/* +/* \fBpostconf\fR \fB-P\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype\fB/\fIparameter\fB=\fIvalue ...\fR +/* +/* \fBpostconf\fR \fB-PX\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* \fIservice\fB/\fItype\fB/\fIparameter ...\fR +/* +/* .ti -4 +/* \fBManaging bounce message templates:\fR +/* +/* \fBpostconf\fR \fB-b\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* [\fItemplate_file\fR] +/* +/* \fBpostconf\fR \fB-t\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* [\fItemplate_file\fR] +/* +/* .ti -4 +/* \fBManaging TLS features:\fR +/* +/* \fBpostconf\fR \fB-T \fImode\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] +/* +/* .ti -4 +/* \fBManaging other configuration:\fR +/* +/* \fBpostconf\fR \fB-a\fR|\fB-A\fR|\fB-l\fR|\fB-m\fR [\fB-v\fR] +/* [\fB-c \fIconfig_dir\fR] +/* DESCRIPTION +/* By default, the \fBpostconf\fR(1) command displays the +/* values of \fBmain.cf\fR configuration parameters, and warns +/* about possible mis-typed parameter names (Postfix 2.9 and later). +/* The command can also change \fBmain.cf\fR configuration +/* parameter values, or display other configuration information +/* about the Postfix mail system. +/* +/* Options: +/* .IP \fB-a\fR +/* List the available SASL plug-in types for the Postfix SMTP +/* server. The plug-in type is selected with the \fBsmtpd_sasl_type\fR +/* configuration parameter by specifying one of the names +/* listed below. +/* .RS +/* .IP \fBcyrus\fR +/* This server plug-in is available when Postfix is built with +/* Cyrus SASL support. +/* .IP \fBdovecot\fR +/* This server plug-in uses the Dovecot authentication server, +/* and is available when Postfix is built with any form of SASL +/* support. +/* .RE +/* .IP +/* This feature is available with Postfix 2.3 and later. +/* .IP \fB-A\fR +/* List the available SASL plug-in types for the Postfix SMTP +/* client. The plug-in type is selected with the \fBsmtp_sasl_type\fR +/* or \fBlmtp_sasl_type\fR configuration parameters by specifying +/* one of the names listed below. +/* .RS +/* .IP \fBcyrus\fR +/* This client plug-in is available when Postfix is built with +/* Cyrus SASL support. +/* .RE +/* .IP +/* This feature is available with Postfix 2.3 and later. +/* .IP "\fB-b\fR [\fItemplate_file\fR]" +/* Display the message text that appears at the beginning of +/* delivery status notification (DSN) messages, expanding +/* $\fBname\fR expressions with actual values as described in +/* \fBbounce\fR(5). +/* +/* To override the \fBbounce_template_file\fR parameter setting, +/* specify a template file name at the end of the "\fBpostconf +/* -b\fR" command line. Specify an empty file name to display +/* built-in templates (in shell language: ""). +/* +/* This feature is available with Postfix 2.3 and later. +/* .IP "\fB-c \fIconfig_dir\fR" +/* The \fBmain.cf\fR configuration file is in the named directory +/* instead of the default configuration directory. +/* .IP "\fB-C \fIclass,...\fR" +/* When displaying \fBmain.cf\fR parameters, select only +/* parameters from the specified class(es): +/* .RS +/* .IP \fBbuiltin\fR +/* Parameters with built-in names. +/* .IP \fBservice\fR +/* Parameters with service-defined names (the first field of +/* a \fBmaster.cf\fR entry plus a Postfix-defined suffix). +/* .IP \fBuser\fR +/* Parameters with user-defined names. +/* .IP \fBall\fR +/* All the above classes. +/* .RE +/* .IP +/* The default is as if "\fB-C all\fR" is +/* specified. +/* +/* This feature is available with Postfix 2.9 and later. +/* .IP \fB-d\fR +/* Print \fBmain.cf\fR default parameter settings instead of +/* actual settings. +/* Specify \fB-df\fR to fold long lines for human readability +/* (Postfix 2.9 and later). +/* .IP \fB-e\fR +/* Edit the \fBmain.cf\fR configuration file, and update +/* parameter settings with the "\fIname=value\fR" pairs on the +/* \fBpostconf\fR(1) command line. +/* +/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file, +/* and replace one or more service entries with new values as +/* specified with "\fIservice/type=value\fR" on the \fBpostconf\fR(1) +/* command line. +/* +/* With \fB-F\fR, edit the \fBmaster.cf\fR configuration file, +/* and replace one or more service fields with new values as +/* specified with "\fIservice/type/field=value\fR" on the +/* \fBpostconf\fR(1) command line. Currently, the "command" +/* field contains the command name and command arguments. this +/* may change in the near future, so that the "command" field +/* contains only the command name, and a new "arguments" +/* pseudofield contains the command arguments. +/* +/* With \fB-P\fR, edit the \fBmaster.cf\fR configuration file, +/* and add or update one or more service parameter settings +/* (-o parameter=value settings) with new values as specified +/* with "\fIservice/type/parameter=value\fR" on the \fBpostconf\fR(1) +/* command line. +/* +/* In all cases the file is copied to a temporary file then +/* renamed into place. Specify quotes to protect special +/* characters and whitespace on the \fBpostconf\fR(1) command +/* line. +/* +/* The \fB-e\fR option is no longer needed with Postfix version +/* 2.8 and later. +/* .IP \fB-f\fR +/* Fold long lines when printing \fBmain.cf\fR or \fBmaster.cf\fR +/* configuration file entries, for human readability. +/* +/* This feature is available with Postfix 2.9 and later. +/* .IP \fB-F\fR +/* Show \fBmaster.cf\fR per-entry field settings (by default +/* all services and all fields), formatted as +/* "\fIservice/type/field=value\fR", one per line. Specify +/* \fB-Ff\fR to fold long lines. +/* +/* Specify one or more "\fIservice/type/field\fR" instances +/* on the \fBpostconf\fR(1) command line to limit the output +/* to fields of interest. Trailing parameter name or service +/* type fields that are omitted will be handled as "*" wildcard +/* fields. +/* +/* This feature is available with Postfix 2.11 and later. +/* .IP \fB-h\fR +/* Show parameter or attribute values without the "\fIname\fR +/* = " label that normally precedes the value. +/* .IP \fB-H\fR +/* Show parameter or attribute names without the " = \fIvalue\fR" +/* that normally follows the name. +/* +/* This feature is available with Postfix 3.1 and later. +/* .IP \fB-l\fR +/* List the names of all supported mailbox locking methods. +/* Postfix supports the following methods: +/* .RS +/* .IP \fBflock\fR +/* A kernel-based advisory locking method for local files only. +/* This locking method is available on systems with a BSD +/* compatible library. +/* .IP \fBfcntl\fR +/* A kernel-based advisory locking method for local and remote +/* files. +/* .IP \fBdotlock\fR +/* An application-level locking method. An application locks +/* a file named \fIfilename\fR by creating a file named +/* \fIfilename\fB.lock\fR. The application is expected to +/* remove its own lock file, as well as stale lock files that +/* were left behind after abnormal program termination. +/* .RE +/* .IP \fB-m\fR +/* List the names of all supported lookup table types. In +/* Postfix configuration files, lookup tables are specified +/* as \fItype\fB:\fIname\fR, where \fItype\fR is one of the +/* types listed below. The table \fIname\fR syntax depends on +/* the lookup table type as described in the DATABASE_README +/* document. +/* .RS +/* .IP \fBbtree\fR +/* A sorted, balanced tree structure. Available on systems +/* with support for Berkeley DB databases. +/* .IP \fBcdb\fR +/* A read-optimized structure with no support for incremental +/* updates. Available on systems with support for CDB databases. +/* +/* This feature is available with Postfix 2.2 and later. +/* .IP \fBcidr\fR +/* A table that associates values with Classless Inter-Domain +/* Routing (CIDR) patterns. This is described in \fBcidr_table\fR(5). +/* +/* This feature is available with Postfix 2.2 and later. +/* .IP \fBdbm\fR +/* An indexed file type based on hashing. Available on systems +/* with support for DBM databases. +/* .IP \fBenviron\fR +/* The UNIX process environment array. The lookup key is the +/* environment variable name; the table name is ignored. Originally +/* implemented for testing, someone may find this useful someday. +/* .IP \fBfail\fR +/* A table that reliably fails all requests. The lookup table +/* name is used for logging. This table exists to simplify +/* Postfix error tests. +/* +/* This feature is available with Postfix 2.9 and later. +/* .IP \fBhash\fR +/* An indexed file type based on hashing. Available on systems +/* with support for Berkeley DB databases. +/* .IP "\fBinline\fR (read-only)" +/* A non-shared, in-memory lookup table. Example: "\fBinline:{ +/* \fIkey\fB=\fIvalue\fB, { \fIkey\fB = \fItext with whitespace +/* or comma\fB }}\fR". Key-value pairs are separated by +/* whitespace or comma; with a key-value pair inside "\fB{}\fR", +/* whitespace is ignored after the opening "\fB{\fR", around +/* the "\fB=\fR" between key and value, and before the closing +/* "\fB}\fR". Inline tables eliminate the need to create a +/* database file for just a few fixed elements. See also the +/* \fIstatic:\fR map type. +/* +/* This feature is available with Postfix 3.0 and later. +/* .IP \fBinternal\fR +/* A non-shared, in-memory hash table. Its content are lost +/* when a process terminates. +/* .IP "\fBlmdb\fR" +/* OpenLDAP LMDB database (a memory-mapped, persistent file). +/* Available on systems with support for LMDB databases. This +/* is described in \fBlmdb_table\fR(5). +/* +/* This feature is available with Postfix 2.11 and later. +/* .IP "\fBldap\fR (read-only)" +/* LDAP database client. This is described in \fBldap_table\fR(5). +/* .IP "\fBmemcache\fR" +/* Memcache database client. This is described in +/* \fBmemcache_table\fR(5). +/* +/* This feature is available with Postfix 2.9 and later. +/* .IP "\fBmysql\fR (read-only)" +/* MySQL database client. Available on systems with support +/* for MySQL databases. This is described in \fBmysql_table\fR(5). +/* .IP "\fBpcre\fR (read-only)" +/* A lookup table based on Perl Compatible Regular Expressions. +/* The file format is described in \fBpcre_table\fR(5). +/* .IP "\fBpgsql\fR (read-only)" +/* PostgreSQL database client. This is described in +/* \fBpgsql_table\fR(5). +/* +/* This feature is available with Postfix 2.1 and later. +/* .IP "\fBpipemap\fR (read-only)" +/* A lookup table that constructs a pipeline of tables. Example: +/* "\fBpipemap:{\fItype_1:name_1, ..., type_n:name_n\fB}\fR". +/* Each "pipemap:" query is given to the first table. Each +/* lookup result becomes the query for the next table in the +/* pipeline, and the last table produces the final result. +/* When any table lookup produces no result, the pipeline +/* produces no result. The first and last characters of the +/* "pipemap:" table name must be "\fB{\fR" and "\fB}\fR". +/* Within these, individual maps are separated with comma or +/* whitespace. +/* +/* This feature is available with Postfix 3.0 and later. +/* .IP "\fBproxy\fR" +/* Postfix \fBproxymap\fR(8) client for shared access to Postfix +/* databases. The table name syntax is \fItype\fB:\fIname\fR. +/* +/* This feature is available with Postfix 2.0 and later. +/* .IP "\fBrandmap\fR (read-only)" +/* An in-memory table that performs random selection. Example: +/* "\fBrandmap:{\fIresult_1, ..., result_n\fB}\fR". Each table query +/* returns a random choice from the specified results. The first +/* and last characters of the "randmap:" table name must be +/* "\fB{\fR" and "\fB}\fR". Within these, individual results +/* are separated with comma or whitespace. To give a specific +/* result more weight, specify it multiple times. +/* +/* This feature is available with Postfix 3.0 and later. +/* .IP "\fBregexp\fR (read-only)" +/* A lookup table based on regular expressions. The file format +/* is described in \fBregexp_table\fR(5). +/* .IP \fBsdbm\fR +/* An indexed file type based on hashing. Available on systems +/* with support for SDBM databases. +/* +/* This feature is available with Postfix 2.2 and later. +/* .IP "\fBsocketmap\fR (read-only)" +/* Sendmail-style socketmap client. The table name is +/* \fBinet\fR:\fIhost\fR:\fIport\fR:\fIname\fR for a TCP/IP +/* server, or \fBunix\fR:\fIpathname\fR:\fIname\fR for a +/* UNIX-domain server. This is described in \fBsocketmap_table\fR(5). +/* +/* This feature is available with Postfix 2.10 and later. +/* .IP "\fBsqlite\fR (read-only)" +/* SQLite database. This is described in \fBsqlite_table\fR(5). +/* +/* This feature is available with Postfix 2.8 and later. +/* .IP "\fBstatic\fR (read-only)" +/* A table that always returns its name as lookup result. For +/* example, \fBstatic:foobar\fR always returns the string +/* \fBfoobar\fR as lookup result. Specify "\fBstatic:{ \fItext +/* with whitespace\fB }\fR" when the result contains whitespace; +/* this form ignores whitespace after the opening "\fB{\fR" +/* and before the closing +/* "\fB}\fR". See also the \fIinline:\fR map. +/* +/* The form "\fBstatic:{\fItext\fB}\fR is available with Postfix +/* 3.0 and later. +/* .IP "\fBtcp\fR (read-only)" +/* TCP/IP client. The protocol is described in \fBtcp_table\fR(5). +/* .IP "\fBtexthash\fR (read-only)" +/* Produces similar results as hash: files, except that you +/* don't need to run the \fBpostmap\fR(1) command before you +/* can use the file, and that it does not detect changes after +/* the file is read. +/* +/* This feature is available with Postfix 2.8 and later. +/* .IP "\fBunionmap\fR (read-only)" +/* A table that sends each query to multiple lookup tables and +/* that concatenates all found results, separated by comma. +/* The table name syntax is the same as for \fBpipemap\fR. +/* +/* This feature is available with Postfix 3.0 and later. +/* .IP "\fBunix\fR (read-only)" +/* A limited view of the UNIX authentication database. The +/* following tables are implemented: +/* .RS +/*. IP \fBunix:passwd.byname\fR +/* The table is the UNIX password database. The key is a login +/* name. The result is a password file entry in \fBpasswd\fR(5) +/* format. +/* .IP \fBunix:group.byname\fR +/* The table is the UNIX group database. The key is a group +/* name. The result is a group file entry in \fBgroup\fR(5) +/* format. +/* .RE +/* .RE +/* .IP +/* Other table types may exist depending on how Postfix was +/* built. +/* .IP \fB-M\fR +/* Show \fBmaster.cf\fR file contents instead of \fBmain.cf\fR +/* file contents. Specify \fB-Mf\fR to fold long lines for +/* human readability. +/* +/* Specify zero or more arguments, each with a \fIservice-name\fR +/* or \fIservice-name/service-type\fR pair, where \fIservice-name\fR +/* is the first field of a master.cf entry and \fIservice-type\fR +/* is one of (\fBinet\fR, \fBunix\fR, \fBfifo\fR, or \fBpass\fR). +/* +/* If \fIservice-name\fR or \fIservice-name/service-type\fR +/* is specified, only the matching master.cf entries will be +/* output. For example, "\fBpostconf -Mf smtp\fR" will output +/* all services named "smtp", and "\fBpostconf -Mf smtp/inet\fR" +/* will output only the smtp service that listens on the +/* network. Trailing service type fields that are omitted +/* will be handled as "*" wildcard fields. +/* +/* This feature is available with Postfix 2.9 and later. The +/* syntax was changed from "\fIname.type\fR" to "\fIname/type\fR", +/* and "*" wildcard support was added with Postfix 2.11. +/* .IP \fB-n\fR +/* Show only configuration parameters that have explicit +/* \fIname=value\fR settings in \fBmain.cf\fR. Specify \fB-nf\fR +/* to fold long lines for human readability (Postfix 2.9 and +/* later). To show settings that differ from built-in defaults +/* only, use the following bash syntax: +/* .nf +/* comm -23 <(postconf -n) <(postconf -d) +/* .fi +/* Replace "-23" with "-12" to show settings that duplicate +/* built-in defaults. +/* .IP "\fB-o \fIname=value\fR" +/* Override \fBmain.cf\fR parameter settings. +/* +/* This feature is available with Postfix 2.10 and later. +/* .IP \fB-p\fR +/* Show \fBmain.cf\fR parameter settings. This is the default. +/* +/* This feature is available with Postfix 2.11 and later. +/* .IP \fB-P\fR +/* Show \fBmaster.cf\fR service parameter settings (by default +/* all services and all parameters), formatted as +/* "\fIservice/type/parameter=value\fR", one per line. Specify +/* \fB-Pf\fR to fold long lines. +/* +/* Specify one or more "\fIservice/type/parameter\fR" instances +/* on the \fBpostconf\fR(1) command line to limit the output +/* to parameters of interest. Trailing parameter name or +/* service type fields that are omitted will be handled as "*" +/* wildcard fields. +/* +/* This feature is available with Postfix 2.11 and later. +/* .IP "\fB-t\fR [\fItemplate_file\fR]" +/* Display the templates for text that appears at the beginning +/* of delivery status notification (DSN) messages, without +/* expanding $\fBname\fR expressions. +/* +/* To override the \fBbounce_template_file\fR parameter setting, +/* specify a template file name at the end of the "\fBpostconf +/* -t\fR" command line. Specify an empty file name to display +/* built-in templates (in shell language: ""). +/* +/* This feature is available with Postfix 2.3 and later. +/* .IP "\fB-T \fImode\fR" +/* If Postfix is compiled without TLS support, the \fB-T\fR option +/* produces no output. Otherwise, if an invalid \fImode\fR is specified, +/* the \fB-T\fR option reports an error and exits with a non-zero status +/* code. The valid modes are: +/* .RS +/* .IP \fBcompile-version\fR +/* Output the OpenSSL version that Postfix was compiled with +/* (i.e. the OpenSSL version in a header file). The output +/* format is the same as with the command "\fBopenssl version\fR". +/* .IP \fBrun-version\fR +/* Output the OpenSSL version that Postfix is linked with at +/* runtime (i.e. the OpenSSL version in a shared library). +/* .IP \fBpublic-key-algorithms\fR +/* Output the lower-case names of the supported public-key +/* algorithms, one per-line. +/* .RE +/* .IP +/* This feature is available with Postfix 3.1 and later. +/* .IP \fB-v\fR +/* Enable verbose logging for debugging purposes. Multiple +/* \fB-v\fR options make the software increasingly verbose. +/* .IP \fB-x\fR +/* Expand \fI$name\fR in \fBmain.cf\fR or \fBmaster.cf\fR +/* parameter values. The expansion is recursive. +/* +/* This feature is available with Postfix 2.10 and later. +/* .IP \fB-X\fR +/* Edit the \fBmain.cf\fR configuration file, and remove the +/* parameters named on the \fBpostconf\fR(1) command line. +/* Specify a list of parameter names, not "\fIname=value\fR" +/* pairs. +/* +/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file, +/* and remove one or more service entries as specified with +/* "\fIservice/type\fR" on the \fBpostconf\fR(1) command line. +/* +/* With \fB-P\fR, edit the \fBmaster.cf\fR configuration file, +/* and remove one or more service parameter settings (-o +/* parameter=value settings) as specified with +/* "\fIservice/type/parameter\fR" on the \fBpostconf\fR(1) +/* command line. +/* +/* In all cases the file is copied to a temporary file then +/* renamed into place. Specify quotes to protect special +/* characters on the \fBpostconf\fR(1) command line. +/* +/* There is no \fBpostconf\fR(1) command to perform the reverse +/* operation. +/* +/* This feature is available with Postfix 2.10 and later. +/* Support for -M and -P was added with Postfix 2.11. +/* .IP \fB-#\fR +/* Edit the \fBmain.cf\fR configuration file, and comment out +/* the parameters named on the \fBpostconf\fR(1) command line, +/* so that those parameters revert to their default values. +/* Specify a list of parameter names, not "\fIname=value\fR" +/* pairs. +/* +/* With \fB-M\fR, edit the \fBmaster.cf\fR configuration file, +/* and comment out one or more service entries as specified +/* with "\fIservice/type\fR" on the \fBpostconf\fR(1) command +/* line. +/* +/* In all cases the file is copied to a temporary file then +/* renamed into place. Specify quotes to protect special +/* characters on the \fBpostconf\fR(1) command line. +/* +/* There is no \fBpostconf\fR(1) command to perform the reverse +/* operation. +/* +/* This feature is available with Postfix 2.6 and later. Support +/* for -M was added with Postfix 2.11. +/* DIAGNOSTICS +/* Problems are reported to the standard error stream. +/* ENVIRONMENT +/* .ad +/* .fi +/* .IP \fBMAIL_CONFIG\fR +/* Directory with Postfix configuration files. +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* The following \fBmain.cf\fR parameters are especially +/* relevant to this program. +/* +/* The text below provides only a parameter summary. See +/* \fBpostconf\fR(5) for more details including examples. +/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" +/* The default location of the Postfix main.cf and master.cf +/* configuration files. +/* .IP "\fBbounce_template_file (empty)\fR" +/* Pathname of a configuration file with bounce message templates. +/* FILES +/* /etc/postfix/main.cf, Postfix configuration parameters +/* /etc/postfix/master.cf, Postfix master daemon configuration +/* SEE ALSO +/* bounce(5), bounce template file format +/* master(5), master.cf configuration file syntax +/* postconf(5), main.cf configuration file syntax +/* README FILES +/* .ad +/* .fi +/* Use "\fBpostconf readme_directory\fR" or "\fBpostconf +/* html_directory\fR" to locate this information. +/* .na +/* .nf +/* DATABASE_README, Postfix lookup table overview +/* 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 <sys/stat.h> +#include <stdlib.h> + +/* Utility library. */ + +#include <msg.h> +#include <msg_vstream.h> +#include <dict.h> +#include <htable.h> +#include <vstring.h> +#include <vstream.h> +#include <stringops.h> +#include <name_mask.h> +#include <warn_stat.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_conf.h> +#include <mail_version.h> +#include <mail_run.h> +#include <mail_dict.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * Global storage. See postconf.h for description. + */ +PCF_PARAM_TABLE *pcf_param_table; +PCF_MASTER_ENT *pcf_master_table; +int pcf_cmd_mode = PCF_DEF_MODE; + + /* + * Application fingerprinting. + */ +MAIL_VERSION_STAMP_DECLARE; + + /* + * This program has so many command-line options that we have to implement a + * compatibility matrix to weed out the conflicting option combinations, and + * to alert the user about option combinations that have no effect. + */ + + /* + * Options that are mutually-exclusive. First entry must specify the major + * modes. Other entries specify conflicts between option modifiers. + */ +static const int pcf_incompat_options[] = { + /* Major modes. */ + PCF_SHOW_SASL_SERV | PCF_SHOW_SASL_CLNT | PCF_EXP_DSN_TEMPL \ + |PCF_SHOW_LOCKS | PCF_SHOW_MAPS | PCF_DUMP_DSN_TEMPL | PCF_MAIN_PARAM \ + |PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM | PCF_SHOW_TLS, + /* Modifiers. */ + PCF_SHOW_DEFS | PCF_EDIT_CONF | PCF_SHOW_NONDEF | PCF_COMMENT_OUT \ + |PCF_EDIT_EXCL, + PCF_FOLD_LINE | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL, + PCF_SHOW_EVAL | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL, + PCF_MAIN_OVER | PCF_SHOW_DEFS | PCF_EDIT_CONF | PCF_COMMENT_OUT \ + |PCF_EDIT_EXCL, + PCF_HIDE_NAME | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL \ + |PCF_HIDE_VALUE, + 0, +}; + + /* + * Options, and the only options that they are compatible with. There must + * be one entry for each major mode. Other entries specify compatibility + * between option modifiers. + */ +static const int pcf_compat_options[][2] = { + /* Major modes. */ + {PCF_SHOW_SASL_SERV, 0}, + {PCF_SHOW_SASL_CLNT, 0}, + {PCF_EXP_DSN_TEMPL, 0}, + {PCF_SHOW_LOCKS, 0}, + {PCF_SHOW_MAPS, 0,}, + {PCF_SHOW_TLS, 0,}, + {PCF_DUMP_DSN_TEMPL, 0}, + {PCF_MAIN_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \ + |PCF_FOLD_LINE | PCF_HIDE_NAME | PCF_PARAM_CLASS \ + |PCF_SHOW_EVAL | PCF_SHOW_DEFS | PCF_SHOW_NONDEF \ + |PCF_MAIN_OVER | PCF_HIDE_VALUE)}, + {PCF_MASTER_ENTRY, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT \ + |PCF_FOLD_LINE | PCF_MAIN_OVER | PCF_SHOW_EVAL)}, + {PCF_MASTER_FLD, (PCF_EDIT_CONF | PCF_FOLD_LINE | PCF_HIDE_NAME \ + |PCF_MAIN_OVER | PCF_SHOW_EVAL | PCF_HIDE_VALUE)}, + {PCF_MASTER_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_FOLD_LINE \ + |PCF_HIDE_NAME | PCF_MAIN_OVER | PCF_SHOW_EVAL \ + |PCF_HIDE_VALUE)}, + /* Modifiers. */ + {PCF_PARAM_CLASS, (PCF_MAIN_PARAM | PCF_SHOW_DEFS | PCF_SHOW_NONDEF)}, + 0, +}; + + /* + * Compatibility to string conversion support. + */ +static const NAME_MASK pcf_compat_names[] = { + "-a", PCF_SHOW_SASL_SERV, + "-A", PCF_SHOW_SASL_CLNT, + "-b", PCF_EXP_DSN_TEMPL, + "-C", PCF_PARAM_CLASS, + "-d", PCF_SHOW_DEFS, + "-e", PCF_EDIT_CONF, + "-f", PCF_FOLD_LINE, + "-F", PCF_MASTER_FLD, + "-h", PCF_HIDE_NAME, + "-H", PCF_HIDE_VALUE, + "-l", PCF_SHOW_LOCKS, + "-m", PCF_SHOW_MAPS, + "-M", PCF_MASTER_ENTRY, + "-n", PCF_SHOW_NONDEF, + "-o", PCF_MAIN_OVER, + "-p", PCF_MAIN_PARAM, + "-P", PCF_MASTER_PARAM, + "-t", PCF_DUMP_DSN_TEMPL, + "-T", PCF_SHOW_TLS, + "-x", PCF_SHOW_EVAL, + "-X", PCF_EDIT_EXCL, + "-#", PCF_COMMENT_OUT, + 0, +}; + +/* usage - enumerate parameters without compatibility info */ + +static void usage(const char *progname) +{ + msg_fatal("usage: %s" + " [-a (server SASL types)]" + " [-A (client SASL types)]" + " [-b (bounce templates)]" + " [-c config_dir]" + " [-c param_class]" + " [-d (parameter defaults)]" + " [-e (edit configuration)]" + " [-f (fold lines)]" + " [-F (master.cf fields)]" + " [-h (no names)]" + " [-H (no values)]" + " [-l (lock types)]" + " [-m (map types)]" + " [-M (master.cf)]" + " [-n (non-default parameters)]" + " [-o name=value (override parameter value)]" + " [-p (main.cf, default)]" + " [-P (master.cf parameters)]" + " [-t (bounce templates)]" + " [-T compile-version|run-version|public-key-algorithms]" + " [-v (verbose)]" + " [-x (expand parameter values)]" + " [-X (exclude)]" + " [-# (comment-out)]" + " [name...]", progname); +} + +/* pcf_check_exclusive_options - complain about mutually-exclusive options */ + +static void pcf_check_exclusive_options(int optval) +{ + const char *myname = "pcf_check_exclusive_options"; + const int *op; + int oval; + unsigned mask; + + for (op = pcf_incompat_options; (oval = *op) != 0; op++) { + oval &= optval; + for (mask = ~0U; (mask & oval) != 0; mask >>= 1) { + if ((mask & oval) != oval) + msg_fatal("specify one of %s", + str_name_mask(myname, pcf_compat_names, oval)); + } + } +} + +/* pcf_check_compat_options - complain about incompatible options */ + +static void pcf_check_compat_options(int optval) +{ + const char *myname = "pcf_check_compat_options"; + VSTRING *buf1 = vstring_alloc(10); + VSTRING *buf2 = vstring_alloc(10); + const int (*op)[2]; + int excess; + + for (op = pcf_compat_options; op[0][0] != 0; op++) { + if ((optval & *op[0]) != 0 + && (excess = (optval & ~((*op)[0] | (*op)[1]))) != 0) + msg_fatal("with option %s, do not specify %s", + str_name_mask_opt(buf1, myname, pcf_compat_names, + (*op)[0], NAME_MASK_NUMBER), + str_name_mask_opt(buf2, myname, pcf_compat_names, + excess, NAME_MASK_NUMBER)); + } + vstring_free(buf1); + vstring_free(buf2); +} + +/* main */ + +int main(int argc, char **argv) +{ + int ch; + int fd; + struct stat st; + ARGV *ext_argv = 0; + int param_class = PCF_PARAM_MASK_CLASS; + static const NAME_MASK param_class_table[] = { + "builtin", PCF_PARAM_FLAG_BUILTIN, + "service", PCF_PARAM_FLAG_SERVICE, + "user", PCF_PARAM_FLAG_USER, + "all", PCF_PARAM_MASK_CLASS, + 0, + }; + ARGV *override_params = 0; + const char *pcf_tls_arg = 0; + + /* + * Fingerprint executables and core dumps. + */ + MAIL_VERSION_STAMP_ALLOCATE; + + /* + * Be consistent with file permissions. + */ + umask(022); + + /* + * To minimize confusion, make sure that the standard file descriptors + * are open before opening anything else. XXX Work around for 44BSD where + * fstat can return EBADF on an open file descriptor. + */ + for (fd = 0; fd < 3; fd++) + if (fstat(fd, &st) == -1 + && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) + msg_fatal("open /dev/null: %m"); + + /* + * Set up logging. + */ + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "aAbc:C:deEfFhHlmMno:pPtT:vxX#")) > 0) { + switch (ch) { + case 'a': + pcf_cmd_mode |= PCF_SHOW_SASL_SERV; + break; + case 'A': + pcf_cmd_mode |= PCF_SHOW_SASL_CLNT; + break; + case 'b': + pcf_cmd_mode |= PCF_EXP_DSN_TEMPL; + if (ext_argv) + msg_fatal("specify one of -b and -t"); + ext_argv = argv_alloc(2); + argv_add(ext_argv, "bounce", "-SVnexpand_templates", (char *) 0); + break; + case 'c': + if (setenv(CONF_ENV_PATH, optarg, 1) < 0) + msg_fatal("out of memory"); + break; + case 'C': + param_class = name_mask_opt("-C option", param_class_table, + optarg, NAME_MASK_ANY_CASE | NAME_MASK_FATAL); + break; + case 'd': + pcf_cmd_mode |= PCF_SHOW_DEFS; + break; + case 'e': + pcf_cmd_mode |= PCF_EDIT_CONF; + break; + case 'f': + pcf_cmd_mode |= PCF_FOLD_LINE; + break; + case 'F': + pcf_cmd_mode |= PCF_MASTER_FLD; + break; + case '#': + pcf_cmd_mode |= PCF_COMMENT_OUT; + break; + case 'h': + pcf_cmd_mode |= PCF_HIDE_NAME; + break; + case 'H': + pcf_cmd_mode |= PCF_HIDE_VALUE; + break; + case 'l': + pcf_cmd_mode |= PCF_SHOW_LOCKS; + break; + case 'm': + pcf_cmd_mode |= PCF_SHOW_MAPS; + break; + case 'M': + pcf_cmd_mode |= PCF_MASTER_ENTRY; + break; + case 'n': + pcf_cmd_mode |= PCF_SHOW_NONDEF; + break; + case 'o': + pcf_cmd_mode |= PCF_MAIN_OVER; + if (override_params == 0) + override_params = argv_alloc(2); + argv_add(override_params, optarg, (char *) 0); + break; + case 'p': + pcf_cmd_mode |= PCF_MAIN_PARAM; + break; + case 'P': + pcf_cmd_mode |= PCF_MASTER_PARAM; + break; + case 't': + pcf_cmd_mode |= PCF_DUMP_DSN_TEMPL; + if (ext_argv) + msg_fatal("specify one of -b and -t"); + ext_argv = argv_alloc(2); + argv_add(ext_argv, "bounce", "-SVndump_templates", (char *) 0); + break; + case 'T': + if (pcf_cmd_mode & PCF_SHOW_TLS) + msg_fatal("At most one -T <mode> option may be specified"); + pcf_cmd_mode |= PCF_SHOW_TLS; + pcf_tls_arg = optarg; + break; + case 'x': + pcf_cmd_mode |= PCF_SHOW_EVAL; + break; + case 'X': + /* This is irreversible, therefore require two-finger action. */ + pcf_cmd_mode |= PCF_EDIT_EXCL; + break; + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + + /* + * We don't enforce import_environment consistency in this program. + * + * We don't extract import_environment from main.cf, because the postconf + * command must be able to extract parameter settings from main.cf before + * all installation parameters such as mail_owner or setgid_group have a + * legitimate value. + * + * We would need the functionality of mail_params_init() including all the + * side effects of populating the CONFIG_DICT with default values so that + * $name expansion works correctly, but excluding all the parameter value + * sanity checks so that it would not abort at installation time. + */ + + /* + * Make all options explicit, before checking their compatibility. + */ +#define PCF_MAIN_OR_MASTER \ + (PCF_MAIN_PARAM | PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM) + + if ((pcf_cmd_mode & pcf_incompat_options[0]) == 0) + pcf_cmd_mode |= PCF_MAIN_PARAM; + if ((pcf_cmd_mode & PCF_MAIN_OR_MASTER) + && argv[optind] && strchr(argv[optind], '=')) + pcf_cmd_mode |= PCF_EDIT_CONF; + + /* + * Sanity check. + */ + pcf_check_exclusive_options(pcf_cmd_mode); + pcf_check_compat_options(pcf_cmd_mode); + + if ((pcf_cmd_mode & PCF_EDIT_CONF) && argc == optind) + msg_fatal("-e requires name=value argument"); + + /* + * Display bounce template information and exit. + */ + if (ext_argv) { + if (argv[optind]) { + if (argv[optind + 1]) + msg_fatal("options -b and -t require at most one template file"); + argv_add(ext_argv, "-o", + concatenate(VAR_BOUNCE_TMPL, "=", + argv[optind], (char *) 0), + (char *) 0); + } + /* Grr... */ + argv_add(ext_argv, "-o", + concatenate(VAR_QUEUE_DIR, "=", ".", (char *) 0), + (char *) 0); + mail_conf_read(); + mail_run_replace(var_daemon_dir, ext_argv->argv); + /* NOTREACHED */ + } + + /* + * If showing map types, show them and exit + */ + if (pcf_cmd_mode & PCF_SHOW_MAPS) { + mail_conf_read(); + mail_dict_init(); + pcf_show_maps(); + } + + /* + * If showing locking methods, show them and exit + */ + else if (pcf_cmd_mode & PCF_SHOW_LOCKS) { + pcf_show_locks(); + } + + /* + * If showing master.cf entries, show them and exit + */ + else if ((pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)) + && !(pcf_cmd_mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_COMMENT_OUT))) { + pcf_read_master(PCF_FAIL_ON_OPEN_ERROR); + pcf_read_parameters(); + if (override_params) + pcf_set_parameters(override_params->argv); + pcf_register_builtin_parameters(basename(argv[0]), getpid()); + pcf_register_service_parameters(); + pcf_register_user_parameters(); + if (pcf_cmd_mode & PCF_MASTER_FLD) + pcf_show_master_fields(VSTREAM_OUT, pcf_cmd_mode, argc - optind, + argv + optind); + else if (pcf_cmd_mode & PCF_MASTER_PARAM) + pcf_show_master_params(VSTREAM_OUT, pcf_cmd_mode, argc - optind, + argv + optind); + else + pcf_show_master_entries(VSTREAM_OUT, pcf_cmd_mode, argc - optind, + argv + optind); + pcf_flag_unused_master_parameters(); + } + + /* + * If showing SASL plug-in types, show them and exit + */ + else if (pcf_cmd_mode & PCF_SHOW_SASL_SERV) { + pcf_show_sasl(PCF_SHOW_SASL_SERV); + } else if (pcf_cmd_mode & PCF_SHOW_SASL_CLNT) { + pcf_show_sasl(PCF_SHOW_SASL_CLNT); + } + + /* + * Show TLS info and exit. + */ + else if (pcf_cmd_mode & PCF_SHOW_TLS) { + pcf_show_tls(pcf_tls_arg); + } + + /* + * Edit main.cf or master.cf. + */ + else if (pcf_cmd_mode & (PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { + if (optind == argc) + msg_fatal("missing service argument"); + if (pcf_cmd_mode & (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)) { + pcf_edit_master(pcf_cmd_mode, argc - optind, argv + optind); + } else { + pcf_edit_main(pcf_cmd_mode, argc - optind, argv + optind); + } + } + + /* + * If showing non-default values, read main.cf. + */ + else { + if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) { + pcf_read_parameters(); + if (override_params) + pcf_set_parameters(override_params->argv); + } + pcf_register_builtin_parameters(basename(argv[0]), getpid()); + + /* + * Add service-dependent parameters (service names from master.cf) + * and user-defined parameters ($name macros in parameter values in + * main.cf and master.cf, but only if those names have a name=value + * in main.cf or master.cf). + */ + pcf_read_master(PCF_WARN_ON_OPEN_ERROR); + pcf_register_service_parameters(); + if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) + pcf_register_user_parameters(); + + /* + * Show the requested values. + */ + pcf_show_parameters(VSTREAM_OUT, pcf_cmd_mode, param_class, + argv + optind); + + /* + * Flag unused parameters. This makes no sense with "postconf -d", + * because that ignores all the user-specified parameters and + * user-specified macro expansions in main.cf. + */ + if ((pcf_cmd_mode & PCF_SHOW_DEFS) == 0) { + pcf_flag_unused_main_parameters(); + pcf_flag_unused_master_parameters(); + } + } + vstream_fflush(VSTREAM_OUT); + exit(0); +} diff --git a/src/postconf/postconf.h b/src/postconf/postconf.h new file mode 100644 index 0000000..0f47648 --- /dev/null +++ b/src/postconf/postconf.h @@ -0,0 +1,327 @@ +/*++ +/* NAME +/* postconf 3h +/* SUMMARY +/* module interfaces +/* SYNOPSIS +/* #include <postconf.h> +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include <unistd.h> + + /* + * Utility library. + */ +#include <htable.h> +#include <argv.h> +#include <dict.h> +#include <name_code.h> + + /* + * What we're supposed to be doing. + */ +#define PCF_SHOW_NONDEF (1<<0) /* show main.cf non-default settings */ +#define PCF_SHOW_DEFS (1<<1) /* show main.cf default setting */ +#define PCF_HIDE_NAME (1<<2) /* hide main.cf parameter name */ +#define PCF_SHOW_MAPS (1<<3) /* show map types */ +#define PCF_EDIT_CONF (1<<4) /* edit main.cf or master.cf */ +#define PCF_SHOW_LOCKS (1<<5) /* show mailbox lock methods */ +#define PCF_SHOW_EVAL (1<<6) /* expand main.cf right-hand sides */ +#define PCF_SHOW_SASL_SERV (1<<7) /* show server auth plugin types */ +#define PCF_SHOW_SASL_CLNT (1<<8) /* show client auth plugin types */ +#define PCF_COMMENT_OUT (1<<9) /* #-out selected main.cf entries */ +#define PCF_MASTER_ENTRY (1<<10) /* manage master.cf entries */ +#define PCF_FOLD_LINE (1<<11) /* fold long *.cf entries */ +#define PCF_EDIT_EXCL (1<<12) /* exclude main.cf entries */ +#define PCF_MASTER_FLD (1<<13) /* hierarchical pathname */ +#define PCF_MAIN_PARAM (1<<14) /* manage main.cf entries */ +#define PCF_EXP_DSN_TEMPL (1<<15) /* expand bounce templates */ +#define PCF_PARAM_CLASS (1<<16) /* select parameter class */ +#define PCF_MAIN_OVER (1<<17) /* override parameter values */ +#define PCF_DUMP_DSN_TEMPL (1<<18) /* show bounce templates */ +#define PCF_MASTER_PARAM (1<<19) /* manage master.cf -o name=value */ +#define PCF_HIDE_VALUE (1<<20) /* hide main.cf/master.cf =value */ +#define PCF_SHOW_TLS (1<<21) /* TLS support introspection */ + +#define PCF_DEF_MODE 0 + + /* + * Structure for one "valid parameter" (built-in, service-defined or valid + * user-defined). See the postconf_builtin, postconf_service and + * postconf_user modules for narrative text. + */ +typedef struct { + int flags; /* see below */ + void *param_data; /* mostly, the default value */ + const char *(*convert_fn) (void *); /* value to string */ +} PCF_PARAM_NODE; + + /* Values for flags. See the postconf_node module for narrative text. */ +#define PCF_PARAM_FLAG_RAW (1<<0) /* raw parameter value */ +#define PCF_PARAM_FLAG_BUILTIN (1<<1) /* built-in parameter name */ +#define PCF_PARAM_FLAG_SERVICE (1<<2) /* service-defined parameter name */ +#define PCF_PARAM_FLAG_USER (1<<3) /* user-defined parameter name */ +#define PCF_PARAM_FLAG_LEGACY (1<<4) /* legacy parameter name */ +#define PCF_PARAM_FLAG_READONLY (1<<5) /* legacy parameter name */ +#define PCF_PARAM_FLAG_DBMS (1<<6) /* dbms-defined parameter name */ + +#define PCF_PARAM_MASK_CLASS \ + (PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_SERVICE | PCF_PARAM_FLAG_USER) +#define PCF_PARAM_CLASS_OVERRIDE(node, class) \ + ((node)->flags = (((node)->flags & ~PCF_PARAM_MASK_CLASS) | (class))) + +#define PCF_RAW_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_RAW) +#define PCF_BUILTIN_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_BUILTIN) +#define PCF_SERVICE_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_SERVICE) +#define PCF_USER_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_USER) +#define PCF_LEGACY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_LEGACY) +#define PCF_READONLY_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_READONLY) +#define PCF_DBMS_PARAMETER(node) ((node)->flags & PCF_PARAM_FLAG_DBMS) + + /* Values for param_data. See postconf_node module for narrative text. */ +#define PCF_PARAM_NO_DATA ((char *) 0) + + /* + * Lookup table for global "valid parameter" information. + */ +#define PCF_PARAM_TABLE HTABLE +#define PCF_PARAM_INFO HTABLE_INFO + +extern PCF_PARAM_TABLE *pcf_param_table; + + /* + * postconf_node.c. + */ +#define PCF_PARAM_TABLE_CREATE(size) htable_create(size); +#define PCF_PARAM_NODE_CAST(ptr) ((PCF_PARAM_NODE *) (ptr)) + +#define PCF_PARAM_TABLE_LIST(table) htable_list(table) +#define PCF_PARAM_INFO_NAME(ht) ((const char *) (ht)->key) +#define PCF_PARAM_INFO_NODE(ht) PCF_PARAM_NODE_CAST((ht)->value) + +#define PCF_PARAM_TABLE_FIND(table, name) \ + PCF_PARAM_NODE_CAST(htable_find((table), (name))) +#define PCF_PARAM_TABLE_LOCATE(table, name) htable_locate((table), (name)) +#define PCF_PARAM_TABLE_ENTER(table, name, flags, data, func) \ + htable_enter((table), (name), (char *) pcf_make_param_node((flags), \ + (data), (func))) + +extern PCF_PARAM_NODE *pcf_make_param_node(int, void *, const char *(*) (void *)); +extern const char *pcf_convert_param_node(int, const char *, PCF_PARAM_NODE *); +extern VSTRING *pcf_param_string_buf; + + /* + * Structure of one master.cf entry. + */ +typedef struct { + char *name_space; /* service/type, parameter name space */ + ARGV *argv; /* null, or master.cf fields */ + DICT *all_params; /* null, or all name=value entries */ + DICT *ro_params; /* read-only name=value entries */ + HTABLE *valid_names; /* null, or "valid" parameter names */ +} PCF_MASTER_ENT; + +#define PCF_MASTER_MIN_FIELDS 8 /* mandatory field minimum */ + +#define PCF_MASTER_NAME_SERVICE "service" +#define PCF_MASTER_NAME_TYPE "type" +#define PCF_MASTER_NAME_PRIVATE "private" +#define PCF_MASTER_NAME_UNPRIV "unprivileged" +#define PCF_MASTER_NAME_CHROOT "chroot" +#define PCF_MASTER_NAME_WAKEUP "wakeup" +#define PCF_MASTER_NAME_MAXPROC "process_limit" +#define PCF_MASTER_NAME_CMD "command" + +#define PCF_MASTER_FLD_SERVICE 0 /* service name */ +#define PCF_MASTER_FLD_TYPE 1 /* service type */ +#define PCF_MASTER_FLD_PRIVATE 2 /* private service */ +#define PCF_MASTER_FLD_UNPRIV 3 /* unprivileged service */ +#define PCF_MASTER_FLD_CHROOT 4 /* chrooted service */ +#define PCF_MASTER_FLD_WAKEUP 5 /* wakeup timer */ +#define PCF_MASTER_FLD_MAXPROC 6 /* process limit */ +#define PCF_MASTER_FLD_CMD 7 /* command */ + +#define PCF_MASTER_FLD_WILDC -1 /* wild-card */ +#define PCF_MASTER_FLD_NONE -2 /* not available */ + + /* + * Lookup table for master.cf entries. The table is terminated with an entry + * that has a null argv member. + */ +PCF_MASTER_ENT *pcf_master_table; + + /* + * Line-wrapping support. + */ +#define PCF_LINE_LIMIT 80 /* try to fold longer lines */ +#define PCF_SEPARATORS " \t\r\n" +#define PCF_INDENT_LEN 4 /* indent long text by 4 */ +#define PCF_INDENT_TEXT " " + + /* + * XXX Global so that postconf_builtin.c call-backs can see it. + */ +extern int pcf_cmd_mode; + + /* + * postconf_misc.c. + */ +extern void pcf_set_config_dir(void); + + /* + * postconf_main.c + */ +extern void pcf_read_parameters(void); +extern void pcf_set_parameters(char **); +extern void pcf_show_parameters(VSTREAM *, int, int, char **); + + /* + * postconf_edit.c + */ +extern void pcf_edit_main(int, int, char **); +extern void pcf_edit_master(int, int, char **); + + /* + * postconf_master.c. + */ +extern const char pcf_daemon_options_expecting_value[]; +extern void pcf_read_master(int); +extern void pcf_show_master_entries(VSTREAM *, int, int, char **); +extern const char *pcf_parse_master_entry(PCF_MASTER_ENT *, const char *); +extern void pcf_print_master_entry(VSTREAM *, int, PCF_MASTER_ENT *); +extern void pcf_free_master_entry(PCF_MASTER_ENT *); +extern void pcf_show_master_fields(VSTREAM *, int, int, char **); +extern void pcf_edit_master_field(PCF_MASTER_ENT *, int, const char *); +extern void pcf_show_master_params(VSTREAM *, int, int, char **); +extern void pcf_edit_master_param(PCF_MASTER_ENT *, int, const char *, const char *); + +#define PCF_WARN_ON_OPEN_ERROR 0 +#define PCF_FAIL_ON_OPEN_ERROR 1 + +#define PCF_MASTER_BLANKS " \t\r\n" /* XXX */ + + /* + * Master.cf parameter namespace management. The idea is to manage master.cf + * "-o name=value" settings with other tools than text editors. + * + * The natural choice is to use "service-name.service-type.parameter-name", but + * unfortunately the '.' may appear in service and parameter names. + * + * For example, a spawn(8) listener can have a service name 127.0.0.1:10028. + * This service name becomes part of a service-dependent parameter name + * "127.0.0.1:10028_time_limit". All those '.' characters mean we can't use + * '.' as the parameter namespace delimiter. + * + * (We could require that such service names are specified as $foo:port with + * the value of "foo" defined in main.cf or at the top of master.cf.) + * + * But it is easier if we use '/' instead. + */ +#define PCF_NAMESP_SEP_CH '/' +#define PCF_NAMESP_SEP_STR "/" + +#define PCF_LEGACY_SEP_CH '.' + + /* + * postconf_match.c. + */ +#define PCF_MATCH_WILDC_STR "*" +#define PCF_MATCH_ANY(p) ((p)[0] == PCF_MATCH_WILDC_STR[0] && (p)[1] == 0) +#define PCF_MATCH_STRING(p, s) (PCF_MATCH_ANY(p) || strcmp((p), (s)) == 0) + +extern ARGV *pcf_parse_service_pattern(const char *, int, int); +extern int pcf_parse_field_pattern(const char *); + +#define PCF_IS_MAGIC_SERVICE_PATTERN(pat) \ + (PCF_MATCH_ANY((pat)->argv[0]) || PCF_MATCH_ANY((pat)->argv[1])) +#define PCF_MATCH_SERVICE_PATTERN(pat, name, type) \ + (PCF_MATCH_STRING((pat)->argv[0], (name)) \ + && PCF_MATCH_STRING((pat)->argv[1], (type))) + +#define pcf_is_magic_field_pattern(pat) ((pat) == PCF_MASTER_FLD_WILDC) +#define pcf_str_field_pattern(pat) ((const char *) (pcf_field_name_offset[pat].name)) + +#define PCF_IS_MAGIC_PARAM_PATTERN(pat) PCF_MATCH_ANY(pat) +#define PCF_MATCH_PARAM_PATTERN(pat, name) PCF_MATCH_STRING((pat), (name)) + +/* The following is not part of the postconf_match API. */ +extern NAME_CODE pcf_field_name_offset[]; + + /* + * postconf_builtin.c. + */ +extern void pcf_register_builtin_parameters(const char *, pid_t); + + /* + * postconf_service.c. + */ +extern void pcf_register_service_parameters(void); + + /* + * Parameter context structure. + */ +typedef struct { + PCF_MASTER_ENT *local_scope; + int param_class; +} PCF_PARAM_CTX; + + /* + * postconf_user.c. + */ +extern void pcf_register_user_parameters(void); + + /* + * postconf_dbms.c + */ +extern void pcf_register_dbms_parameters(const char *, + const char *(*) (const char *, int, PCF_MASTER_ENT *), + PCF_MASTER_ENT *); + + /* + * postconf_lookup.c. + */ +extern const char *pcf_lookup_parameter_value(int, const char *, + PCF_MASTER_ENT *, + PCF_PARAM_NODE *); + +extern char *pcf_expand_parameter_value(VSTRING *, int, const char *, + PCF_MASTER_ENT *); + + /* + * postconf_print.c. + */ +extern void PRINTFLIKE(3, 4) pcf_print_line(VSTREAM *, int, const char *,...); + + /* + * postconf_unused.c. + */ +extern void pcf_flag_unused_main_parameters(void); +extern void pcf_flag_unused_master_parameters(void); + + /* + * postconf_other.c. + */ +extern void pcf_show_maps(void); +extern void pcf_show_locks(void); +extern void pcf_show_sasl(int); +extern void pcf_show_tls(const char *); + +/* 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 +/*--*/ diff --git a/src/postconf/postconf_builtin.c b/src/postconf/postconf_builtin.c new file mode 100644 index 0000000..e1a55fa --- /dev/null +++ b/src/postconf/postconf_builtin.c @@ -0,0 +1,461 @@ +/*++ +/* NAME +/* postconf_builtin 3 +/* SUMMARY +/* built-in main.cf parameter support +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_register_builtin_parameters(procname, pid) +/* const char *procname; +/* pid_t pid; +/* DESCRIPTION +/* pcf_register_builtin_parameters() initializes the global +/* main.cf parameter name space and adds all built-in parameter +/* information. +/* +/* Arguments: +/*.IP procname +/* Provides the default value for the "process_name" parameter. +/*.IP pid +/* Provides the default value for the "process_id" parameter. +/* 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> + +#ifdef USE_PATHS_H +#include <paths.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <htable.h> +#include <vstring.h> +#include <get_hostname.h> +#include <stringops.h> + +/* Global library. */ + +#include <mynetworks.h> +#include <mail_conf.h> +#include <mail_params.h> +#include <mail_version.h> +#include <mail_proto.h> +#include <mail_addr.h> +#include <inet_proto.h> +#include <server_acl.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * Support for built-in parameters: declarations generated by scanning + * actual C source files. + */ +#include "time_vars.h" +#include "bool_vars.h" +#include "int_vars.h" +#include "str_vars.h" +#include "raw_vars.h" +#include "nint_vars.h" +#include "nbool_vars.h" +#include "long_vars.h" + + /* + * Support for built-in parameters: manually extracted. + */ +#include "install_vars.h" + + /* + * Support for built-in parameters: lookup tables generated by scanning + * actual C source files. + */ +static const CONFIG_TIME_TABLE pcf_time_table[] = { +#include "time_table.h" + 0, +}; + +static const CONFIG_BOOL_TABLE pcf_bool_table[] = { +#include "bool_table.h" + 0, +}; + +static const CONFIG_INT_TABLE pcf_int_table[] = { +#include "int_table.h" + 0, +}; + +static const CONFIG_STR_TABLE pcf_str_table[] = { +#include "str_table.h" +#include "install_table.h" + 0, +}; + +static const CONFIG_RAW_TABLE pcf_raw_table[] = { +#include "raw_table.h" + 0, +}; + +static const CONFIG_NINT_TABLE pcf_nint_table[] = { +#include "nint_table.h" + 0, +}; + +static const CONFIG_NBOOL_TABLE pcf_nbool_table[] = { +#include "nbool_table.h" + 0, +}; + +static const CONFIG_LONG_TABLE pcf_long_table[] = { +#include "long_table.h" + 0, +}; + + /* + * Legacy parameters for backwards compatibility. + */ +static const CONFIG_STR_TABLE pcf_legacy_str_table[] = { + {"virtual_maps", ""}, + {"fallback_relay", ""}, + {"authorized_verp_clients", ""}, + {"smtpd_client_connection_limit_exceptions", ""}, + {"postscreen_dnsbl_ttl", ""}, + 0, +}; + + /* + * Parameters whose default values are normally initialized by calling a + * function. We direct the calls to our own versions of those functions + * because the run-time conditions are slightly different. + * + * Important: if the evaluation of a parameter default value has any side + * effects, then those side effects must happen only once. + */ +static const char *pcf_check_myhostname(void); +static const char *pcf_check_mydomainname(void); +static const char *pcf_mynetworks(void); + +#include "str_fn_vars.h" + +static const CONFIG_STR_FN_TABLE pcf_str_fn_table[] = { +#include "str_fn_table.h" + 0, +}; + + /* + * Parameters whose default values are normally initialized by ad-hoc code. + * The AWK script cannot identify these parameters or values, so we provide + * our own. + * + * Important: if the evaluation of a parameter default value has any side + * effects, then those side effects must happen only once. + */ +static CONFIG_STR_TABLE pcf_adhoc_procname = {VAR_PROCNAME}; +static CONFIG_STR_TABLE pcf_adhoc_servname = {VAR_SERVNAME}; +static CONFIG_INT_TABLE pcf_adhoc_pid = {VAR_PID}; + +#define STR(x) vstring_str(x) + +/* pcf_check_myhostname - lookup hostname and validate */ + +static const char *pcf_check_myhostname(void) +{ + static const char *name; + const char *dot; + const char *domain; + + /* + * Use cached result. + */ + if (name) + return (name); + + /* + * If the local machine name is not in FQDN form, try to append the + * contents of $mydomain. + */ + name = get_hostname(); + if ((dot = strchr(name, '.')) == 0) { + if ((domain = mail_conf_lookup_eval(VAR_MYDOMAIN)) == 0) + domain = DEF_MYDOMAIN; + name = concatenate(name, ".", domain, (char *) 0); + } + return (name); +} + +/* pcf_get_myhostname - look up and store my hostname */ + +static void pcf_get_myhostname(void) +{ + const char *name; + + if ((name = mail_conf_lookup_eval(VAR_MYHOSTNAME)) == 0) + name = pcf_check_myhostname(); + var_myhostname = mystrdup(name); +} + +/* pcf_check_mydomainname - lookup domain name and validate */ + +static const char *pcf_check_mydomainname(void) +{ + static const char *domain; + char *dot; + + /* + * Use cached result. + */ + if (domain) + return (domain); + + /* + * Use a default domain when the hostname is not a FQDN ("foo"). + */ + if (var_myhostname == 0) + pcf_get_myhostname(); + if ((dot = strchr(var_myhostname, '.')) == 0) + return (domain = DEF_MYDOMAIN); + return (domain = mystrdup(dot + 1)); +} + +/* pcf_mynetworks - lookup network address list */ + +static const char *pcf_mynetworks(void) +{ + static const char *networks; + VSTRING *exp_buf; + const char *junk; + + /* + * Use cached result. + */ + if (networks) + return (networks); + + exp_buf = vstring_alloc(100); + + if (var_inet_interfaces == 0) { + if ((pcf_cmd_mode & PCF_SHOW_DEFS) + || (junk = mail_conf_lookup_eval(VAR_INET_INTERFACES)) == 0) + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, + DEF_INET_INTERFACES, + (PCF_MASTER_ENT *) 0); + var_inet_interfaces = mystrdup(junk); + } + if (var_mynetworks_style == 0) { + if ((pcf_cmd_mode & PCF_SHOW_DEFS) + || (junk = mail_conf_lookup_eval(VAR_MYNETWORKS_STYLE)) == 0) + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, + DEF_MYNETWORKS_STYLE, + (PCF_MASTER_ENT *) 0); + var_mynetworks_style = mystrdup(junk); + } + if (var_inet_protocols == 0) { + if ((pcf_cmd_mode & PCF_SHOW_DEFS) + || (junk = mail_conf_lookup_eval(VAR_INET_PROTOCOLS)) == 0) + junk = pcf_expand_parameter_value(exp_buf, pcf_cmd_mode, + DEF_INET_PROTOCOLS, + (PCF_MASTER_ENT *) 0); + var_inet_protocols = mystrdup(junk); + (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols); + } + vstring_free(exp_buf); + return (networks = mystrdup(mynetworks())); +} + +/* pcf_conv_bool_parameter - get boolean parameter string value */ + +static const char *pcf_conv_bool_parameter(void *ptr) +{ + CONFIG_BOOL_TABLE *cbt = (CONFIG_BOOL_TABLE *) ptr; + + return (cbt->defval ? "yes" : "no"); +} + +/* pcf_conv_time_parameter - get relative time parameter string value */ + +static const char *pcf_conv_time_parameter(void *ptr) +{ + CONFIG_TIME_TABLE *ctt = (CONFIG_TIME_TABLE *) ptr; + + return (ctt->defval); +} + +/* pcf_conv_int_parameter - get integer parameter string value */ + +static const char *pcf_conv_int_parameter(void *ptr) +{ + CONFIG_INT_TABLE *cit = (CONFIG_INT_TABLE *) ptr; + + return (STR(vstring_sprintf(pcf_param_string_buf, "%d", cit->defval))); +} + +/* pcf_conv_str_parameter - get string parameter string value */ + +static const char *pcf_conv_str_parameter(void *ptr) +{ + CONFIG_STR_TABLE *cst = (CONFIG_STR_TABLE *) ptr; + + return (cst->defval); +} + +/* pcf_conv_str_fn_parameter - get string-function parameter string value */ + +static const char *pcf_conv_str_fn_parameter(void *ptr) +{ + CONFIG_STR_FN_TABLE *cft = (CONFIG_STR_FN_TABLE *) ptr; + + return (cft->defval()); +} + +/* pcf_conv_raw_parameter - get raw string parameter string value */ + +static const char *pcf_conv_raw_parameter(void *ptr) +{ + CONFIG_RAW_TABLE *rst = (CONFIG_RAW_TABLE *) ptr; + + return (rst->defval); +} + +/* pcf_conv_nint_parameter - get new integer parameter string value */ + +static const char *pcf_conv_nint_parameter(void *ptr) +{ + CONFIG_NINT_TABLE *rst = (CONFIG_NINT_TABLE *) ptr; + + return (rst->defval); +} + +/* pcf_conv_nbool_parameter - get new boolean parameter string value */ + +static const char *pcf_conv_nbool_parameter(void *ptr) +{ + CONFIG_NBOOL_TABLE *bst = (CONFIG_NBOOL_TABLE *) ptr; + + return (bst->defval); +} + +/* pcf_conv_long_parameter - get long parameter string value */ + +static const char *pcf_conv_long_parameter(void *ptr) +{ + CONFIG_LONG_TABLE *clt = (CONFIG_LONG_TABLE *) ptr; + + return (STR(vstring_sprintf(pcf_param_string_buf, "%ld", clt->defval))); +} + +/* pcf_register_builtin_parameters - add built-ins to the global name space */ + +void pcf_register_builtin_parameters(const char *procname, pid_t pid) +{ + const char *myname = "pcf_register_builtin_parameters"; + const CONFIG_TIME_TABLE *ctt; + const CONFIG_BOOL_TABLE *cbt; + const CONFIG_INT_TABLE *cit; + const CONFIG_STR_TABLE *cst; + const CONFIG_STR_FN_TABLE *cft; + const CONFIG_RAW_TABLE *rst; + const CONFIG_NINT_TABLE *nst; + const CONFIG_NBOOL_TABLE *bst; + const CONFIG_LONG_TABLE *lst; + + /* + * Sanity checks. + */ + if (pcf_param_table != 0) + msg_panic("%s: global parameter table is already initialized", myname); + + /* + * Initialize the global parameter table. + */ + pcf_param_table = PCF_PARAM_TABLE_CREATE(1000); + + /* + * Add the built-in parameters to the global name space. The class + * (built-in) is tentative; some parameters are actually service-defined, + * but they have their own default value. + */ + for (ctt = pcf_time_table; ctt->name; ctt++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, ctt->name, + PCF_PARAM_FLAG_BUILTIN, (void *) ctt, + pcf_conv_time_parameter); + for (cbt = pcf_bool_table; cbt->name; cbt++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, cbt->name, + PCF_PARAM_FLAG_BUILTIN, (void *) cbt, + pcf_conv_bool_parameter); + for (cit = pcf_int_table; cit->name; cit++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, cit->name, + PCF_PARAM_FLAG_BUILTIN, (void *) cit, + pcf_conv_int_parameter); + for (cst = pcf_str_table; cst->name; cst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, cst->name, + PCF_PARAM_FLAG_BUILTIN, (void *) cst, + pcf_conv_str_parameter); + for (cft = pcf_str_fn_table; cft->name; cft++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, cft->name, + PCF_PARAM_FLAG_BUILTIN, (void *) cft, + pcf_conv_str_fn_parameter); + for (rst = pcf_raw_table; rst->name; rst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, rst->name, + PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_RAW, + (void *) rst, pcf_conv_raw_parameter); + for (nst = pcf_nint_table; nst->name; nst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, nst->name, + PCF_PARAM_FLAG_BUILTIN, (void *) nst, + pcf_conv_nint_parameter); + for (bst = pcf_nbool_table; bst->name; bst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, bst->name, + PCF_PARAM_FLAG_BUILTIN, (void *) bst, + pcf_conv_nbool_parameter); + for (lst = pcf_long_table; lst->name; lst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, lst->name, + PCF_PARAM_FLAG_BUILTIN, (void *) lst, + pcf_conv_long_parameter); + + /* + * Register legacy parameters (used as a backwards-compatible migration + * aid). + */ + for (cst = pcf_legacy_str_table; cst->name; cst++) + PCF_PARAM_TABLE_ENTER(pcf_param_table, cst->name, + PCF_PARAM_FLAG_LEGACY, (void *) cst, + pcf_conv_str_parameter); + + /* + * Register parameters whose default value is normally initialized by + * ad-hoc code. + */ + pcf_adhoc_procname.defval = mystrdup(procname); + PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_procname.name, + PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, + (void *) &pcf_adhoc_procname, pcf_conv_str_parameter); + pcf_adhoc_servname.defval = mystrdup(""); + PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_servname.name, + PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, + (void *) &pcf_adhoc_servname, pcf_conv_str_parameter); + pcf_adhoc_pid.defval = pid; + PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_pid.name, + PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, + (void *) &pcf_adhoc_pid, pcf_conv_int_parameter); +} diff --git a/src/postconf/postconf_dbms.c b/src/postconf/postconf_dbms.c new file mode 100644 index 0000000..eddeab0 --- /dev/null +++ b/src/postconf/postconf_dbms.c @@ -0,0 +1,325 @@ +/*++ +/* NAME +/* postconf_dbms 3 +/* SUMMARY +/* legacy support for database-defined main.cf parameter names +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_register_dbms_parameters(param_value, flag_parameter, +/* local_scope) +/* const char *param_value; +/* const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *); +/* PCF_MASTER_ENT *local_scope; +/* DESCRIPTION +/* This module implements legacy support for database configuration +/* where main.cf parameter names are generated by prepending +/* the database name to a database-defined suffix. +/* +/* Arguments: +/* .IP param_value +/* A parameter value to be searched for "type:table" strings. +/* When a database type is found that supports legacy-style +/* configuration, the table name is combined with each of the +/* database-defined suffixes to generate candidate parameter +/* names for that database type; if the table name specifies +/* a client configuration file, that file is scanned for unused +/* parameter settings. +/* .IP flag_parameter +/* A function that takes as arguments a candidate parameter +/* name, parameter flags, and a PCF_MASTER_ENT pointer. The +/* function will flag the parameter as "used" if it has a +/* "name=value" entry in the local or global namespace. +/* .IP local_scope +/* The local namespace. +/* DIAGNOSTICS +/* No explicit diagnostics. +/* 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 <sys/stat.h> +#include <errno.h> +#include <string.h> + +/* Utility library. */ + +#include <stringops.h> +#include <split_at.h> +#include <mac_expand.h> +#include <dict.h> +#include <msg.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <mail_conf.h> +#include <mail_params.h> +#include <dict_ht.h> +#include <dict_proxy.h> +#include <dict_ldap.h> +#include <dict_mysql.h> +#include <dict_pgsql.h> +#include <dict_sqlite.h> +#include <dict_memcache.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +#ifdef LEGACY_DBMS_SUPPORT + + /* + * The legacy database interface automagically instantiates a list of + * parameters by prepending the table name to database-specific suffixes. + */ + +/* See ldap_table(5). */ + +static const char *pcf_ldap_suffixes[] = { +#include "pcf_ldap_suffixes.h" + 0, +}; + +/* See mysql_table(5). */ + +static const char *pcf_mysql_suffixes[] = { +#include "pcf_mysql_suffixes.h" + 0, +}; + +/* See pgsql_table(5). */ + +static const char *pcf_pgsql_suffixes[] = { +#include "pcf_pgsql_suffixes.h" + 0, +}; + +/* See sqlite_table(5). */ + +static const char *pcf_sqlite_suffixes[] = { +#include "pcf_sqlite_suffixes.h" + 0, +}; + +/* See memcache_table(5). */ + +static const char *pcf_memcache_suffixes[] = { +#include "pcf_memcache_suffixes.h" + 0, +}; + + /* + * Bundle up the database types and their suffix lists. + */ +typedef struct { + const char *db_type; + const char **db_suffixes; +} PCF_DBMS_INFO; + +static const PCF_DBMS_INFO pcf_dbms_info[] = { + DICT_TYPE_LDAP, pcf_ldap_suffixes, + DICT_TYPE_MYSQL, pcf_mysql_suffixes, + DICT_TYPE_PGSQL, pcf_pgsql_suffixes, + DICT_TYPE_SQLITE, pcf_sqlite_suffixes, + DICT_TYPE_MEMCACHE, pcf_memcache_suffixes, + 0, +}; + +/* pcf_check_dbms_client - look for unused names in client configuration */ + +static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file) +{ + DICT *dict; + VSTREAM *fp; + const char **cpp; + const char *name; + const char *value; + char *dict_spec; + int dir; + + /* + * We read each database client configuration file into its own + * dictionary, and nag only the first time that a file is visited. + */ + dict_spec = concatenate(dp->db_type, ":", cf_file, (char *) 0); + if ((dict = dict_handle(dict_spec)) == 0) { + struct stat st; + + /* + * Populate the dictionary with settings in this database client + * configuration file. Don't die if a file can't be opened - some + * files may contain passwords and should not be world-readable. + * Note: dict_load_fp() nags about duplicate pameter settings. + */ + dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0); + dict_register(dict_spec, dict); + if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0) { + if (errno != EACCES) + msg_warn("open \"%s\" configuration \"%s\": %m", + dp->db_type, cf_file); + myfree(dict_spec); + return; + } + if (fstat(vstream_fileno(fp), &st) == 0 && !S_ISREG(st.st_mode)) { + msg_warn("open \"%s\" configuration \"%s\": not a regular file", + dp->db_type, cf_file); + myfree(dict_spec); + (void) vstream_fclose(fp); + return; + } + dict_load_fp(dict_spec, fp); + if (vstream_fclose(fp)) { + msg_warn("read \"%s\" configuration \"%s\": %m", + dp->db_type, cf_file); + myfree(dict_spec); + return; + } + + /* + * Remove all known database client parameters from this dictionary, + * then report the remaining ones as "unused". We use ad-hoc logging + * code, because a database client parameter namespace is unlike the + * parameter namespaces in main.cf or master.cf. + */ + for (cpp = dp->db_suffixes; *cpp; cpp++) + (void) dict_del(dict, *cpp); + for (dir = DICT_SEQ_FUN_FIRST; + dict->sequence(dict, dir, &name, &value) == DICT_STAT_SUCCESS; + dir = DICT_SEQ_FUN_NEXT) + msg_warn("%s: unused parameter: %s=%s", dict_spec, name, value); + } + myfree(dict_spec); +} + +/* pcf_register_dbms_helper - parse one possible database type:name */ + +static void pcf_register_dbms_helper(char *str_value, + const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *), + PCF_MASTER_ENT *local_scope) +{ + const PCF_DBMS_INFO *dp; + char *db_type; + char *prefix; + static VSTRING *candidate = 0; + const char **cpp; + char *err; + + /* + * Naive parsing. We don't really know if this substring specifies a + * database or some other text. + */ + while ((db_type = mystrtokq(&str_value, CHARS_COMMA_SP, CHARS_BRACE)) != 0) { + + /* + * Skip over "proxy:" maptypes, to emulate the proxymap(8) server's + * behavior when opening a local database configuration file. + */ + while ((prefix = split_at(db_type, ':')) != 0 + && strcmp(db_type, DICT_TYPE_PROXY) == 0) + db_type = prefix; + + if (prefix == 0) + continue; + + /* + * Look for database:prefix where the prefix is an absolute pathname. + * Then, report unknown database client configuration parameters. + * + * XXX What about a pathname beginning with '.'? This supposedly is + * relative to the queue directory, which is the default directory + * for all Postfix daemon processes. This would also have to handle + * the case that the queue is not yet created. + */ + if (*prefix == '/') { + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + pcf_check_dbms_client(dp, prefix); + break; + } + } + continue; + } + + /* + * Look for database:prefix where the prefix is not a pathname and + * the database is a known type. Synthesize candidate parameter names + * from the user-defined prefix and from the database-defined suffix + * list, and see if those parameters have a "name=value" entry in the + * local or global namespace. + */ + if (*prefix != '.') { + if (*prefix == CHARS_BRACE[0]) { + if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) { + /* XXX Encapsulate this in pcf_warn() function. */ + if (local_scope) + msg_warn("%s:%s: %s", + MASTER_CONF_FILE, local_scope->name_space, + err); + else + msg_warn("%s: %s", MAIN_CONF_FILE, err); + myfree(err); + } + pcf_register_dbms_helper(prefix, flag_parameter, + local_scope); + } else { + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + for (cpp = dp->db_suffixes; *cpp; cpp++) { + vstring_sprintf(candidate ? candidate : + (candidate = vstring_alloc(30)), + "%s_%s", prefix, *cpp); + flag_parameter(STR(candidate), + PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER, + local_scope); + } + break; + } + } + } + } + } +} + +/* pcf_register_dbms_parameters - look for database_type:prefix_name */ + +void pcf_register_dbms_parameters(const char *param_value, + const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *), + PCF_MASTER_ENT *local_scope) +{ + char *bufp; + static VSTRING *buffer = 0; + + /* + * XXX This does not examine both sides of conditional macro expansion, + * and may expand the "wrong" conditional macros. This is the best we can + * do for legacy database configuration support. + */ + if (buffer == 0) + buffer = vstring_alloc(100); + bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value, + local_scope); + pcf_register_dbms_helper(bufp, flag_parameter, local_scope); +} + +#endif diff --git a/src/postconf/postconf_edit.c b/src/postconf/postconf_edit.c new file mode 100644 index 0000000..c60cb06 --- /dev/null +++ b/src/postconf/postconf_edit.c @@ -0,0 +1,578 @@ +/*++ +/* NAME +/* postconf_edit 3 +/* SUMMARY +/* edit main.cf or master.cf +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_edit_main(mode, argc, argv) +/* int mode; +/* int argc; +/* char **argv; +/* +/* void pcf_edit_master(mode, argc, argv) +/* int mode; +/* int argc; +/* char **argv; +/* DESCRIPTION +/* pcf_edit_main() edits the \fBmain.cf\fR configuration file. +/* It replaces or adds parameter settings given as "\fIname=value\fR" +/* pairs given on the command line, or removes parameter +/* settings given as "\fIname\fR" on the command line. The +/* file is copied to a temporary file then renamed into place. +/* +/* pcf_edit_master() edits the \fBmaster.cf\fR configuration +/* file. The file is copied to a temporary file then renamed +/* into place. Depending on the flags in \fBmode\fR: +/* .IP PCF_MASTER_ENTRY +/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds +/* entire master.cf entries, specified on the command line as +/* "\fIname/type = name type private unprivileged chroot wakeup +/* process_limit command...\fR". +/* +/* With PCF_EDIT_EXCL or PCF_COMMENT_OUT, pcf_edit_master() +/* removes or comments out entries specified on the command +/* line as "\fIname/type\fR. +/* .IP PCF_MASTER_FLD +/* With PCF_EDIT_CONF, pcf_edit_master() replaces the value +/* of specific service attributes, specified on the command +/* line as "\fIname/type/attribute = value\fR". +/* .IP PCF_MASTER_PARAM +/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds the +/* value of service parameters, specified on the command line +/* as "\fIname/type/parameter = value\fR". +/* +/* With PCF_EDIT_EXCL, pcf_edit_master() removes service +/* parameters specified on the command line as "\fIparametername\fR". +/* DIAGNOSTICS +/* Problems are reported to the standard error stream. +/* FILES +/* /etc/postfix/main.cf, Postfix configuration parameters +/* /etc/postfix/main.cf.tmp, temporary name +/* /etc/postfix/master.cf, Postfix configuration parameters +/* /etc/postfix/master.cf.tmp, temporary name +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <htable.h> +#include <vstring.h> +#include <vstring_vstream.h> +#include <edit_file.h> +#include <readlline.h> +#include <stringops.h> +#include <split_at.h> + +/* Global library. */ + +#include <mail_params.h> + +/* Application-specific. */ + +#include <postconf.h> + +#define STR(x) vstring_str(x) + +/* pcf_find_cf_info - pass-through non-content line, return content or null */ + +static char *pcf_find_cf_info(VSTRING *buf, VSTREAM *dst) +{ + char *cp; + + for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++) + /* void */ ; + /* Pass-through comment, all-whitespace, or empty line. */ + if (*cp == '#' || *cp == 0) { + vstream_fputs(STR(buf), dst); + return (0); + } else { + return (cp); + } +} + +/* pcf_next_cf_line - return next content line, pass non-content */ + +static char *pcf_next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno) +{ + char *cp; + + while (vstring_get(buf, src) != VSTREAM_EOF) { + if (lineno) + *lineno += 1; + if ((cp = pcf_find_cf_info(buf, dst)) != 0) + return (cp); + } + return (0); +} + +/* pcf_gobble_cf_line - accumulate multi-line content, pass non-content */ + +static void pcf_gobble_cf_line(VSTRING *full_entry_buf, VSTRING *line_buf, + VSTREAM *src, VSTREAM *dst, int *lineno) +{ + int ch; + + vstring_strcpy(full_entry_buf, STR(line_buf)); + for (;;) { + if ((ch = VSTREAM_GETC(src)) != VSTREAM_EOF) + vstream_ungetc(src, ch); + if ((ch != '#' && !ISSPACE(ch)) + || vstring_get(line_buf, src) == VSTREAM_EOF) + break; + lineno += 1; + if (pcf_find_cf_info(line_buf, dst)) + vstring_strcat(full_entry_buf, STR(line_buf)); + } +} + +/* pcf_edit_main - edit main.cf file */ + +void pcf_edit_main(int mode, int argc, char **argv) +{ + char *path; + EDIT_FILE *ep; + VSTREAM *src; + VSTREAM *dst; + VSTRING *buf = vstring_alloc(100); + VSTRING *key = vstring_alloc(10); + char *cp; + char *pattern; + char *edit_value; + HTABLE *table; + struct cvalue { + char *value; + int found; + }; + struct cvalue *cvalue; + HTABLE_INFO **ht_info; + HTABLE_INFO **ht; + int interesting; + const char *err; + + /* + * Store command-line parameters for quick lookup. + */ + table = htable_create(argc); + while ((cp = *argv++) != 0) { + if (strchr(cp, '\n') != 0) + msg_fatal("-e, -X, or -# accepts no multi-line input"); + while (ISSPACE(*cp)) + cp++; + if (*cp == '#') + msg_fatal("-e, -X, or -# accepts no comment input"); + if (mode & PCF_EDIT_CONF) { + if ((err = split_nameval(cp, &pattern, &edit_value)) != 0) + msg_fatal("%s: \"%s\"", err, cp); + } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { + if (*cp == 0) + msg_fatal("-X or -# requires non-blank parameter names"); + if (strchr(cp, '=') != 0) + msg_fatal("-X or -# requires parameter names without value"); + pattern = cp; + trimblanks(pattern, 0); + edit_value = 0; + } else { + msg_panic("pcf_edit_main: unknown mode %d", mode); + } + cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue)); + cvalue->value = edit_value; + cvalue->found = 0; + htable_enter(table, pattern, (void *) cvalue); + } + + /* + * Open a temp file for the result. This uses a deterministic name so we + * don't leave behind thrash with random names. + */ + pcf_set_config_dir(); + path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0); + if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) + msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); + dst = ep->tmp_fp; + + /* + * Open the original file for input. + */ + if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { + /* OK to delete, since we control the temp file name exclusively. */ + (void) unlink(ep->tmp_path); + msg_fatal("open %s for reading: %m", path); + } + + /* + * Copy original file to temp file, while replacing parameters on the + * fly. Issue warnings for names found multiple times. + */ +#define STR(x) vstring_str(x) + + interesting = 0; + while ((cp = pcf_next_cf_line(buf, src, dst, (int *) 0)) != 0) { + /* Copy, skip or replace continued text. */ + if (cp > STR(buf)) { + if (interesting == 0) + vstream_fputs(STR(buf), dst); + else if (mode & PCF_COMMENT_OUT) + vstream_fprintf(dst, "#%s", STR(buf)); + } + /* Copy or replace start of logical line. */ + else { + vstring_strncpy(key, cp, strcspn(cp, CHARS_SPACE "=")); + cvalue = (struct cvalue *) htable_find(table, STR(key)); + if ((interesting = !!cvalue) != 0) { + if (cvalue->found++ == 1) + msg_warn("%s: multiple entries for \"%s\"", path, STR(key)); + if (mode & PCF_EDIT_CONF) + vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value); + else if (mode & PCF_COMMENT_OUT) + vstream_fprintf(dst, "#%s", cp); + } else { + vstream_fputs(STR(buf), dst); + } + } + } + + /* + * Generate new entries for parameters that were not found. + */ + if (mode & PCF_EDIT_CONF) { + for (ht_info = ht = htable_list(table); *ht; ht++) { + cvalue = (struct cvalue *) ht[0]->value; + if (cvalue->found == 0) + vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value); + } + myfree((void *) ht_info); + } + + /* + * When all is well, rename the temp file to the original one. + */ + if (vstream_fclose(src)) + msg_fatal("read %s: %m", path); + if (edit_file_close(ep) != 0) + msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); + + /* + * Cleanup. + */ + myfree(path); + vstring_free(buf); + vstring_free(key); + htable_free(table, myfree); +} + + /* + * Data structure to hold a master.cf edit request. + */ +typedef struct { + int match_count; /* hit count */ + const char *raw_text; /* unparsed command-line argument */ + char *parsed_text; /* destructive parse */ + ARGV *service_pattern; /* service name, type, ... */ + int field_number; /* attribute field number */ + const char *param_pattern; /* parameter name */ + char *edit_value; /* value substring */ +} PCF_MASTER_EDIT_REQ; + +/* pcf_edit_master - edit master.cf file */ + +void pcf_edit_master(int mode, int argc, char **argv) +{ + const char *myname = "pcf_edit_master"; + char *path; + EDIT_FILE *ep; + VSTREAM *src; + VSTREAM *dst; + VSTRING *line_buf = vstring_alloc(100); + VSTRING *parse_buf = vstring_alloc(100); + int lineno; + PCF_MASTER_ENT *new_entry; + VSTRING *full_entry_buf = vstring_alloc(100); + char *cp; + char *pattern; + int service_name_type_matched; + const char *err; + PCF_MASTER_EDIT_REQ *edit_reqs; + PCF_MASTER_EDIT_REQ *req; + int num_reqs = argc; + const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#"; + char *service_name; + char *service_type; + + /* + * Sanity check. + */ + if (num_reqs <= 0) + msg_panic("%s: empty argument list", myname); + + /* + * Preprocessing: split pattern=value, then split the pattern components. + */ + edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs); + for (req = edit_reqs; *argv != 0; req++, argv++) { + req->match_count = 0; + req->raw_text = *argv; + cp = req->parsed_text = mystrdup(req->raw_text); + if (strchr(cp, '\n') != 0) + msg_fatal("%s accept no multi-line input", edit_opts); + while (ISSPACE(*cp)) + cp++; + if (*cp == '#') + msg_fatal("%s accept no comment input", edit_opts); + /* Separate the pattern from the value. */ + if (mode & PCF_EDIT_CONF) { + if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0) + msg_fatal("%s: \"%s\"", err, req->raw_text); +#if 0 + if ((mode & PCF_MASTER_PARAM) + && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) + msg_fatal("whitespace in parameter value: \"%s\"", + req->raw_text); +#endif + } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { + if (strchr(cp, '=') != 0) + msg_fatal("-X or -# requires names without value"); + pattern = cp; + trimblanks(pattern, 0); + req->edit_value = 0; + } else { + msg_panic("%s: unknown mode %d", myname, mode); + } + +#define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM) + + /* + * Split name/type or name/type/whatever pattern into components. + */ + switch (mode & PCF_MASTER_MASK) { + case PCF_MASTER_ENTRY: + if ((req->service_pattern = + pcf_parse_service_pattern(pattern, 2, 2)) == 0) + msg_fatal("-Me, -MX or -M# requires service_name/type"); + break; + case PCF_MASTER_FLD: + if ((req->service_pattern = + pcf_parse_service_pattern(pattern, 3, 3)) == 0) + msg_fatal("-Fe or -FX requires service_name/type/field_name"); + req->field_number = + pcf_parse_field_pattern(req->service_pattern->argv[2]); + if (pcf_is_magic_field_pattern(req->field_number)) + msg_fatal("-Fe does not accept wild-card field name"); + if ((mode & PCF_EDIT_CONF) + && req->field_number < PCF_MASTER_FLD_CMD + && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) + msg_fatal("-Fe does not accept whitespace in non-command field"); + break; + case PCF_MASTER_PARAM: + if ((req->service_pattern = + pcf_parse_service_pattern(pattern, 3, 3)) == 0) + msg_fatal("-Pe or -PX requires service_name/type/parameter"); + req->param_pattern = req->service_pattern->argv[2]; + if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) + msg_fatal("-Pe does not accept wild-card parameter name"); + if ((mode & PCF_EDIT_CONF) + && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) + msg_fatal("-Pe does not accept whitespace in parameter value"); + break; + default: + msg_panic("%s: unknown edit mode %d", myname, mode); + } + } + + /* + * Open a temp file for the result. This uses a deterministic name so we + * don't leave behind thrash with random names. + */ + pcf_set_config_dir(); + path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); + if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) + msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); + dst = ep->tmp_fp; + + /* + * Open the original file for input. + */ + if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { + /* OK to delete, since we control the temp file name exclusively. */ + (void) unlink(ep->tmp_path); + msg_fatal("open %s for reading: %m", path); + } + + /* + * Copy original file to temp file, while replacing service entries on + * the fly. + */ + service_name_type_matched = 0; + new_entry = 0; + lineno = 0; + while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) { + vstring_strcpy(line_buf, STR(parse_buf)); + + /* + * Copy, skip or replace continued text. + */ + if (cp > STR(parse_buf)) { + if (service_name_type_matched == 0) + vstream_fputs(STR(line_buf), dst); + else if (mode & PCF_COMMENT_OUT) + vstream_fprintf(dst, "#%s", STR(line_buf)); + } + + /* + * Copy or replace (start of) logical line. + */ + else { + service_name_type_matched = 0; + + /* + * Parse out the service name and type. + */ + if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0 + || (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0) + msg_fatal("file %s: line %d: specify service name and type " + "on the same line", path, lineno); + if (strchr(service_name, '=')) + msg_fatal("file %s: line %d: service name syntax \"%s\" is " + "unsupported with %s", path, lineno, service_name, + edit_opts); + if (service_type[strcspn(service_type, "=/")] != 0) + msg_fatal("file %s: line %d: " + "service type syntax \"%s\" is unsupported with %s", + path, lineno, service_type, edit_opts); + + /* + * Match each service pattern. + */ + for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { + if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, + service_name, + service_type)) { + service_name_type_matched = 1; /* Sticky flag */ + req->match_count += 1; + + /* + * Generate replacement master.cf entries. + */ + if ((mode & PCF_EDIT_CONF) + || ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) { + switch (mode & PCF_MASTER_MASK) { + + /* + * Replace master.cf entry field or parameter + * value. + */ + case PCF_MASTER_FLD: + case PCF_MASTER_PARAM: + if (new_entry == 0) { + /* Gobble up any continuation lines. */ + pcf_gobble_cf_line(full_entry_buf, line_buf, + src, dst, &lineno); + new_entry = (PCF_MASTER_ENT *) + mymalloc(sizeof(*new_entry)); + if ((err = pcf_parse_master_entry(new_entry, + STR(full_entry_buf))) != 0) + msg_fatal("file %s: line %d: %s", + path, lineno, err); + } + if (mode & PCF_MASTER_FLD) { + pcf_edit_master_field(new_entry, + req->field_number, + req->edit_value); + } else { + pcf_edit_master_param(new_entry, mode, + req->param_pattern, + req->edit_value); + } + break; + + /* + * Replace entire master.cf entry. + */ + case PCF_MASTER_ENTRY: + if (new_entry != 0) + pcf_free_master_entry(new_entry); + new_entry = (PCF_MASTER_ENT *) + mymalloc(sizeof(*new_entry)); + if ((err = pcf_parse_master_entry(new_entry, + req->edit_value)) != 0) + msg_fatal("%s: \"%s\"", err, req->raw_text); + break; + default: + msg_panic("%s: unknown edit mode %d", myname, mode); + } + } + } + } + + /* + * Pass through or replace the current input line. + */ + if (new_entry) { + pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); + pcf_free_master_entry(new_entry); + new_entry = 0; + } else if (service_name_type_matched == 0) { + vstream_fputs(STR(line_buf), dst); + } else if (mode & PCF_COMMENT_OUT) { + vstream_fprintf(dst, "#%s", STR(line_buf)); + } + } + } + + /* + * Postprocessing: when editing entire service entries, generate new + * entries for services not found. Otherwise (editing fields or + * parameters), "service not found" is a fatal error. + */ + for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { + if (req->match_count == 0) { + if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) { + new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); + if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0) + msg_fatal("%s: \"%s\"", err, req->raw_text); + pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); + pcf_free_master_entry(new_entry); + } else if ((mode & PCF_MASTER_ENTRY) == 0) { + msg_warn("unmatched service_name/type: \"%s\"", req->raw_text); + } + } + } + + /* + * When all is well, rename the temp file to the original one. + */ + if (vstream_fclose(src)) + msg_fatal("read %s: %m", path); + if (edit_file_close(ep) != 0) + msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); + + /* + * Cleanup. + */ + myfree(path); + vstring_free(line_buf); + vstring_free(parse_buf); + vstring_free(full_entry_buf); + for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { + argv_free(req->service_pattern); + myfree(req->parsed_text); + } + myfree((void *) edit_reqs); +} diff --git a/src/postconf/postconf_lookup.c b/src/postconf/postconf_lookup.c new file mode 100644 index 0000000..3cfa9f3 --- /dev/null +++ b/src/postconf/postconf_lookup.c @@ -0,0 +1,198 @@ +/*++ +/* NAME +/* postconf_lookup 3 +/* SUMMARY +/* parameter lookup routines +/* SYNOPSIS +/* #include <postconf.h> +/* +/* const char *pcf_lookup_parameter_value(mode, name, local_scope, node) +/* int mode; +/* const char *name; +/* PCF_MASTER_ENT *local_scope; +/* PCF_PARAM_NODE *node; +/* +/* char *pcf_expand_parameter_value(buf, mode, value, local_scope) +/* VSTRING *buf; +/* int mode; +/* const char *value; +/* PCF_MASTER_ENT *local_scope; +/* DESCRIPTION +/* These functions perform parameter value lookups. The order +/* of decreasing precedence is: +/* .IP \(bu +/* Search name=value parameter settings in master.cf. These +/* lookups are disabled with the PCF_SHOW_DEFS flag. +/* .IP \(bu +/* Search name=value parameter settings in main.cf. These +/* lookups are disabled with the PCF_SHOW_DEFS flag. +/* .IP \(bu +/* Search built-in default parameter settings. These lookups +/* are disabled with the PCF_SHOW_NONDEF flag. +/* .PP +/* pcf_lookup_parameter_value() looks up the value for the +/* named parameter, and returns null if the name was not found. +/* +/* pcf_expand_parameter_value() expands $name in the specified +/* parameter value. This function ignores the PCF_SHOW_NONDEF +/* flag. The result value is a pointer to storage in a +/* user-supplied buffer, or in a buffer that is overwritten +/* with each call. +/* +/* Arguments: +/* .IP buf +/* Null buffer pointer, or pointer to user-supplied buffer. +/* .IP mode +/* Bit-wise OR of zero or one of the following (other flags +/* are ignored): +/* .RS +/* .IP PCF_SHOW_DEFS +/* Search built-in default parameter settings only. +/* .IP PCF_SHOW_NONDEF +/* Search local (master.cf) and global (main.cf) name=value +/* parameter settings only. +/* .RE +/* .IP name +/* The name of a parameter to be looked up. +/* .IP value +/* The parameter value where $name should be expanded. +/* .IP local_scope +/* Pointer to master.cf entry with local name=value settings, +/* or a null pointer (i.e. no local parameter lookup). +/* .IP node +/* Global default value for the named parameter, or a null +/* pointer (i.e. do the global default lookup anyway). +/* 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> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> +#include <dict.h> +#include <stringops.h> +#include <mac_expand.h> + +/* Global library. */ + +#include <mail_conf.h> + +/* Application-specific. */ + +#include <postconf.h> + +#define STR(x) vstring_str(x) + +/* pcf_lookup_parameter_value - look up specific parameter value */ + +const char *pcf_lookup_parameter_value(int mode, const char *name, + PCF_MASTER_ENT *local_scope, + PCF_PARAM_NODE *node) +{ + const char *value = 0; + +#define LOOKUP(dict, name) ((dict) ? dict_get((dict), (name)) : 0) + + /* + * Local name=value entries in master.cf take precedence over global + * name=value entries in main.cf. Built-in defaults have the lowest + * precedence. + */ + if ((mode & PCF_SHOW_DEFS) != 0 + || ((local_scope == 0 + || ((value = LOOKUP(local_scope->ro_params, name)) == 0 + && (value = LOOKUP(local_scope->all_params, name)) == 0)) + && (value = dict_lookup(CONFIG_DICT, name)) == 0 + && (mode & PCF_SHOW_NONDEF) == 0)) { + if (node != 0 || (node = PCF_PARAM_TABLE_FIND(pcf_param_table, name)) != 0) + value = pcf_convert_param_node(PCF_SHOW_DEFS, name, node); + } + return (value); +} + + /* + * Data structure to pass private state while recursively expanding $name in + * parameter values. + */ +typedef struct { + int mode; + PCF_MASTER_ENT *local_scope; +} PCF_EVAL_CTX; + +/* pcf_lookup_parameter_value_wrapper - macro parser call-back routine */ + +static const char *pcf_lookup_parameter_value_wrapper(const char *key, + int unused_type, + void *context) +{ + PCF_EVAL_CTX *cp = (PCF_EVAL_CTX *) context; + + return (pcf_lookup_parameter_value(cp->mode, key, cp->local_scope, + (PCF_PARAM_NODE *) 0)); +} + +/* pcf_expand_parameter_value - expand $name in parameter value */ + +char *pcf_expand_parameter_value(VSTRING *buf, int mode, const char *value, + PCF_MASTER_ENT *local_scope) +{ + const char *myname = "pcf_expand_parameter_value"; + static VSTRING *local_buf; + int status; + PCF_EVAL_CTX eval_ctx; + + /* + * Initialize. + */ + if (buf == 0) { + if (local_buf == 0) + local_buf = vstring_alloc(10); + buf = local_buf; + } + + /* + * Expand macros recursively. + * + * When expanding $name in "postconf -n" parameter values, don't limit the + * search to only non-default parameter values. + * + * When expanding $name in "postconf -d" parameter values, do limit the + * search to only default parameter values. + */ +#define DONT_FILTER (char *) 0 + + eval_ctx.mode = (mode & ~PCF_SHOW_NONDEF); + eval_ctx.local_scope = local_scope; + status = mac_expand(buf, value, MAC_EXP_FLAG_RECURSE, DONT_FILTER, + pcf_lookup_parameter_value_wrapper, (void *) &eval_ctx); + if (status & MAC_PARSE_ERROR) + msg_fatal("macro processing error"); + if (msg_verbose > 1) { + if (strcmp(value, STR(buf)) != 0) + msg_info("%s: expand %s -> %s", myname, value, STR(buf)); + else + msg_info("%s: const %s", myname, value); + } + return (STR(buf)); +} diff --git a/src/postconf/postconf_main.c b/src/postconf/postconf_main.c new file mode 100644 index 0000000..07485f1 --- /dev/null +++ b/src/postconf/postconf_main.c @@ -0,0 +1,216 @@ +/*++ +/* NAME +/* postconf_main 3 +/* SUMMARY +/* basic support for main.cf +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_read_parameters() +/* +/* void pcf_show_parameters(fp, mode, param_class, names) +/* VSTREAM *fp; +/* int mode; +/* int param_class; +/* char **names; +/* DESCRIPTION +/* pcf_read_parameters() reads parameters from main.cf. +/* +/* pcf_set_parameters() takes an array of \fIname=value\fR +/* pairs and overrides settings read with pcf_read_parameters(). +/* +/* pcf_show_parameters() writes main.cf parameters to the +/* specified output stream. +/* +/* Arguments: +/* .IP fp +/* Output stream. +/* .IP mode +/* Bit-wise OR of zero or more of the following: +/* .RS +/* .IP PCF_FOLD_LINE +/* Fold long lines. +/* .IP PCF_SHOW_DEFS +/* Output default parameter values. +/* .IP PCF_SHOW_NONDEF +/* Output explicit settings only. +/* .IP PCF_HIDE_NAME +/* Output parameter values without the "name =" prefix. +/* .IP PCF_SHOW_EVAL +/* Expand $name in parameter values. +/* .RE +/* .IP param_class +/* Bit-wise OR of one or more of the following: +/* .RS +/* .IP PCF_PARAM_FLAG_BUILTIN +/* Show built-in parameters. +/* .IP PCF_PARAM_FLAG_SERVICE +/* Show service-defined parameters. +/* .IP PCF_PARAM_FLAG_USER +/* Show user-defined parameters. +/* .RE +/* .IP names +/* List of zero or more parameter names. If the list is empty, +/* output all parameters. +/* 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 <stdarg.h> +#include <stdlib.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> +#include <readlline.h> +#include <dict.h> +#include <stringops.h> +#include <htable.h> +#include <mac_expand.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_conf.h> + +/* Application-specific. */ + +#include <postconf.h> + +#define STR(x) vstring_str(x) + +/* pcf_read_parameters - read parameter info from file */ + +void pcf_read_parameters(void) +{ + char *path; + + /* + * A direct rip-off of mail_conf_read(). XXX Avoid code duplication by + * better code decomposition. + */ + pcf_set_config_dir(); + path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0); + if (dict_load_file_xt(CONFIG_DICT, path) == 0) + msg_fatal("open %s: %m", path); + myfree(path); +} + +/* pcf_set_parameters - add or override name=value pairs */ + +void pcf_set_parameters(char **name_val_array) +{ + char *name, *value, *junk; + const char *err; + char **cpp; + + for (cpp = name_val_array; *cpp; cpp++) { + junk = mystrdup(*cpp); + if ((err = split_nameval(junk, &name, &value)) != 0) + msg_fatal("invalid parameter override: %s: %s", *cpp, err); + mail_conf_update(name, value); + myfree(junk); + } +} + +/* pcf_print_parameter - show specific parameter */ + +static void pcf_print_parameter(VSTREAM *fp, int mode, const char *name, + PCF_PARAM_NODE *node) +{ + const char *value; + + /* + * Use the default or actual value. + */ + value = pcf_lookup_parameter_value(mode, name, (PCF_MASTER_ENT *) 0, node); + + /* + * Optionally expand $name in the parameter value. Print the result with + * or without the name= prefix. + */ + if (value != 0) { + if (mode & PCF_HIDE_VALUE) { + pcf_print_line(fp, mode, "%s\n", name); + } else { + if ((mode & PCF_SHOW_EVAL) != 0 && PCF_RAW_PARAMETER(node) == 0) + value = pcf_expand_parameter_value((VSTRING *) 0, mode, value, + (PCF_MASTER_ENT *) 0); + if ((mode & PCF_HIDE_NAME) == 0) { + pcf_print_line(fp, mode, "%s = %s\n", name, value); + } else { + pcf_print_line(fp, mode, "%s\n", value); + } + } + if (msg_verbose) + vstream_fflush(fp); + } +} + +/* pcf_comp_names - qsort helper */ + +static int pcf_comp_names(const void *a, const void *b) +{ + PCF_PARAM_INFO **ap = (PCF_PARAM_INFO **) a; + PCF_PARAM_INFO **bp = (PCF_PARAM_INFO **) b; + + return (strcmp(PCF_PARAM_INFO_NAME(ap[0]), + PCF_PARAM_INFO_NAME(bp[0]))); +} + +/* pcf_show_parameters - show parameter info */ + +void pcf_show_parameters(VSTREAM *fp, int mode, int param_class, char **names) +{ + PCF_PARAM_INFO **list; + PCF_PARAM_INFO **ht; + char **namep; + PCF_PARAM_NODE *node; + + /* + * Show all parameters. + */ + if (*names == 0) { + list = PCF_PARAM_TABLE_LIST(pcf_param_table); + qsort((void *) list, pcf_param_table->used, sizeof(*list), + pcf_comp_names); + for (ht = list; *ht; ht++) + if (param_class & PCF_PARAM_INFO_NODE(*ht)->flags) + pcf_print_parameter(fp, mode, PCF_PARAM_INFO_NAME(*ht), + PCF_PARAM_INFO_NODE(*ht)); + myfree((void *) list); + return; + } + + /* + * Show named parameters. + */ + for (namep = names; *namep; namep++) { + if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, *namep)) == 0) { + msg_warn("%s: unknown parameter", *namep); + } else { + pcf_print_parameter(fp, mode, *namep, node); + } + } +} diff --git a/src/postconf/postconf_master.c b/src/postconf/postconf_master.c new file mode 100644 index 0000000..3c169f7 --- /dev/null +++ b/src/postconf/postconf_master.c @@ -0,0 +1,1108 @@ +/*++ +/* 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, argc, **param_filters) +/* VSTREAM *fp; +/* int mode; +/* 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. +/* +/* 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> + +/* 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-"; + +#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_free(masterp->ro_params); + if (masterp->all_params) + dict_free(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; + process_name = basename(argv->argv[PCF_MASTER_FLD_CMD]); + dict_update(ro_name_space, VAR_PROCNAME, process_name); + dict_update(ro_name_space, VAR_SERVNAME, + strcmp(process_name, argv->argv[0]) != 0 ? + argv->argv[0] : process_name); + masterp->ro_params = dict_handle(ro_name_space); + 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"; + 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. + */ + if (var_config_dir == 0) + pcf_set_config_dir(); + path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); + + /* + * 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; + myfree(path); +} + +/* 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 */ + }; + +#define ADD_TEXT(text, len) do { \ + vstream_fputs(text, fp); line_len += len; } \ + while (0) +#define ADD_SPACE ADD_TEXT(" ", 1) + + /* + * 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((VSTRING *) 0, 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 - 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; + + /* + * 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((VSTRING *) 0, 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 (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); +} + +/* 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)); +} + +/* 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) +{ + 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; + + /* + * Print parameters in sorted order. The number of parameters per + * master.cf entry is small, so we optmiize 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 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 ((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); + } + } + } + + /* + * 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; + } +} diff --git a/src/postconf/postconf_match.c b/src/postconf/postconf_match.c new file mode 100644 index 0000000..53709a4 --- /dev/null +++ b/src/postconf/postconf_match.c @@ -0,0 +1,188 @@ +/*++ +/* NAME +/* postconf_match 3 +/* SUMMARY +/* pattern-matching support +/* SYNOPSIS +/* #include <postconf.h> +/* +/* int pcf_parse_field_pattern(field_expr) +/* char *field_expr; +/* +/* const char *pcf_str_field_pattern(field_pattern) +/* int field_pattern; +/* +/* int pcf_is_magic_field_pattern(field_pattern) +/* int field_pattern; +/* +/* ARGV *pcf_parse_service_pattern(service_expr, min_expr, max_expr) +/* const char *service_expr; +/* int min_expr; +/* int max_expr; +/* +/* int PCF_IS_MAGIC_SERVICE_PATTERN(service_pattern) +/* const ARGV *service_pattern; +/* +/* int PCF_MATCH_SERVICE_PATTERN(service_pattern, service_name, +/* service_type) +/* const ARGV *service_pattern; +/* const char *service_name; +/* const char *service_type; +/* +/* const char *pcf_str_field_pattern(field_pattern) +/* int field_pattern; +/* +/* int PCF_IS_MAGIC_PARAM_PATTERN(param_pattern) +/* const char *param_pattern; +/* +/* int PCF_MATCH_PARAM_PATTERN(param_pattern, param_name) +/* const char *param_pattern; +/* const char *param_name; +/* DESCRIPTION +/* pcf_parse_service_pattern() takes an expression and splits +/* it up on '/' into an array of sub-expressions, This function +/* returns null if the input does fewer than "min" or more +/* than "max" sub-expressions. +/* +/* PCF_IS_MAGIC_SERVICE_PATTERN() returns non-zero if any of +/* the service name or service type sub-expressions contains +/* a matching operator (as opposed to string literals that +/* only match themselves). This is an unsafe macro that evaluates +/* its arguments more than once. +/* +/* PCF_MATCH_SERVICE_PATTERN() matches a service name and type +/* from master.cf against the parsed pattern. This is an unsafe +/* macro that evaluates its arguments more than once. +/* +/* pcf_parse_field_pattern() converts a field sub-expression, +/* and returns the conversion result. +/* +/* pcf_str_field_pattern() converts a result from +/* pcf_parse_field_pattern() into string form. +/* +/* pcf_is_magic_field_pattern() returns non-zero if the field +/* pattern sub-expression contained a matching operator (as +/* opposed to a string literal that only matches itself). +/* +/* PCF_IS_MAGIC_PARAM_PATTERN() returns non-zero if the parameter +/* sub-expression contains a matching operator (as opposed to +/* a string literal that only matches itself). This is an +/* unsafe macro that evaluates its arguments more than once. +/* +/* PCF_MATCH_PARAM_PATTERN() matches a parameter name from +/* master.cf against the parsed pattern. This is an unsafe +/* macro that evaluates its arguments more than once. +/* +/* Arguments +/* .IP field_expr +/* A field expression. +/* .IP service_expr +/* This argument is split on '/' into its constituent +/* sub-expressions. +/* .IP min_expr +/* The minimum number of sub-expressions in service_expr. +/* .IP max_expr +/* The maximum number of sub-expressions in service_expr. +/* .IP service_name +/* Service name from master.cf. +/* .IP service_type +/* Service type from master.cf. +/* .IP param_pattern +/* A parameter name expression. +/* DIAGNOSTICS +/* Fatal errors: invalid syntax. +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> + +/* Global library. */ + +#include <split_at.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * Conversion table. Each PCF_MASTER_NAME_XXX name entry must be stored at + * table offset PCF_MASTER_FLD_XXX. So don't mess it up. + */ +NAME_CODE pcf_field_name_offset[] = { + PCF_MASTER_NAME_SERVICE, PCF_MASTER_FLD_SERVICE, + PCF_MASTER_NAME_TYPE, PCF_MASTER_FLD_TYPE, + PCF_MASTER_NAME_PRIVATE, PCF_MASTER_FLD_PRIVATE, + PCF_MASTER_NAME_UNPRIV, PCF_MASTER_FLD_UNPRIV, + PCF_MASTER_NAME_CHROOT, PCF_MASTER_FLD_CHROOT, + PCF_MASTER_NAME_WAKEUP, PCF_MASTER_FLD_WAKEUP, + PCF_MASTER_NAME_MAXPROC, PCF_MASTER_FLD_MAXPROC, + PCF_MASTER_NAME_CMD, PCF_MASTER_FLD_CMD, + "*", PCF_MASTER_FLD_WILDC, + 0, PCF_MASTER_FLD_NONE, +}; + +/* pcf_parse_field_pattern - parse service attribute pattern */ + +int pcf_parse_field_pattern(const char *field_name) +{ + int field_pattern; + + if ((field_pattern = name_code(pcf_field_name_offset, + NAME_CODE_FLAG_STRICT_CASE, + field_name)) == PCF_MASTER_FLD_NONE) + msg_fatal("invalid service attribute name: \"%s\"", field_name); + return (field_pattern); +} + +/* pcf_parse_service_pattern - parse service pattern */ + +ARGV *pcf_parse_service_pattern(const char *pattern, int min_expr, int max_expr) +{ + ARGV *argv; + char **cpp; + + /* + * Work around argv_split() lameness. + */ + if (*pattern == '/') + return (0); + argv = argv_split(pattern, PCF_NAMESP_SEP_STR); + if (argv->argc < min_expr || argv->argc > max_expr) { + argv_free(argv); + return (0); + } + + /* + * Allow '*' only all by itself. + */ + for (cpp = argv->argv; *cpp; cpp++) { + if (!PCF_MATCH_ANY(*cpp) && strchr(*cpp, PCF_MATCH_WILDC_STR[0]) != 0) { + argv_free(argv); + return (0); + } + } + + /* + * Provide defaults for missing fields. + */ + while (argv->argc < max_expr) + argv_add(argv, PCF_MATCH_WILDC_STR, ARGV_END); + return (argv); +} diff --git a/src/postconf/postconf_misc.c b/src/postconf/postconf_misc.c new file mode 100644 index 0000000..0107651 --- /dev/null +++ b/src/postconf/postconf_misc.c @@ -0,0 +1,60 @@ +/*++ +/* NAME +/* postconf_misc 3 +/* SUMMARY +/* miscellaneous low-level code +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_set_config_dir() +/* DESCRIPTION +/* pcf_set_config_dir() forcibly overrides the var_config_dir +/* parameter setting with the value from the environment or +/* with the default pathname, and updates the mail parameter +/* dictionary. +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <mymalloc.h> +#include <safe.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_conf.h> + +/* Application-specific. */ + +#include <postconf.h> + +/* pcf_set_config_dir - forcibly override var_config_dir */ + +void pcf_set_config_dir(void) +{ + char *config_dir; + + if (var_config_dir) + myfree(var_config_dir); + if ((config_dir = safe_getenv(CONF_ENV_PATH)) != 0) { + var_config_dir = mystrdup(config_dir); + set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir); + } else { + var_config_dir = mystrdup(DEF_CONFIG_DIR); + } +} diff --git a/src/postconf/postconf_node.c b/src/postconf/postconf_node.c new file mode 100644 index 0000000..dca3594 --- /dev/null +++ b/src/postconf/postconf_node.c @@ -0,0 +1,185 @@ +/*++ +/* NAME +/* postconf_node 3 +/* SUMMARY +/* low-level parameter node support +/* SYNOPSIS +/* #include <postconf.h> +/* +/* PCF_PARAM_TABLE *PCF_PARAM_TABLE_CREATE(size) +/* ssize_t size; +/* +/* PCF_PARAM_INFO **PCF_PARAM_TABLE_LIST(table) +/* PCF_PARAM_TABLE *table; +/* +/* const char *PCF_PARAM_INFO_NAME(info) +/* PCF_PARAM_INFO *info; +/* +/* PCF_PARAM_NODE *PCF_PARAM_INFO_NODE(info) +/* PCF_PARAM_INFO *info; +/* +/* PCF_PARAM_NODE *PCF_PARAM_TABLE_FIND(table, name) +/* PCF_PARAM_TABLE *table; +/* const char *name; +/* +/* PCF_PARAM_INFO *PCF_PARAM_TABLE_LOCATE(table, name) +/* PCF_PARAM_TABLE *table; +/* const char *name; +/* +/* PCF_PARAM_INFO *PCF_PARAM_TABLE_ENTER(table, name, flags, +/* param_data, convert_fn) +/* PCF_PARAM_TABLE *table; +/* const char *name; +/* int flags; +/* void *param_data; +/* const char *(*convert_fn)(void *); +/* +/* PCF_PARAM_NODE *pcf_make_param_node(flags, param_data, convert_fn) +/* int flags; +/* void *param_data; +/* const char *(*convert_fn) (void *); +/* +/* const char *pcf_convert_param_node(mode, name, node) +/* int mode; +/* const char *name; +/* PCF_PARAM_NODE *node; +/* +/* VSTRING *pcf_param_string_buf; +/* +/* PCF_RAW_PARAMETER(node) +/* const PCF_PARAM_NODE *node; +/* DESCRIPTION +/* This module maintains data structures (PCF_PARAM_NODE) with +/* information about known-legitimate parameters. These data +/* structures are stored in a hash table. +/* +/* The PCF_PARAM_MUMBLE() macros are wrappers around the +/* htable(3) module. Their sole purpose is to encapsulate all +/* the pointer casting from and to (PCF_PARAM_NODE *). Apart +/* from that, the macros have no features worth discussing. +/* +/* pcf_make_param_node() creates a node for the global parameter +/* table. This node provides a parameter default value, and a +/* function that converts the default value to string. +/* +/* pcf_convert_param_node() produces a string representation +/* for a global parameter default value. +/* +/* PCF_RAW_PARAMETER() returns non-zero if the specified +/* parameter node represents a "raw parameter". The value of +/* such parameters must not be scanned for macro names. Some +/* "raw parameter" values contain "$" without macros, such as +/* the smtpd_expansion_filter "safe character" set; and some +/* contain $name from a private name space, such as forward_path. +/* Some "raw parameter" values in postscreen(8) are safe to +/* expand by one level. Support for that may be added later. +/* +/* pcf_param_string_buf is a buffer that is initialized on the +/* fly and that parameter-to-string conversion functions may +/* use for temporary result storage. +/* +/* Arguments: +/* .IP size +/* The initial size of the hash table. +/* .IP table +/* A hash table for storage of "valid parameter" information. +/* .IP info +/* A data structure with a name component and a PCF_PARAM_NODE +/* component. Use PCF_PARAM_INFO_NAME() and PCF_PARAM_INFO_NODE() +/* to access these components. +/* .IP name +/* The name of a "valid parameter". +/* .IP flags +/* PCF_PARAM_FLAG_RAW for a "raw parameter", PCF_PARAM_FLAG_NONE +/* otherwise. See the PCF_RAW_PARAMETER() discussion above for +/* discussion of "raw parameter" values. +/* .IP param_data +/* Information about the parameter value. Specify PCF_PARAM_NO_DATA +/* if this is not applicable. +/* .IP convert_fn +/* The function that will be invoked to produce a string +/* representation of the information in param_data. The function +/* receives the param_data value as argument. +/* .IP mode +/* For now, the PCF_SHOW_DEFS flag is required. +/* .IP name +/* The name of the parameter whose value is requested. This +/* is used for diagnostics. +/* .IP node +/* The (flags, param_data, convert_fn) information that needs +/* to be converted to a string representation of the default +/* value. +/* 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 +/*--*/ + + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> + +/* Application-specific. */ + +#include <postconf.h> + +VSTRING *pcf_param_string_buf; + +/* pcf_make_param_node - make node for global parameter table */ + +PCF_PARAM_NODE *pcf_make_param_node(int flags, void *param_data, + const char *(*convert_fn) (void *)) +{ + PCF_PARAM_NODE *node; + + node = (PCF_PARAM_NODE *) mymalloc(sizeof(*node)); + node->flags = flags; + node->param_data = param_data; + node->convert_fn = convert_fn; + return (node); +} + +/* pcf_convert_param_node - get default parameter value */ + +const char *pcf_convert_param_node(int mode, const char *name, PCF_PARAM_NODE *node) +{ + const char *myname = "pcf_convert_param_node"; + const char *value; + + /* + * One-off initialization. + */ + if (pcf_param_string_buf == 0) + pcf_param_string_buf = vstring_alloc(100); + + /* + * Sanity check. A null value indicates that a parameter does not have + * the requested value. At this time, the only requested value can be the + * default value, and a null pointer value makes no sense here. + */ + if ((mode & PCF_SHOW_DEFS) == 0) + msg_panic("%s: request for non-default value of parameter %s", + myname, name); + if ((value = node->convert_fn(node->param_data)) == 0) + msg_panic("%s: parameter %s has null pointer default value", + myname, name); + + /* + * Return the parameter default value. + */ + return (value); +} diff --git a/src/postconf/postconf_other.c b/src/postconf/postconf_other.c new file mode 100644 index 0000000..0c4c0c4 --- /dev/null +++ b/src/postconf/postconf_other.c @@ -0,0 +1,141 @@ +/*++ +/* NAME +/* postconf_other 3 +/* SUMMARY +/* support for miscellaneous information categories +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_show_maps() +/* +/* void pcf_show_locks() +/* +/* void pcf_show_sasl(mode) +/* int mode; +/* +/* void pcf_show_tls(what) +/* const char *what; +/* DESCRIPTION +/* pcf_show_maps() lists the available map (lookup table) +/* types. +/* +/* pcf_show_locks() lists the available mailbox lock types. +/* +/* pcf_show_sasl() shows the available SASL authentication +/* plugin types. +/* +/* pcf_show_tls() reports the "compile-version" or "run-version" +/* of the TLS library, or the supported public-key algorithms. +/* +/* Arguments: +/* .IP mode +/* Show server information if the PCF_SHOW_SASL_SERV flag is +/* set, otherwise show client information. +/* .IP what +/* One of the literals "compile-version", "run-version" or +/* "public-key-algorithms". +/* 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> + +/* Utility library. */ + +#include <vstream.h> +#include <argv.h> +#include <dict.h> +#include <msg.h> + +/* Global library. */ + +#include <mbox_conf.h> + +/* XSASL library. */ + +#include <xsasl.h> + +/* TLS library. */ + +#include <tls.h> + +/* Application-specific. */ + +#include <postconf.h> + +/* pcf_show_maps - show available maps */ + +void pcf_show_maps(void) +{ + ARGV *maps_argv; + int i; + + maps_argv = dict_mapnames(); + for (i = 0; i < maps_argv->argc; i++) + vstream_printf("%s\n", maps_argv->argv[i]); + argv_free(maps_argv); +} + +/* pcf_show_locks - show available mailbox locking methods */ + +void pcf_show_locks(void) +{ + ARGV *locks_argv; + int i; + + locks_argv = mbox_lock_names(); + for (i = 0; i < locks_argv->argc; i++) + vstream_printf("%s\n", locks_argv->argv[i]); + argv_free(locks_argv); +} + +/* pcf_show_sasl - show SASL plug-in types */ + +void pcf_show_sasl(int what) +{ + ARGV *sasl_argv; + int i; + + sasl_argv = (what & PCF_SHOW_SASL_SERV) ? xsasl_server_types() : + xsasl_client_types(); + for (i = 0; i < sasl_argv->argc; i++) + vstream_printf("%s\n", sasl_argv->argv[i]); + argv_free(sasl_argv); +} + +/* pcf_show_tls - show TLS support */ + +void pcf_show_tls(const char *what) +{ +#ifdef USE_TLS + if (strcmp(what, "compile-version") == 0) + vstream_printf("%s\n", tls_compile_version()); + else if (strcmp(what, "run-version") == 0) + vstream_printf("%s\n", tls_run_version()); + else if (strcmp(what, "public-key-algorithms") == 0) { + const char **cpp; + + for (cpp = tls_pkey_algorithms(); *cpp; cpp++) + vstream_printf("%s\n", *cpp); + } else { + msg_warn("unknown 'postconf -T' mode: %s", what); + exit(1); + } +#endif /* USE_TLS */ +} diff --git a/src/postconf/postconf_print.c b/src/postconf/postconf_print.c new file mode 100644 index 0000000..32c4015 --- /dev/null +++ b/src/postconf/postconf_print.c @@ -0,0 +1,114 @@ +/*++ +/* NAME +/* postconf_print 3 +/* SUMMARY +/* basic line printing support +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_print_line(fp, mode, const char *fmt, ...) +/* VSTREAM *fp; +/* int mode; +/* const char *fmt; +/* DESCRIPTION +/* pcf_print_line() formats text, normalized whitespace, and +/* optionally folds long lines. +/* +/* Arguments: +/* .IP fp +/* Output stream. +/* .IP mode +/* Bit-wise OR of zero or more of the following (other flags +/* are ignored): +/* .RS +/* .IP PCF_FOLD_LINE +/* Fold long lines. +/* .RE +/* .IP fmt +/* Format string. +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <string.h> +#include <stdarg.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> + +/* Application-specific. */ + +#include <postconf.h> + +/* SLMs. */ + +#define STR(x) vstring_str(x) + +/* pcf_print_line - show line possibly folded, and with normalized whitespace */ + +void pcf_print_line(VSTREAM *fp, int mode, const char *fmt,...) +{ + va_list ap; + static VSTRING *buf = 0; + char *start; + char *next; + int line_len = 0; + int word_len; + + /* + * One-off initialization. + */ + if (buf == 0) + buf = vstring_alloc(100); + + /* + * Format the text. + */ + va_start(ap, fmt); + vstring_vsprintf(buf, fmt, ap); + va_end(ap); + + /* + * Normalize the whitespace. We don't use the line_wrap() routine because + * 1) that function does not normalize whitespace between words and 2) we + * want to normalize whitespace even when not wrapping lines. + * + * XXX Some parameters preserve whitespace: for example, smtpd_banner and + * smtpd_reject_footer. If we have to preserve whitespace between words, + * then perhaps readlline() can be changed to canonicalize whitespace + * that follows a newline. + */ + for (start = STR(buf); *(start += strspn(start, PCF_SEPARATORS)) != 0; start = next) { + word_len = strcspn(start, PCF_SEPARATORS); + if (*(next = start + word_len) != 0) + *next++ = 0; + if (word_len > 0 && line_len > 0) { + if ((mode & PCF_FOLD_LINE) == 0 + || line_len + word_len < PCF_LINE_LIMIT) { + vstream_fputs(" ", fp); + line_len += 1; + } else { + vstream_fputs("\n" PCF_INDENT_TEXT, fp); + line_len = PCF_INDENT_LEN; + } + } + vstream_fputs(start, fp); + line_len += word_len; + } + vstream_fputs("\n", fp); +} diff --git a/src/postconf/postconf_service.c b/src/postconf/postconf_service.c new file mode 100644 index 0000000..38a4cce --- /dev/null +++ b/src/postconf/postconf_service.c @@ -0,0 +1,199 @@ +/*++ +/* NAME +/* postconf_service 3 +/* SUMMARY +/* service-defined main.cf parameter name support +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_register_service_parameters() +/* DESCRIPTION +/* Service-defined parameter names are created by appending +/* postfix-defined suffixes to master.cf service names. All +/* service-defined parameters have default values that are +/* defined by a built-in parameter. +/* +/* pcf_register_service_parameters() adds the service-defined +/* parameters to the global name space. This function must be +/* called after the built-in parameters are added to the global +/* name space, and after the master.cf file is read. +/* 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> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <htable.h> +#include <vstring.h> +#include <stringops.h> +#include <argv.h> + +/* Global library. */ + +#include <mail_params.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * Basename of programs in $daemon_directory. XXX These belong in a header + * file, or they should be made configurable. + */ +#ifndef MAIL_PROGRAM_LOCAL +#define MAIL_PROGRAM_LOCAL "local" +#define MAIL_PROGRAM_ERROR "error" +#define MAIL_PROGRAM_VIRTUAL "virtual" +#define MAIL_PROGRAM_SMTP "smtp" +#define MAIL_PROGRAM_LMTP "lmtp" +#define MAIL_PROGRAM_PIPE "pipe" +#define MAIL_PROGRAM_SPAWN "spawn" +#endif + + /* + * Ad-hoc name-value string pair. + */ +typedef struct { + const char *name; + const char *value; +} PCF_STRING_NV; + +#define STR(x) vstring_str(x) + +/* pcf_convert_service_parameter - get service parameter string value */ + +static const char *pcf_convert_service_parameter(void *ptr) +{ + return (STR(vstring_sprintf(pcf_param_string_buf, "$%s", (char *) ptr))); +} + +/* pcf_register_service_parameter - add service parameter name and default */ + +static void pcf_register_service_parameter(const char *service, + const char *suffix, + const char *defparam) +{ + char *name = concatenate(service, suffix, (char *) 0); + PCF_PARAM_NODE *node; + + /* + * Skip service parameter names that have built-in definitions. This + * happens with message delivery transports that have a non-default + * per-destination concurrency or recipient limit, such as local(8). + * + * Some parameters were tentatively flagged as built-in, but they are + * service parameters with their own default value. We don't change the + * default but we correct the parameter class. + */ + if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, name)) != 0) { + PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_SERVICE); + } else { + PCF_PARAM_TABLE_ENTER(pcf_param_table, name, PCF_PARAM_FLAG_SERVICE, + (void *) defparam, pcf_convert_service_parameter); + } + myfree(name); +} + +/* pcf_register_service_parameters - add all service parameters with defaults */ + +void pcf_register_service_parameters(void) +{ + const char *myname = "pcf_register_service_parameters"; + static const PCF_STRING_NV pipe_params[] = { + /* suffix, default parameter name */ + _MAXTIME, VAR_COMMAND_MAXTIME, +#define service_params (pipe_params + 1) + _XPORT_RCPT_LIMIT, VAR_XPORT_RCPT_LIMIT, + _STACK_RCPT_LIMIT, VAR_STACK_RCPT_LIMIT, + _XPORT_REFILL_LIMIT, VAR_XPORT_REFILL_LIMIT, + _XPORT_REFILL_DELAY, VAR_XPORT_REFILL_DELAY, + _DELIVERY_SLOT_COST, VAR_DELIVERY_SLOT_COST, + _DELIVERY_SLOT_LOAN, VAR_DELIVERY_SLOT_LOAN, + _DELIVERY_SLOT_DISCOUNT, VAR_DELIVERY_SLOT_DISCOUNT, + _MIN_DELIVERY_SLOTS, VAR_MIN_DELIVERY_SLOTS, + _INIT_DEST_CON, VAR_INIT_DEST_CON, + _DEST_CON_LIMIT, VAR_DEST_CON_LIMIT, + _DEST_RCPT_LIMIT, VAR_DEST_RCPT_LIMIT, + _CONC_POS_FDBACK, VAR_CONC_POS_FDBACK, + _CONC_NEG_FDBACK, VAR_CONC_NEG_FDBACK, + _CONC_COHORT_LIM, VAR_CONC_COHORT_LIM, + _DEST_RATE_DELAY, VAR_DEST_RATE_DELAY, + _XPORT_RATE_DELAY, VAR_XPORT_RATE_DELAY, + 0, + }; + static const PCF_STRING_NV spawn_params[] = { + /* suffix, default parameter name */ + _MAXTIME, VAR_COMMAND_MAXTIME, + 0, + }; + typedef struct { + const char *progname; + const PCF_STRING_NV *params; + } PCF_SERVICE_DEF; + static const PCF_SERVICE_DEF service_defs[] = { + MAIL_PROGRAM_LOCAL, service_params, + MAIL_PROGRAM_ERROR, service_params, + MAIL_PROGRAM_VIRTUAL, service_params, + MAIL_PROGRAM_SMTP, service_params, + MAIL_PROGRAM_LMTP, service_params, + MAIL_PROGRAM_PIPE, pipe_params, + MAIL_PROGRAM_SPAWN, spawn_params, + 0, + }; + const PCF_STRING_NV *sp; + const char *progname; + const char *service; + PCF_MASTER_ENT *masterp; + ARGV *argv; + const PCF_SERVICE_DEF *sd; + + /* + * Sanity checks. + */ + if (pcf_param_table == 0) + msg_panic("%s: global parameter table is not initialized", myname); + if (pcf_master_table == 0) + msg_panic("%s: master table is not initialized", myname); + + /* + * Extract service names from master.cf and generate service parameter + * information. + */ + for (masterp = pcf_master_table; (argv = masterp->argv) != 0; masterp++) { + + /* + * Add service parameters for message delivery transports or spawn + * programs. + */ + progname = argv->argv[7]; + for (sd = service_defs; sd->progname; sd++) { + if (strcmp(sd->progname, progname) == 0) { + service = argv->argv[0]; + for (sp = sd->params; sp->name; sp++) + pcf_register_service_parameter(service, sp->name, sp->value); + break; + } + } + } +} diff --git a/src/postconf/postconf_unused.c b/src/postconf/postconf_unused.c new file mode 100644 index 0000000..d4416f8 --- /dev/null +++ b/src/postconf/postconf_unused.c @@ -0,0 +1,129 @@ +/*++ +/* NAME +/* postconf_unused 3 +/* SUMMARY +/* report unused parameters +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_flag_unused_main_parameters() +/* +/* void pcf_flag_unused_master_parameters() +/* DESCRIPTION +/* These functions must be called after all parameter information +/* is initialized: built-ins, service-defined and user-defined. +/* In other words, don't call these functions with "postconf +/* -d" which ignores user-defined main.cf settings. +/* +/* pcf_flag_unused_main_parameters() reports unused "name=value" +/* entries in main.cf. +/* +/* pcf_flag_unused_master_parameters() reports unused "-o +/* name=value" entries in master.cf. +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <dict.h> +#include <vstream.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_conf.h> + +/* Application-specific. */ + +#include <postconf.h> + +/* pcf_flag_unused_parameters - warn about unused parameters */ + +static void pcf_flag_unused_parameters(DICT *dict, const char *conf_name, + PCF_MASTER_ENT *local_scope) +{ + const char *myname = "pcf_flag_unused_parameters"; + const char *param_name; + const char *param_value; + int how; + + /* + * Sanity checks. + */ + if (pcf_param_table == 0) + msg_panic("%s: global parameter table is not initialized", myname); + + /* + * Iterate over all entries, and flag parameter names that aren't used + * anywhere. Show the warning message at the end of the output. + */ + if (dict->sequence == 0) + msg_panic("%s: parameter dictionary %s has no iterator", + myname, conf_name); + for (how = DICT_SEQ_FUN_FIRST; + dict->sequence(dict, how, ¶m_name, ¶m_value) == 0; + how = DICT_SEQ_FUN_NEXT) { + if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, param_name) == 0 + && (local_scope == 0 + || PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, param_name) == 0)) { + vstream_fflush(VSTREAM_OUT); + msg_warn("%s/%s: unused parameter: %s=%s", + var_config_dir, conf_name, param_name, param_value); + } + } +} + +/* pcf_flag_unused_main_parameters - warn about unused parameters */ + +void pcf_flag_unused_main_parameters(void) +{ + const char *myname = "pcf_flag_unused_main_parameters"; + DICT *dict; + + /* + * Iterate over all main.cf entries, and flag parameter names that aren't + * used anywhere. + */ + if ((dict = dict_handle(CONFIG_DICT)) == 0) + msg_panic("%s: parameter dictionary %s not found", + myname, CONFIG_DICT); + pcf_flag_unused_parameters(dict, MAIN_CONF_FILE, (PCF_MASTER_ENT *) 0); +} + +/* pcf_flag_unused_master_parameters - warn about unused parameters */ + +void pcf_flag_unused_master_parameters(void) +{ + const char *myname = "pcf_flag_unused_master_parameters"; + PCF_MASTER_ENT *masterp; + DICT *dict; + + /* + * Sanity checks. + */ + if (pcf_master_table == 0) + msg_panic("%s: master table is not initialized", myname); + + /* + * Iterate over all master.cf entries, and flag parameter names that + * aren't used anywhere. + */ + for (masterp = pcf_master_table; masterp->argv != 0; masterp++) + if ((dict = masterp->all_params) != 0) + pcf_flag_unused_parameters(dict, MASTER_CONF_FILE, masterp); +} diff --git a/src/postconf/postconf_user.c b/src/postconf/postconf_user.c new file mode 100644 index 0000000..8d0e726 --- /dev/null +++ b/src/postconf/postconf_user.c @@ -0,0 +1,422 @@ +/*++ +/* NAME +/* postconf_user 3 +/* SUMMARY +/* support for user-defined main.cf parameter names +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_register_user_parameters() +/* DESCRIPTION +/* Postfix has multiple parameter name spaces: the global +/* main.cf parameter name space, and the local parameter name +/* space of each master.cf entry. Parameters in local name +/* spaces take precedence over global parameters. +/* +/* There are three categories of known parameter names: built-in, +/* service-defined (see postconf_service.c), and valid +/* user-defined. +/* +/* There are two categories of valid user-defined parameter +/* names: +/* +/* - Parameters whose user-defined-name appears in the value +/* of smtpd_restriction_classes in main.cf or master.cf. +/* +/* - Parameters whose $user-defined-name appear in the value +/* of "name=value" entries in main.cf or master.cf. +/* +/* - In both cases the parameters must have a +/* "user-defined-name=value" entry in main.cf or master.cf. +/* +/* Other user-defined parameter names are flagged as "unused". +/* +/* pcf_register_user_parameters() scans the global and per-service +/* name spaces for user-defined parameters and flags parameters +/* as "valid" in the global name space (pcf_param_table) or +/* in the per-service name space (valid_params). +/* +/* This function also invokes pcf_register_dbms_parameters() to +/* to instantiate legacy per-dbms parameters, and to examine +/* per-dbms configuration files. This is limited to the content +/* of global and local, built-in and per-service, parameters. +/* 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> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <htable.h> +#include <mac_expand.h> +#include <stringops.h> + +/* Global library. */ + +#include <mail_conf.h> +#include <mail_params.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * Hash with all user-defined names in the global smtpd_restriction_classes + * value. This is used when validating "-o user-defined-name=value" entries + * in master.cf. + */ +static HTABLE *pcf_rest_class_table; + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + + /* + * Macros to make code with obscure constants more readable. + */ +#define NO_SCAN_RESULT ((VSTRING *) 0) +#define NO_SCAN_FILTER ((char *) 0) + +/* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */ + +#define SCAN_USER_PARAMETER_VALUE(value, class, scope) do { \ + PCF_PARAM_CTX _ctx; \ + _ctx.local_scope = (scope); \ + _ctx.param_class = (class); \ + (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \ + NO_SCAN_FILTER, pcf_flag_user_parameter_wrapper, (void *) &_ctx); \ +} while (0) + +/* pcf_convert_user_parameter - get user-defined parameter string value */ + +static const char *pcf_convert_user_parameter(void *unused_ptr) +{ + return (""); /* can't happen */ +} + +/* pcf_flag_user_parameter - flag user-defined name "valid" if it has name=value */ + +static const char *pcf_flag_user_parameter(const char *mac_name, + int param_class, + PCF_MASTER_ENT *local_scope) +{ + const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE; + int user_supplied = 0; + + /* + * If the name=value exists in the local (or global) name space, update + * the local (or global) "valid" parameter name table. + * + * Do not "validate" user-defined parameters whose name appears only as + * macro expansion; this is how Postfix historically implements backwards + * compatibility after a feature name change. + */ + if (local_scope && dict_get(local_scope->all_params, mac_name)) { + user_supplied = 1; + /* $name in master.cf references name=value in master.cf. */ + if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) { + PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name, + param_class, PCF_PARAM_NO_DATA, + pcf_convert_user_parameter); + if (msg_verbose) + msg_info("$%s in %s:%s validates %s=value in %s:%s", + mac_name, MASTER_CONF_FILE, + local_scope->name_space, + mac_name, MASTER_CONF_FILE, + local_scope->name_space); + } + } else if (mail_conf_lookup(mac_name) != 0) { + user_supplied = 1; + /* $name in main/master.cf references name=value in main.cf. */ + if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0) { + PCF_PARAM_TABLE_ENTER(pcf_param_table, mac_name, param_class, + PCF_PARAM_NO_DATA, pcf_convert_user_parameter); + if (msg_verbose) { + if (local_scope) + msg_info("$%s in %s:%s validates %s=value in %s", + mac_name, MASTER_CONF_FILE, + local_scope->name_space, + mac_name, MAIN_CONF_FILE); + else + msg_info("$%s in %s validates %s=value in %s", + mac_name, MAIN_CONF_FILE, + mac_name, MAIN_CONF_FILE); + } + } + } + if (local_scope == 0) { + for (local_scope = pcf_master_table; local_scope->argv; local_scope++) { + if (local_scope->all_params != 0 + && dict_get(local_scope->all_params, mac_name) != 0) { + user_supplied = 1; + /* $name in main.cf references name=value in master.cf. */ + if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) { + PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name, + param_class, PCF_PARAM_NO_DATA, + pcf_convert_user_parameter); + if (msg_verbose) + msg_info("$%s in %s validates %s=value in %s:%s", + mac_name, MAIN_CONF_FILE, + mac_name, MASTER_CONF_FILE, + local_scope->name_space); + } + } + } + } + + /* + * Warn about a $name that has no user-supplied explicit value or + * Postfix-supplied default value. We don't enforce this for legacy DBMS + * parameters because they exist only for backwards compatibility, so we + * don't bother to figure out which parameters come without defaults. + */ + if (user_supplied == 0 && (param_class & PCF_PARAM_FLAG_DBMS) == 0 + && PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0) + msg_warn("%s/%s: undefined parameter: %s", + var_config_dir, source, mac_name); + return (0); +} + +/* pcf_flag_user_parameter_wrapper - mac_expand call-back helper */ + +static const char *pcf_flag_user_parameter_wrapper(const char *mac_name, + int unused_mode, + void *context) +{ + PCF_PARAM_CTX *ctx = (PCF_PARAM_CTX *) context; + + return (pcf_flag_user_parameter(mac_name, ctx->param_class, ctx->local_scope)); +} + +/* pcf_lookup_eval - generalized mail_conf_lookup_eval */ + +static const char *pcf_lookup_eval(const char *dict_name, const char *name) +{ + const char *value; + +#define RECURSIVE 1 + + if ((value = dict_lookup(dict_name, name)) != 0) + value = dict_eval(dict_name, value, RECURSIVE); + return (value); +} + +/* pcf_scan_user_parameter_namespace - scan parameters in name space */ + +static void pcf_scan_user_parameter_namespace(const char *dict_name, + PCF_MASTER_ENT *local_scope) +{ + const char *myname = "pcf_scan_user_parameter_namespace"; + const char *class_list; + char *saved_class_list; + char *cp; + DICT *dict; + char *param_name; + int how; + const char *cparam_name; + const char *cparam_value; + PCF_PARAM_NODE *node; + const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE; + + /* + * Flag parameter names in smtpd_restriction_classes as "valid", but only + * if they have a "name=value" entry. If we are in not in a local name + * space, update the global restriction class name table, so that we can + * query the global table from within a local master.cf name space. + */ + if ((class_list = pcf_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0) { + cp = saved_class_list = mystrdup(class_list); + while ((param_name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) { + if (local_scope == 0 + && htable_locate(pcf_rest_class_table, param_name) == 0) + htable_enter(pcf_rest_class_table, param_name, ""); + pcf_flag_user_parameter(param_name, PCF_PARAM_FLAG_USER, local_scope); + } + myfree(saved_class_list); + } + + /* + * For all "name=value" instances: a) if the name space is local and the + * name appears in the global restriction class table, flag the name as + * "valid" in the local name space; b) scan the value for macro + * expansions of unknown parameter names, and flag those parameter names + * as "valid" if they have a "name=value" entry. + * + * We delete name=value entries for read-only parameters, to maintain + * compatibility with Postfix programs that ignore such settings. + */ + if ((dict = dict_handle(dict_name)) == 0) + msg_panic("%s: parameter dictionary %s not found", + myname, dict_name); + if (dict->sequence == 0) + msg_panic("%s: parameter dictionary %s has no iterator", + myname, dict_name); + for (how = DICT_SEQ_FUN_FIRST; + dict->sequence(dict, how, &cparam_name, &cparam_value) == 0; + how = DICT_SEQ_FUN_NEXT) { + if (local_scope != 0 + && PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, cparam_name) == 0 + && htable_locate(pcf_rest_class_table, cparam_name) != 0) + PCF_PARAM_TABLE_ENTER(local_scope->valid_names, cparam_name, + PCF_PARAM_FLAG_USER, PCF_PARAM_NO_DATA, + pcf_convert_user_parameter); + if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, cparam_name)) != 0) { + if (PCF_READONLY_PARAMETER(node)) { + msg_warn("%s/%s: read-only parameter assignment: %s=%s", + var_config_dir, source, cparam_name, cparam_value); + /* Can't use dict_del() with Postfix<2.10 htable_sequence(). */ + if (dict_del(dict, cparam_name) != 0) + msg_panic("%s: can't delete %s/%s parameter entry for %s", + myname, var_config_dir, source, cparam_name); + continue; + } + /* Re-label legacy parameter as user-defined, so it's printed. */ + if (PCF_LEGACY_PARAMETER(node)) + PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_USER); + /* Skip "do not expand" parameters. */ + if (PCF_RAW_PARAMETER(node)) + continue; + } + SCAN_USER_PARAMETER_VALUE(cparam_value, PCF_PARAM_FLAG_USER, local_scope); +#ifdef LEGACY_DBMS_SUPPORT + + /* + * Scan global or local parameters that are built-in or per-service + * (when node == 0, the parameter doesn't exist in the global + * namespace and therefore it can't be built-in or per-service). + */ + if (node != 0 + && (PCF_BUILTIN_PARAMETER(node) || PCF_SERVICE_PARAMETER(node))) + pcf_register_dbms_parameters(cparam_value, pcf_flag_user_parameter, + local_scope); +#endif + } +} + +/* pcf_scan_default_parameter_values - scan parameters at implicit defaults */ + +static void pcf_scan_default_parameter_values(HTABLE *valid_params, + const char *dict_name, + PCF_MASTER_ENT *local_scope) +{ + const char *myname = "pcf_scan_default_parameter_values"; + PCF_PARAM_INFO **list; + PCF_PARAM_INFO **ht; + const char *param_value; + + list = PCF_PARAM_TABLE_LIST(valid_params); + for (ht = list; *ht; ht++) { + /* Skip "do not expand" parameters. */ + if (PCF_RAW_PARAMETER(PCF_PARAM_INFO_NODE(*ht))) + continue; + /* Skip parameters with a non-default value. */ + if (dict_lookup(dict_name, PCF_PARAM_INFO_NAME(*ht))) + continue; + if ((param_value = pcf_convert_param_node(PCF_SHOW_DEFS, PCF_PARAM_INFO_NAME(*ht), + PCF_PARAM_INFO_NODE(*ht))) == 0) + msg_panic("%s: parameter %s has no default value", + myname, PCF_PARAM_INFO_NAME(*ht)); + SCAN_USER_PARAMETER_VALUE(param_value, PCF_PARAM_FLAG_USER, local_scope); + /* No need to scan default values for legacy DBMS configuration. */ + } + myfree((void *) list); +} + +/* pcf_register_user_parameters - add parameters with user-defined names */ + +void pcf_register_user_parameters(void) +{ + const char *myname = "pcf_register_user_parameters"; + PCF_MASTER_ENT *masterp; + ARGV *argv; + char *arg; + char *aval; + int field; + char *saved_arg; + char *param_name; + char *param_value; + DICT *dict; + + /* + * Sanity checks. + */ + if (pcf_param_table == 0) + msg_panic("%s: global parameter table is not initialized", myname); + if (pcf_master_table == 0) + msg_panic("%s: master table is not initialized", myname); + if (pcf_rest_class_table != 0) + msg_panic("%s: restriction class table is already initialized", myname); + + /* + * Initialize the table with global restriction class names. + */ + pcf_rest_class_table = htable_create(1); + + /* + * Initialize the per-service parameter name spaces. + */ + for (masterp = pcf_master_table; (argv = masterp->argv) != 0; masterp++) { + for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) { + arg = argv->argv[field]; + if (arg[0] != '-' || strcmp(arg, "--") == 0) + break; + if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0 + || (aval = argv->argv[field + 1]) == 0) + continue; + if (strcmp(arg, "-o") == 0) { + saved_arg = mystrdup(aval); + if (split_nameval(saved_arg, ¶m_name, ¶m_value) == 0) + dict_update(masterp->name_space, param_name, param_value); + myfree(saved_arg); + } + field += 1; + } + if ((dict = dict_handle(masterp->name_space)) != 0) { + masterp->all_params = dict; + masterp->valid_names = htable_create(1); + } + } + + /* + * Scan the "-o parameter=value" instances in each master.cf name space. + */ + for (masterp = pcf_master_table; masterp->argv != 0; masterp++) + if (masterp->all_params != 0) + pcf_scan_user_parameter_namespace(masterp->name_space, masterp); + + /* + * Scan parameter values that are left at their defaults in the global + * name space. Some defaults contain the $name of an obsolete parameter + * for backwards compatilility purposes. We might warn that an explicit + * name=value is obsolete, but we must not warn that the parameter is + * unused. + */ + pcf_scan_default_parameter_values(pcf_param_table, CONFIG_DICT, + (PCF_MASTER_ENT *) 0); + + /* + * Scan the explicit name=value entries in the global name space. + */ + pcf_scan_user_parameter_namespace(CONFIG_DICT, (PCF_MASTER_ENT *) 0); +} diff --git a/src/postconf/test1.ref b/src/postconf/test1.ref new file mode 100644 index 0000000..2aa9903 --- /dev/null +++ b/src/postconf/test1.ref @@ -0,0 +1,4 @@ +./postconf: warning: ./main.cf: undefined parameter: bar +config_directory = . +foo = yes +smtpd_restriction_classes = foo bar diff --git a/src/postconf/test10.ref b/src/postconf/test10.ref new file mode 100644 index 0000000..5bb6891 --- /dev/null +++ b/src/postconf/test10.ref @@ -0,0 +1,2 @@ +./postconf: warning: unmatched request: "bar/inet" +./postconf: warning: unmatched request: "foo/unix" diff --git a/src/postconf/test11.ref b/src/postconf/test11.ref new file mode 100644 index 0000000..c4cc727 --- /dev/null +++ b/src/postconf/test11.ref @@ -0,0 +1,2 @@ +foo inet - n n - 0 spawn +bar unix - n n - 0 spawn diff --git a/src/postconf/test12.ref b/src/postconf/test12.ref new file mode 100644 index 0000000..a9f5eb4 --- /dev/null +++ b/src/postconf/test12.ref @@ -0,0 +1,2 @@ +foo inet - n n - 0 spawn -o always_bcc=$bar -o +foo inet - n n - 0 spawn -o always_bcc=$bar -o diff --git a/src/postconf/test13.ref b/src/postconf/test13.ref new file mode 100644 index 0000000..09787fa --- /dev/null +++ b/src/postconf/test13.ref @@ -0,0 +1,3 @@ +bar = yes +config_directory = . +./postconf: warning: ./main.cf: unused parameter: baz=xx diff --git a/src/postconf/test14.ref b/src/postconf/test14.ref new file mode 100644 index 0000000..98884f3 --- /dev/null +++ b/src/postconf/test14.ref @@ -0,0 +1,3 @@ +config_directory = . +smtpd_restriction_classes = bar +./postconf: warning: ./master.cf: unused parameter: baz=xx diff --git a/src/postconf/test15.ref b/src/postconf/test15.ref new file mode 100644 index 0000000..2a15ca8 --- /dev/null +++ b/src/postconf/test15.ref @@ -0,0 +1,3 @@ +baz = yy +config_directory = . +./postconf: warning: ./main.cf: unused parameter: bar=xx diff --git a/src/postconf/test16.ref b/src/postconf/test16.ref new file mode 100644 index 0000000..a1c2e06 --- /dev/null +++ b/src/postconf/test16.ref @@ -0,0 +1,2 @@ +./postconf: warning: open ./master.cf: No such file or directory +config_directory = . diff --git a/src/postconf/test17.ref b/src/postconf/test17.ref new file mode 100644 index 0000000..bcfb716 --- /dev/null +++ b/src/postconf/test17.ref @@ -0,0 +1 @@ +./postconf: fatal: open ./master.cf: No such file or directory diff --git a/src/postconf/test18.ref b/src/postconf/test18.ref new file mode 100644 index 0000000..09224a6 --- /dev/null +++ b/src/postconf/test18.ref @@ -0,0 +1,3 @@ +config_directory = . +smtpd_client_connection_limit_exceptions = yyy +virtual_maps = xxx diff --git a/src/postconf/test19.ref b/src/postconf/test19.ref new file mode 100644 index 0000000..5a286c6 --- /dev/null +++ b/src/postconf/test19.ref @@ -0,0 +1,3 @@ +config_directory = . +default_rbl_reply = $bbbb +forward_path = $aaaa diff --git a/src/postconf/test2.ref b/src/postconf/test2.ref new file mode 100644 index 0000000..49af249 --- /dev/null +++ b/src/postconf/test2.ref @@ -0,0 +1,3 @@ +config_directory = . +./postconf: warning: ./main.cf: unused parameter: restriction_classes=foo bar +./postconf: warning: ./main.cf: unused parameter: foo=yes diff --git a/src/postconf/test20.ref b/src/postconf/test20.ref new file mode 100644 index 0000000..0a430d2 --- /dev/null +++ b/src/postconf/test20.ref @@ -0,0 +1,4 @@ +./postconf: warning: ./master.cf: undefined parameter: bar +./postconf: warning: ./master.cf: undefined parameter: baz +foo inet - n n - 0 spawn + -o always_bcc=$bar$baz diff --git a/src/postconf/test21.ref b/src/postconf/test21.ref new file mode 100644 index 0000000..423197e --- /dev/null +++ b/src/postconf/test21.ref @@ -0,0 +1,3 @@ +config_directory = . +forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx xxxxxxxxxxxxx + xxxxxxxxxxxxxx diff --git a/src/postconf/test22.ref b/src/postconf/test22.ref new file mode 100644 index 0000000..1e4629f --- /dev/null +++ b/src/postconf/test22.ref @@ -0,0 +1,16 @@ +whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost +whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount +whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan +whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit +whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit +whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback +whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback +whatevershebrings_destination_rate_delay = $default_destination_rate_delay +whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit +whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit +whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency +whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots +whatevershebrings_recipient_limit = $default_recipient_limit +whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay +whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit +whatevershebrings_transport_rate_delay = $default_transport_rate_delay diff --git a/src/postconf/test23.ref b/src/postconf/test23.ref new file mode 100644 index 0000000..d1f9a90 --- /dev/null +++ b/src/postconf/test23.ref @@ -0,0 +1,2 @@ +always_bcc = yes +config_directory = . diff --git a/src/postconf/test24.ref b/src/postconf/test24.ref new file mode 100644 index 0000000..2292793 --- /dev/null +++ b/src/postconf/test24.ref @@ -0,0 +1 @@ +name = value diff --git a/src/postconf/test25.ref b/src/postconf/test25.ref new file mode 100644 index 0000000..1e4629f --- /dev/null +++ b/src/postconf/test25.ref @@ -0,0 +1,16 @@ +whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost +whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount +whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan +whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit +whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit +whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback +whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback +whatevershebrings_destination_rate_delay = $default_destination_rate_delay +whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit +whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit +whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency +whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots +whatevershebrings_recipient_limit = $default_recipient_limit +whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay +whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit +whatevershebrings_transport_rate_delay = $default_transport_rate_delay diff --git a/src/postconf/test26.ref b/src/postconf/test26.ref new file mode 100644 index 0000000..0b33025 --- /dev/null +++ b/src/postconf/test26.ref @@ -0,0 +1,3 @@ +always_bcc = yes +config_directory = . +name = value diff --git a/src/postconf/test27.ref b/src/postconf/test27.ref new file mode 100644 index 0000000..1e4629f --- /dev/null +++ b/src/postconf/test27.ref @@ -0,0 +1,16 @@ +whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost +whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount +whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan +whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit +whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit +whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback +whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback +whatevershebrings_destination_rate_delay = $default_destination_rate_delay +whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit +whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit +whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency +whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots +whatevershebrings_recipient_limit = $default_recipient_limit +whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay +whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit +whatevershebrings_transport_rate_delay = $default_transport_rate_delay diff --git a/src/postconf/test28.ref b/src/postconf/test28.ref new file mode 100644 index 0000000..cb117b0 --- /dev/null +++ b/src/postconf/test28.ref @@ -0,0 +1,10 @@ +aap_domain = whatever +config_directory = . +db = memcache +header_checks = ldap:hh +hh_domain = whatever +yy = aap +zz = $yy +./postconf: warning: ./main.cf: unused parameter: foo_domain=bar +./postconf: warning: ./main.cf: unused parameter: aa_domain=whatever +./postconf: warning: ./main.cf: unused parameter: xx=proxy:ldap:foo diff --git a/src/postconf/test29.ref b/src/postconf/test29.ref new file mode 100644 index 0000000..d44e38d --- /dev/null +++ b/src/postconf/test29.ref @@ -0,0 +1,16 @@ +config_directory = . +./postconf: warning: ./main.cf: unused parameter: sqlitexx=proxy:sqlite:sqlitefoo +./postconf: warning: ./main.cf: unused parameter: pgsqlxx=proxy:pgsql:pgsqlfoo +./postconf: warning: ./main.cf: unused parameter: ldapfoo_domain=bar +./postconf: warning: ./main.cf: unused parameter: memcachefoo_domainx=bar +./postconf: warning: ./main.cf: unused parameter: sqlitefoo_domainx=bar +./postconf: warning: ./main.cf: unused parameter: sqlitefoo_domain=bar +./postconf: warning: ./main.cf: unused parameter: memcachexx=proxy:memcache:memcachefoo +./postconf: warning: ./main.cf: unused parameter: mysqlxx=proxy:mysql:mysqlfoo +./postconf: warning: ./main.cf: unused parameter: ldapxx=proxy:ldap:ldapfoo +./postconf: warning: ./main.cf: unused parameter: ldapfoo_domainx=bar +./postconf: warning: ./main.cf: unused parameter: memcachefoo_domain=bar +./postconf: warning: ./main.cf: unused parameter: pgsqlfoo_domainx=bar +./postconf: warning: ./main.cf: unused parameter: mysqlfoo_domainx=bar +./postconf: warning: ./main.cf: unused parameter: mysqlfoo_domain=bar +./postconf: warning: ./main.cf: unused parameter: pgsqlfoo_domain=bar diff --git a/src/postconf/test3.ref b/src/postconf/test3.ref new file mode 100644 index 0000000..bdeff73 --- /dev/null +++ b/src/postconf/test3.ref @@ -0,0 +1,4 @@ +always_bcc = $bar +bar = $foo +config_directory = . +foo = yes diff --git a/src/postconf/test30.ref b/src/postconf/test30.ref new file mode 100644 index 0000000..706a429 --- /dev/null +++ b/src/postconf/test30.ref @@ -0,0 +1,7 @@ +config_directory = . +p1 = xx +p2 = xx +p3 = xx +p4 = xx +./postconf: warning: ./master.cf: unused parameter: bodyx_checks=$p2 +./postconf: warning: ./master.cf: unused parameter: headerx_checks=$p4 diff --git a/src/postconf/test31.ref b/src/postconf/test31.ref new file mode 100644 index 0000000..6fda09c --- /dev/null +++ b/src/postconf/test31.ref @@ -0,0 +1,3 @@ +config_directory = . +smtpd_helo_restrictions = whatever +smtpd_sender_restrictions = whatever diff --git a/src/postconf/test32.ref b/src/postconf/test32.ref new file mode 100644 index 0000000..43e2b7b --- /dev/null +++ b/src/postconf/test32.ref @@ -0,0 +1 @@ +fast_flush_domains = whatever diff --git a/src/postconf/test33.ref b/src/postconf/test33.ref new file mode 100644 index 0000000..7b3f52d --- /dev/null +++ b/src/postconf/test33.ref @@ -0,0 +1 @@ +always_bcc = whatever diff --git a/src/postconf/test34.ref b/src/postconf/test34.ref new file mode 100644 index 0000000..e771293 --- /dev/null +++ b/src/postconf/test34.ref @@ -0,0 +1,4 @@ +./postconf: warning: ./main.cf: read-only parameter assignment: process_id=yyy +./postconf: warning: ./main.cf: read-only parameter assignment: process_name=xxx +mydestination = whatever +process_name = postconf diff --git a/src/postconf/test35.ref b/src/postconf/test35.ref new file mode 100644 index 0000000..dc27f39 --- /dev/null +++ b/src/postconf/test35.ref @@ -0,0 +1,3 @@ +./postconf: warning: ./master.cf: read-only parameter assignment: process_id=bbb +./postconf: warning: ./master.cf: read-only parameter assignment: process_name=aaa +process_name = postconf diff --git a/src/postconf/test36.ref b/src/postconf/test36.ref new file mode 100644 index 0000000..bf09921 --- /dev/null +++ b/src/postconf/test36.ref @@ -0,0 +1,4 @@ +./postconf: warning: ./main.cf: undefined parameter: virtual_mapx +config_directory = . +mydestination = +virtual_alias_maps = diff --git a/src/postconf/test37.ref b/src/postconf/test37.ref new file mode 100644 index 0000000..8ef6d6d --- /dev/null +++ b/src/postconf/test37.ref @@ -0,0 +1,4 @@ +whatever unix - n n - 0 other + -o mydestination=yyy + -o always_bcc=ccc + -o aaa=ccc diff --git a/src/postconf/test38.ref b/src/postconf/test38.ref new file mode 100644 index 0000000..186ffc3 --- /dev/null +++ b/src/postconf/test38.ref @@ -0,0 +1 @@ +bar unix - n n - 0 other -o aaa=ccc diff --git a/src/postconf/test39.ref b/src/postconf/test39.ref new file mode 100644 index 0000000..35b078d --- /dev/null +++ b/src/postconf/test39.ref @@ -0,0 +1,2 @@ +foo unix - n n - 0 other +baz unix - n n - 0 other diff --git a/src/postconf/test4.ref b/src/postconf/test4.ref new file mode 100644 index 0000000..6ec1913 --- /dev/null +++ b/src/postconf/test4.ref @@ -0,0 +1,3 @@ +bar = $foo +config_directory = . +foo = yes diff --git a/src/postconf/test40.ref b/src/postconf/test40.ref new file mode 100644 index 0000000..fa3a5d7 --- /dev/null +++ b/src/postconf/test40.ref @@ -0,0 +1,7 @@ +foo unix - n n - 0 other -v + -o aaa=bbb + -v + -o ccc=bbb + -v + -o ddd=bbb +./postconf: warning: ./master.cf: unused parameter: ddd=$ccc diff --git a/src/postconf/test41.ref b/src/postconf/test41.ref new file mode 100644 index 0000000..f8200d4 --- /dev/null +++ b/src/postconf/test41.ref @@ -0,0 +1,18 @@ +foo unix - n n - 0 other +bar unix - n n - 0 other + -o xxx=yyy + -o aaa=bbb +baz unix - n n - 0 other +./postconf: warning: ./master.cf: unused parameter: aaa=bbb +./postconf: warning: ./master.cf: unused parameter: xxx=yyy +foo unix - n n - 0 other +bar unix - n n - 0 other + -o xxx=YYY + -o aaa=BBB +baz unix - n n - 0 other +./postconf: warning: ./master.cf: unused parameter: aaa=BBB +./postconf: warning: ./master.cf: unused parameter: xxx=YYY +bar/unix/aaa = BBB +bar/unix/xxx = YYY +./postconf: warning: ./master.cf: unused parameter: aaa=BBB +./postconf: warning: ./master.cf: unused parameter: xxx=YYY diff --git a/src/postconf/test42.ref b/src/postconf/test42.ref new file mode 100644 index 0000000..80676f3 --- /dev/null +++ b/src/postconf/test42.ref @@ -0,0 +1,14 @@ +foo unix - n n - 0 other +bar unix - n n - 0 other + -o xxx=yyy + -o aaa=bbb +baz unix - n n - 0 other +./postconf: warning: ./master.cf: unused parameter: aaa=bbb +./postconf: warning: ./master.cf: unused parameter: xxx=yyy +bar/unix/aaa = bbb +bar/unix/xxx = yyy +./postconf: warning: ./master.cf: unused parameter: aaa=bbb +./postconf: warning: ./master.cf: unused parameter: xxx=yyy +foo unix - n n - 0 other +bar unix - n n - 0 other +baz unix - n n - 0 other diff --git a/src/postconf/test43.ref b/src/postconf/test43.ref new file mode 100644 index 0000000..2bfea0e --- /dev/null +++ b/src/postconf/test43.ref @@ -0,0 +1,6 @@ +foo unix - n n - 0 other +bar unix - n y - 0 aa -stuff + -o bb=cc + dd +baz unix - n n - 0 other +./postconf: warning: ./master.cf: unused parameter: bb=cc diff --git a/src/postconf/test44.ref b/src/postconf/test44.ref new file mode 100644 index 0000000..b8cf23d --- /dev/null +++ b/src/postconf/test44.ref @@ -0,0 +1,6 @@ +foo unix - n n - 0 other +xx inet - n n - 0 aa -stuff + -o bb=cc + dd +baz unix - n n - 0 other +./postconf: warning: ./master.cf: unused parameter: bb=cc diff --git a/src/postconf/test45.ref b/src/postconf/test45.ref new file mode 100644 index 0000000..8ae54a0 --- /dev/null +++ b/src/postconf/test45.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid type field "xxxx" in "bar xxxx - n n - 0 other" diff --git a/src/postconf/test46.ref b/src/postconf/test46.ref new file mode 100644 index 0000000..aac7c1f --- /dev/null +++ b/src/postconf/test46.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid private field "X" in "bar inet X n n - 0 other" diff --git a/src/postconf/test47.ref b/src/postconf/test47.ref new file mode 100644 index 0000000..e8d46ed --- /dev/null +++ b/src/postconf/test47.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid unprivileged field "X" in "bar inet - X n - 0 other" diff --git a/src/postconf/test48.ref b/src/postconf/test48.ref new file mode 100644 index 0000000..9403117 --- /dev/null +++ b/src/postconf/test48.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid chroot field "X" in "bar inet - n X - 0 other" diff --git a/src/postconf/test49.ref b/src/postconf/test49.ref new file mode 100644 index 0000000..f52b171 --- /dev/null +++ b/src/postconf/test49.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid wakeup field "X" in "bar inet - n n X 0 other" diff --git a/src/postconf/test4b.ref b/src/postconf/test4b.ref new file mode 100644 index 0000000..5dda033 --- /dev/null +++ b/src/postconf/test4b.ref @@ -0,0 +1,5 @@ +always_bcc = $foo +bar = aaa +biff = $bar +config_directory = . +./postconf: warning: ./master.cf: unused parameter: baz=zzz diff --git a/src/postconf/test5.ref b/src/postconf/test5.ref new file mode 100644 index 0000000..41bee93 --- /dev/null +++ b/src/postconf/test5.ref @@ -0,0 +1 @@ +config_directory = . diff --git a/src/postconf/test50.ref b/src/postconf/test50.ref new file mode 100644 index 0000000..41cdc44 --- /dev/null +++ b/src/postconf/test50.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid process_limit field "X" in "bar inet - n n - X other" diff --git a/src/postconf/test51.ref b/src/postconf/test51.ref new file mode 100644 index 0000000..a37d749 --- /dev/null +++ b/src/postconf/test51.ref @@ -0,0 +1 @@ +./postconf: fatal: invalid wakeup field "X?" in "bar inet - n n X? 0 other" diff --git a/src/postconf/test52.ref b/src/postconf/test52.ref new file mode 100644 index 0000000..8902db9 --- /dev/null +++ b/src/postconf/test52.ref @@ -0,0 +1 @@ +baz unix - n n 0 0 other diff --git a/src/postconf/test53.ref b/src/postconf/test53.ref new file mode 100644 index 0000000..c81cf65 --- /dev/null +++ b/src/postconf/test53.ref @@ -0,0 +1,3 @@ +foo unix - n n - 0 other +#bar inet - n n 0 0 other +baz unix - n n 0 0 other diff --git a/src/postconf/test54.ref b/src/postconf/test54.ref new file mode 100644 index 0000000..045a9f6 --- /dev/null +++ b/src/postconf/test54.ref @@ -0,0 +1,3 @@ +#foo unix - n n - 0 other +#bar inet - n n 0 0 other +baz unix - n n 0 0 other diff --git a/src/postconf/test55.ref b/src/postconf/test55.ref new file mode 100644 index 0000000..96c11b0 --- /dev/null +++ b/src/postconf/test55.ref @@ -0,0 +1,3 @@ +foo unix - n n - 0 other +#bar inet - n n 0 0 other +#baz unix - n n 0 0 other diff --git a/src/postconf/test56.ref b/src/postconf/test56.ref new file mode 100644 index 0000000..ac845f6 --- /dev/null +++ b/src/postconf/test56.ref @@ -0,0 +1,5 @@ +foo unix - n n - 0 other +#bar inet - n n 0 0 other +# -o first +# -o second +baz unix - n n 0 0 other diff --git a/src/postconf/test57.ref b/src/postconf/test57.ref new file mode 100644 index 0000000..362fd16 --- /dev/null +++ b/src/postconf/test57.ref @@ -0,0 +1,10 @@ +./postconf: warning: ./main.cf: undefined parameter: z +./postconf: warning: ./main.cf: undefined parameter: z +bar = y-value +baz = +config_directory = . +t1 = Postfix 2.11 compatible +x = x-value +y = y-value +./postconf: warning: ./main.cf: unused parameter: t2=$t1 +./postconf: warning: ./main.cf: unused parameter: foo=$bar$baz diff --git a/src/postconf/test58.ref b/src/postconf/test58.ref new file mode 100644 index 0000000..1e74c4a --- /dev/null +++ b/src/postconf/test58.ref @@ -0,0 +1,8 @@ +./postconf: warning: main.cf: syntax error after '}' in "{ldap:xxx, memcache:yy}x" +./postconf: warning: main.cf: missing '}' in "{xx" +config_directory = . +mydestination = foo bar pipemap:{ldap:xxx, memcache:yy}x randmap:{xx +xxx_domain = foo +yy_backup = bbb +./postconf: warning: ./main.cf: unused parameter: yy_bogus=bbb +./postconf: warning: ./main.cf: unused parameter: xxx_bogus=foo diff --git a/src/postconf/test59.ref b/src/postconf/test59.ref new file mode 100644 index 0000000..3a7e57f --- /dev/null +++ b/src/postconf/test59.ref @@ -0,0 +1,10 @@ +./postconf: warning: master.cf: syntax error after '}' in "{ arg2a arg2b }x" +./postconf: warning: master.cf: missing '}' in "{ arg3a arg3b " +foo unix - n n - 0 other +bar inet - n n 0 0 other + -o name1=value1 + -o {name2=value2a value2b} + arg1a arg1b {arg2a arg2b} {arg3a arg3b} +baz unix - n n 0 0 other +./postconf: warning: ./master.cf: unused parameter: name1=value1 +./postconf: warning: ./master.cf: unused parameter: name2=value2a value2b diff --git a/src/postconf/test6.ref b/src/postconf/test6.ref new file mode 100644 index 0000000..55e47f2 --- /dev/null +++ b/src/postconf/test6.ref @@ -0,0 +1,17 @@ +whatevershebrings_delivery_slot_cost = $default_delivery_slot_cost +whatevershebrings_delivery_slot_discount = $default_delivery_slot_discount +whatevershebrings_delivery_slot_loan = $default_delivery_slot_loan +whatevershebrings_destination_concurrency_failed_cohort_limit = $default_destination_concurrency_failed_cohort_limit +whatevershebrings_destination_concurrency_limit = $default_destination_concurrency_limit +whatevershebrings_destination_concurrency_negative_feedback = $default_destination_concurrency_negative_feedback +whatevershebrings_destination_concurrency_positive_feedback = $default_destination_concurrency_positive_feedback +whatevershebrings_destination_rate_delay = $default_destination_rate_delay +whatevershebrings_destination_recipient_limit = $default_destination_recipient_limit +whatevershebrings_extra_recipient_limit = $default_extra_recipient_limit +whatevershebrings_initial_destination_concurrency = $initial_destination_concurrency +whatevershebrings_minimum_delivery_slots = $default_minimum_delivery_slots +whatevershebrings_recipient_limit = $default_recipient_limit +whatevershebrings_recipient_refill_delay = $default_recipient_refill_delay +whatevershebrings_recipient_refill_limit = $default_recipient_refill_limit +whatevershebrings_time_limit = $command_time_limit +whatevershebrings_transport_rate_delay = $default_transport_rate_delay diff --git a/src/postconf/test60.ref b/src/postconf/test60.ref new file mode 100644 index 0000000..5a6f3ca --- /dev/null +++ b/src/postconf/test60.ref @@ -0,0 +1,8 @@ +foo +unix +- +n +n +- +0 +other -o always_bcc=bar diff --git a/src/postconf/test61.ref b/src/postconf/test61.ref new file mode 100644 index 0000000..5716ca5 --- /dev/null +++ b/src/postconf/test61.ref @@ -0,0 +1 @@ +bar diff --git a/src/postconf/test62.ref b/src/postconf/test62.ref new file mode 100644 index 0000000..ffc4913 --- /dev/null +++ b/src/postconf/test62.ref @@ -0,0 +1,8 @@ +foo/unix/service +foo/unix/type +foo/unix/private +foo/unix/unprivileged +foo/unix/chroot +foo/unix/wakeup +foo/unix/process_limit +foo/unix/command diff --git a/src/postconf/test63.ref b/src/postconf/test63.ref new file mode 100644 index 0000000..b609d62 --- /dev/null +++ b/src/postconf/test63.ref @@ -0,0 +1 @@ +foo/unix/always_bcc diff --git a/src/postconf/test64.ref b/src/postconf/test64.ref new file mode 100644 index 0000000..595620c --- /dev/null +++ b/src/postconf/test64.ref @@ -0,0 +1 @@ +relayhost = relay-from-main.cf diff --git a/src/postconf/test65.ref b/src/postconf/test65.ref new file mode 100644 index 0000000..6bc7fd8 --- /dev/null +++ b/src/postconf/test65.ref @@ -0,0 +1 @@ +relayhost = relay-from-cmd-line diff --git a/src/postconf/test66.ref b/src/postconf/test66.ref new file mode 100644 index 0000000..bd35822 --- /dev/null +++ b/src/postconf/test66.ref @@ -0,0 +1,5 @@ +./postconf: warning: ldap:PWD/test66.cf: unused parameter: junk=junk +./postconf: warning: mysql:PWD/test66.cf: unused parameter: junk=junk +./postconf: warning: pgsql:PWD/test66.cf: unused parameter: junk=junk +./postconf: warning: sqlite:PWD/test66.cf: unused parameter: junk=junk +./postconf: warning: memcache:PWD/test66.cf: unused parameter: junk=junk diff --git a/src/postconf/test67.ref b/src/postconf/test67.ref new file mode 100644 index 0000000..03def6f --- /dev/null +++ b/src/postconf/test67.ref @@ -0,0 +1,10 @@ +smtp inet n - n - - smtpd + -o test1_process_name=smtpd + -o test1_service_name=smtp +smtp unix n - n - - smtp + -o test2_process_name=smtp + -o test2_service_name=smtp +./postconf: warning: ./master.cf: unused parameter: test1_process_name=$process_name +./postconf: warning: ./master.cf: unused parameter: test1_service_name=$service_name +./postconf: warning: ./master.cf: unused parameter: test2_process_name=$process_name +./postconf: warning: ./master.cf: unused parameter: test2_service_name=$service_name diff --git a/src/postconf/test68.ref b/src/postconf/test68.ref new file mode 100644 index 0000000..e2d7c7d --- /dev/null +++ b/src/postconf/test68.ref @@ -0,0 +1,5 @@ +./postconf: warning: ldap:PWD/test68.cf: unused parameter: junk=junk +./postconf: warning: mysql:PWD/test68.cf: unused parameter: junk=junk +./postconf: warning: pgsql:PWD/test68.cf: unused parameter: junk=junk +./postconf: warning: sqlite:PWD/test68.cf: unused parameter: junk=junk +./postconf: warning: memcache:PWD/test68.cf: unused parameter: junk=junk diff --git a/src/postconf/test69.ref b/src/postconf/test69.ref new file mode 100644 index 0000000..465a91f --- /dev/null +++ b/src/postconf/test69.ref @@ -0,0 +1,2 @@ +./postconf: warning: ldap:PWD/test69.cf: unused parameter: junk=junk +config_directory = . diff --git a/src/postconf/test7.ref b/src/postconf/test7.ref new file mode 100644 index 0000000..1bd731b --- /dev/null +++ b/src/postconf/test7.ref @@ -0,0 +1 @@ +whatevershebrings_time_limit = $command_time_limit diff --git a/src/postconf/test8.ref b/src/postconf/test8.ref new file mode 100644 index 0000000..af47b44 --- /dev/null +++ b/src/postconf/test8.ref @@ -0,0 +1 @@ +whatevershebrings_time_limit = 1 diff --git a/src/postconf/test9.ref b/src/postconf/test9.ref new file mode 100644 index 0000000..b62303f --- /dev/null +++ b/src/postconf/test9.ref @@ -0,0 +1 @@ +foo inet - n n - 0 spawn |