diff options
Diffstat (limited to '')
-rw-r--r-- | src/util/Makefile.am | 90 | ||||
-rw-r--r-- | src/util/Makefile.in | 1049 | ||||
-rwxr-xr-x | src/util/dovecot-sysreport | 212 | ||||
-rw-r--r-- | src/util/gdbhelper.c | 71 | ||||
-rw-r--r-- | src/util/health-check-settings.c | 31 | ||||
-rwxr-xr-x | src/util/health-check.sh | 32 | ||||
-rw-r--r-- | src/util/maildirlock.c | 106 | ||||
-rw-r--r-- | src/util/rawlog.c | 428 | ||||
-rw-r--r-- | src/util/script-login.c | 249 | ||||
-rw-r--r-- | src/util/script.c | 321 | ||||
-rw-r--r-- | src/util/tcpwrap-settings.c | 35 | ||||
-rw-r--r-- | src/util/tcpwrap.c | 131 | ||||
-rw-r--r-- | src/util/test-fs.c | 449 |
13 files changed, 3204 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am new file mode 100644 index 0000000..2c7621d --- /dev/null +++ b/src/util/Makefile.am @@ -0,0 +1,90 @@ +pkglibexecdir = $(libexecdir)/dovecot + +pkglibexec_PROGRAMS = \ + rawlog \ + script \ + script-login \ + $(TCPWRAP_BIN) \ + gdbhelper \ + maildirlock + +noinst_PROGRAMS = \ + test-fs + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-auth \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-fs \ + -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/auth \ + -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ + -DPKG_RUNDIR=\""$(rundir)"\" \ + $(BINARY_CFLAGS) + +test_fs_SOURCES = test-fs.c +test_fs_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) +test_fs_DEPENDENCIES = $(LIBDOVECOT_DEPS) + +rawlog_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +rawlog_DEPENDENCIES = $(LIBDOVECOT_DEPS) +rawlog_SOURCES = \ + rawlog.c + +script_login_LDADD = \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +script_login_DEPENDENCIES = \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) +script_login_SOURCES = \ + script-login.c + +script_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) +script_DEPENDENCIES = $(LIBDOVECOT_DEPS) +script_SOURCES = \ + script.c \ + health-check-settings.c + +if TCPWRAPPERS +TCPWRAP_BIN = tcpwrap +tcpwrap_LDADD = $(LIBDOVECOT) $(LIBWRAP_LIBS) \ + $(BINARY_LDFLAGS) + +tcpwrap_DEPENDENCIES = $(LIBDOVECOT_DEPS) +tcpwrap_SOURCES = \ + tcpwrap.c \ + tcpwrap-settings.c +endif + +gdbhelper_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +gdbhelper_DEPENDENCIES = $(LIBDOVECOT_DEPS) +gdbhelper_SOURCES = \ + gdbhelper.c + +maildirlock_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +maildirlock_DEPENDENCIES = $(LIBDOVECOT_DEPS) +maildirlock_SOURCES = \ + maildirlock.c + +pkglibexec_SCRIPTS = health-check.sh + +bin_SCRIPTS = dovecot-sysreport + +EXTRA_DIST = $(pkglibexec_SCRIPTS) \ + $(bin_SCRIPTS)
\ No newline at end of file diff --git a/src/util/Makefile.in b/src/util/Makefile.in new file mode 100644 index 0000000..79e9bcb --- /dev/null +++ b/src/util/Makefile.in @@ -0,0 +1,1049 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +pkglibexec_PROGRAMS = rawlog$(EXEEXT) script$(EXEEXT) \ + script-login$(EXEEXT) $(am__EXEEXT_1) gdbhelper$(EXEEXT) \ + maildirlock$(EXEEXT) +noinst_PROGRAMS = test-fs$(EXEEXT) +subdir = src/util +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \ + $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \ + $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \ + $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \ + $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \ + $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \ + $(top_srcdir)/m4/flexible_array_member.m4 \ + $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \ + $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \ + $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \ + $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \ + $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \ + $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \ + $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \ + $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/pr_set_dumpable.m4 \ + $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \ + $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \ + $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \ + $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \ + $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \ + $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \ + $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \ + $(top_srcdir)/m4/typeof_dev_t.m4 \ + $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \ + $(top_srcdir)/m4/want_apparmor.m4 \ + $(top_srcdir)/m4/want_bsdauth.m4 \ + $(top_srcdir)/m4/want_bzlib.m4 \ + $(top_srcdir)/m4/want_cassandra.m4 \ + $(top_srcdir)/m4/want_cdb.m4 \ + $(top_srcdir)/m4/want_checkpassword.m4 \ + $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \ + $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \ + $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \ + $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \ + $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \ + $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \ + $(top_srcdir)/m4/want_prefetch.m4 \ + $(top_srcdir)/m4/want_shadow.m4 \ + $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \ + $(top_srcdir)/m4/want_sqlite.m4 \ + $(top_srcdir)/m4/want_stemmer.m4 \ + $(top_srcdir)/m4/want_systemd.m4 \ + $(top_srcdir)/m4/want_textcat.m4 \ + $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \ + $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@TCPWRAPPERS_TRUE@am__EXEEXT_1 = tcpwrap$(EXEEXT) +am__installdirs = "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(bindir)" \ + "$(DESTDIR)$(pkglibexecdir)" +PROGRAMS = $(noinst_PROGRAMS) $(pkglibexec_PROGRAMS) +am_gdbhelper_OBJECTS = gdbhelper.$(OBJEXT) +gdbhelper_OBJECTS = $(am_gdbhelper_OBJECTS) +am__DEPENDENCIES_1 = +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +am_maildirlock_OBJECTS = maildirlock.$(OBJEXT) +maildirlock_OBJECTS = $(am_maildirlock_OBJECTS) +am_rawlog_OBJECTS = rawlog.$(OBJEXT) +rawlog_OBJECTS = $(am_rawlog_OBJECTS) +am_script_OBJECTS = script.$(OBJEXT) health-check-settings.$(OBJEXT) +script_OBJECTS = $(am_script_OBJECTS) +am_script_login_OBJECTS = script-login.$(OBJEXT) +script_login_OBJECTS = $(am_script_login_OBJECTS) +am__tcpwrap_SOURCES_DIST = tcpwrap.c tcpwrap-settings.c +@TCPWRAPPERS_TRUE@am_tcpwrap_OBJECTS = tcpwrap.$(OBJEXT) \ +@TCPWRAPPERS_TRUE@ tcpwrap-settings.$(OBJEXT) +tcpwrap_OBJECTS = $(am_tcpwrap_OBJECTS) +am_test_fs_OBJECTS = test-fs.$(OBJEXT) +test_fs_OBJECTS = $(am_test_fs_OBJECTS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +SCRIPTS = $(bin_SCRIPTS) $(pkglibexec_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/gdbhelper.Po \ + ./$(DEPDIR)/health-check-settings.Po \ + ./$(DEPDIR)/maildirlock.Po ./$(DEPDIR)/rawlog.Po \ + ./$(DEPDIR)/script-login.Po ./$(DEPDIR)/script.Po \ + ./$(DEPDIR)/tcpwrap-settings.Po ./$(DEPDIR)/tcpwrap.Po \ + ./$(DEPDIR)/test-fs.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(gdbhelper_SOURCES) $(maildirlock_SOURCES) \ + $(rawlog_SOURCES) $(script_SOURCES) $(script_login_SOURCES) \ + $(tcpwrap_SOURCES) $(test_fs_SOURCES) +DIST_SOURCES = $(gdbhelper_SOURCES) $(maildirlock_SOURCES) \ + $(rawlog_SOURCES) $(script_SOURCES) $(script_login_SOURCES) \ + $(am__tcpwrap_SOURCES_DIST) $(test_fs_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +pkglibexecdir = $(libexecdir)/dovecot +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_LIBS = @APPARMOR_LIBS@ +AR = @AR@ +AUTH_CFLAGS = @AUTH_CFLAGS@ +AUTH_LIBS = @AUTH_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +BISON = @BISON@ +CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ +CASSANDRA_LIBS = @CASSANDRA_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CDB_LIBS = @CDB_LIBS@ +CFLAGS = @CFLAGS@ +CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ +CLUCENE_LIBS = @CLUCENE_LIBS@ +COMPRESS_LIBS = @COMPRESS_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPT_LIBS = @CRYPT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DICT_LIBS = @DICT_LIBS@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLEX = @FLEX@ +FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@ +FUZZER_LDFLAGS = @FUZZER_LDFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5CONFIG = @KRB5CONFIG@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_LIBS = @KRB5_LIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@ +LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@ +LIBCAP = @LIBCAP@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ +LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ +LIBICONV = @LIBICONV@ +LIBICU_CFLAGS = @LIBICU_CFLAGS@ +LIBICU_LIBS = @LIBICU_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@ +LIBSODIUM_LIBS = @LIBSODIUM_LIBS@ +LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@ +LIBTIRPC_LIBS = @LIBTIRPC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MODULE_LIBS = @MODULE_LIBS@ +MODULE_SUFFIX = @MODULE_SUFFIX@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOPLUGIN_LDFLAGS = @NOPLUGIN_LDFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PANDOC = @PANDOC@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PGSQL_CFLAGS = @PGSQL_CFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PG_CONFIG = @PG_CONFIG@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +QUOTA_LIBS = @QUOTA_LIBS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RPCGEN = @RPCGEN@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SETTING_FILES = @SETTING_FILES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SQL_CFLAGS = @SQL_CFLAGS@ +SQL_LIBS = @SQL_LIBS@ +SSL_CFLAGS = @SSL_CFLAGS@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dict_drivers = @dict_drivers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +moduledir = @moduledir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rundir = @rundir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sql_drivers = @sql_drivers@ +srcdir = @srcdir@ +ssldir = @ssldir@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +systemdservicetype = @systemdservicetype@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-auth \ + -I$(top_srcdir)/src/lib-dict \ + -I$(top_srcdir)/src/lib-fs \ + -I$(top_srcdir)/src/lib-settings \ + -I$(top_srcdir)/src/lib-master \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/auth \ + -DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \ + -DPKG_RUNDIR=\""$(rundir)"\" \ + $(BINARY_CFLAGS) + +test_fs_SOURCES = test-fs.c +test_fs_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +test_fs_DEPENDENCIES = $(LIBDOVECOT_DEPS) +rawlog_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +rawlog_DEPENDENCIES = $(LIBDOVECOT_DEPS) +rawlog_SOURCES = \ + rawlog.c + +script_login_LDADD = \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +script_login_DEPENDENCIES = \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) + +script_login_SOURCES = \ + script-login.c + +script_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +script_DEPENDENCIES = $(LIBDOVECOT_DEPS) +script_SOURCES = \ + script.c \ + health-check-settings.c + +@TCPWRAPPERS_TRUE@TCPWRAP_BIN = tcpwrap +@TCPWRAPPERS_TRUE@tcpwrap_LDADD = $(LIBDOVECOT) $(LIBWRAP_LIBS) \ +@TCPWRAPPERS_TRUE@ $(BINARY_LDFLAGS) + +@TCPWRAPPERS_TRUE@tcpwrap_DEPENDENCIES = $(LIBDOVECOT_DEPS) +@TCPWRAPPERS_TRUE@tcpwrap_SOURCES = \ +@TCPWRAPPERS_TRUE@ tcpwrap.c \ +@TCPWRAPPERS_TRUE@ tcpwrap-settings.c + +gdbhelper_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +gdbhelper_DEPENDENCIES = $(LIBDOVECOT_DEPS) +gdbhelper_SOURCES = \ + gdbhelper.c + +maildirlock_LDADD = $(LIBDOVECOT) \ + $(BINARY_LDFLAGS) + +maildirlock_DEPENDENCIES = $(LIBDOVECOT_DEPS) +maildirlock_SOURCES = \ + maildirlock.c + +pkglibexec_SCRIPTS = health-check.sh +bin_SCRIPTS = dovecot-sysreport +EXTRA_DIST = $(pkglibexec_SCRIPTS) \ + $(bin_SCRIPTS) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/util/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/util/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list +install-pkglibexecPROGRAMS: $(pkglibexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-pkglibexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(pkglibexec_PROGRAMS)'; test -n "$(pkglibexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(pkglibexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(pkglibexecdir)" && rm -f $$files + +clean-pkglibexecPROGRAMS: + @list='$(pkglibexec_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +gdbhelper$(EXEEXT): $(gdbhelper_OBJECTS) $(gdbhelper_DEPENDENCIES) $(EXTRA_gdbhelper_DEPENDENCIES) + @rm -f gdbhelper$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(gdbhelper_OBJECTS) $(gdbhelper_LDADD) $(LIBS) + +maildirlock$(EXEEXT): $(maildirlock_OBJECTS) $(maildirlock_DEPENDENCIES) $(EXTRA_maildirlock_DEPENDENCIES) + @rm -f maildirlock$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(maildirlock_OBJECTS) $(maildirlock_LDADD) $(LIBS) + +rawlog$(EXEEXT): $(rawlog_OBJECTS) $(rawlog_DEPENDENCIES) $(EXTRA_rawlog_DEPENDENCIES) + @rm -f rawlog$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(rawlog_OBJECTS) $(rawlog_LDADD) $(LIBS) + +script$(EXEEXT): $(script_OBJECTS) $(script_DEPENDENCIES) $(EXTRA_script_DEPENDENCIES) + @rm -f script$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(script_OBJECTS) $(script_LDADD) $(LIBS) + +script-login$(EXEEXT): $(script_login_OBJECTS) $(script_login_DEPENDENCIES) $(EXTRA_script_login_DEPENDENCIES) + @rm -f script-login$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(script_login_OBJECTS) $(script_login_LDADD) $(LIBS) + +tcpwrap$(EXEEXT): $(tcpwrap_OBJECTS) $(tcpwrap_DEPENDENCIES) $(EXTRA_tcpwrap_DEPENDENCIES) + @rm -f tcpwrap$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(tcpwrap_OBJECTS) $(tcpwrap_LDADD) $(LIBS) + +test-fs$(EXEEXT): $(test_fs_OBJECTS) $(test_fs_DEPENDENCIES) $(EXTRA_test_fs_DEPENDENCIES) + @rm -f test-fs$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(test_fs_OBJECTS) $(test_fs_LDADD) $(LIBS) +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) +install-pkglibexecSCRIPTS: $(pkglibexec_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(pkglibexec_SCRIPTS)'; test -n "$(pkglibexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibexecdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(pkglibexecdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(pkglibexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-pkglibexecSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(pkglibexec_SCRIPTS)'; test -n "$(pkglibexecdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(pkglibexecdir)'; $(am__uninstall_files_from_dir) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gdbhelper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/health-check-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/maildirlock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rawlog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script-login.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/script.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpwrap-settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tcpwrap.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-fs.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(SCRIPTS) +installdirs: + for dir in "$(DESTDIR)$(pkglibexecdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkglibexecdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + clean-pkglibexecPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/gdbhelper.Po + -rm -f ./$(DEPDIR)/health-check-settings.Po + -rm -f ./$(DEPDIR)/maildirlock.Po + -rm -f ./$(DEPDIR)/rawlog.Po + -rm -f ./$(DEPDIR)/script-login.Po + -rm -f ./$(DEPDIR)/script.Po + -rm -f ./$(DEPDIR)/tcpwrap-settings.Po + -rm -f ./$(DEPDIR)/tcpwrap.Po + -rm -f ./$(DEPDIR)/test-fs.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binSCRIPTS install-pkglibexecPROGRAMS \ + install-pkglibexecSCRIPTS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f ./$(DEPDIR)/gdbhelper.Po + -rm -f ./$(DEPDIR)/health-check-settings.Po + -rm -f ./$(DEPDIR)/maildirlock.Po + -rm -f ./$(DEPDIR)/rawlog.Po + -rm -f ./$(DEPDIR)/script-login.Po + -rm -f ./$(DEPDIR)/script.Po + -rm -f ./$(DEPDIR)/tcpwrap-settings.Po + -rm -f ./$(DEPDIR)/tcpwrap.Po + -rm -f ./$(DEPDIR)/test-fs.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binSCRIPTS uninstall-pkglibexecPROGRAMS \ + uninstall-pkglibexecSCRIPTS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-noinstPROGRAMS \ + clean-pkglibexecPROGRAMS cscopelist-am ctags ctags-am \ + distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binSCRIPTS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-pkglibexecPROGRAMS \ + install-pkglibexecSCRIPTS install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-binSCRIPTS uninstall-pkglibexecPROGRAMS \ + uninstall-pkglibexecSCRIPTS + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/util/dovecot-sysreport b/src/util/dovecot-sysreport new file mode 100755 index 0000000..a19aef1 --- /dev/null +++ b/src/util/dovecot-sysreport @@ -0,0 +1,212 @@ +#!/usr/bin/env bash + +set -eu + +dest="dovecot-sysreport-$(uname -n)-$(date +'%s').tar.gz" +conf_flag="" +binary="" +core="" +copy_files="" +keep_temp=0 + +PARAMS="" +SRTEMP="`mktemp -d`" + +if test "x$SRTEMP" = x; then + echo "Could not create temp directory" + exit 1 +fi + +while (( "$#" )); do + case "$1" in + -d|--destination) + + if [ "$#" -lt "2" ] ; then + echo "Usage: $0 $1 <destination.tar.gz>" + exit 1 + fi + dest=$2 + shift 2 + ;; + + -c|--config) + + if [ "$#" -lt "2" ] ; then + echo "Usage: $0 $1 <config_file>" + exit 1 + fi + conf_flag="-c $2" + shift 2 + ;; + + -o|--core) + + gdb=`which gdb` + if [ "$gdb" = "" ]; then + echo "gdb not found" + exit 1 + fi + + if [[ "$#" -lt 2 ]] ; then + echo "Usage: $0 $1 [<binary>] <core> [...]" + exit 1 + fi + + while [[ "$#" -ge 2 ]]; do + # see if binary parameter is specified + binary=$2 + if ! [ -r "$binary" ]; then + echo "$binary not readable" + exit 1 + fi + binary_info=$(file "$binary") + if echo "$binary_info" | grep "core file.*execfn: '" >/dev/null; then + # no binary specified - detect it + binary=$(echo "$binary_info" | sed "s;^.*execfn: '\([^\']\+\)'.*$;\1;") + if ! [ -r "$binary" ]; then + echo "Detected binary path '$binary' from core file, but it is not readable" + exit 1 + fi + echo "Core file was detected to be generated by $binary" + else + shift + fi + + core=$2 + shift + if ! [ -s "$core" ]; then + echo "$core not found or it is empty" + exit 1 + fi + + echo "gathering core file dependencies..." + core_files=$((echo "info shared"; sleep 1) | $gdb $binary $core | grep '^0x.*/' | sed 's,^[^/]*,,') + copy_files="$copy_files $binary $core_files" + cp $core $SRTEMP + done + shift + ;; + + -k|--keeptemp) + + keep_temp=1 + shift + ;; + + -h|--help) + + echo -e "dovecot-sysreport \t[-h|--help] [-o|--core [binary] core [...]] [-d|--destination dest] + \t\t\t[-k|--keeptemp] -- utility to gather information from the current + \t\t\tsystem to be reported for dovecot bug fixes." + echo "" + echo -e "where:" + echo "" + echo -e "\t-h, --help\t\tShow the contents of this help." + echo -e "\t-d, --destination\tThe file location which the report archive should be put to. + \t\t\t\tThe default value is dovecot-sysreport-<hostname>-<current_timestamp>.tar.gz" + echo -e "\t-c, --config\t\tSpecify the root configuration file of dovecot." + echo -e "\t-o, --core\t\tInclude an specific core file along with its dependencies." + echo -e "\t-k, --keeptemp\t\tDo not remove temp files at the end." + exit 0 + ;; + + --) + + shift + break + ;; + + -*|--*=) + + echo "Error: Unsupported flag $1" >&2 + exit 1 + ;; + + *) + + PARAMS="$PARAMS $1" + shift + ;; + + esac +done + +eval set -- "$PARAMS" + +mkdir $SRTEMP/conf + +doveconf $conf_flag -n > $SRTEMP/conf/dovecot.conf + +unwrap_and_hide_pass () { + files=`grep -zPo 'dict\s*{[^}]*}' $1 | grep -zPo '.*=.*:\K(.*)' | tr '\0' '\n'` + files="$files `grep -zPo 'args\s*=\s*\K(.*)' $1 | tr '\0' '\n'`" + for cf in $files; do + if [ -r "$cf" ]; then + if [[ ! -z `grep -vhIE '^([^:]*:){6}[^:]*$' $cf` ]]; then + unwrap_and_hide_pass $cf + mkdir -p $SRTEMP/conf"$(dirname "$cf")" + if [[ -x "$(command -v python)" ]]; then + python <<HEREDOC +import re +conf = open('$cf', 'r').read() +hidden = re.sub('(?<!no)((?:password|key|nonce|dnpass)\s*=\s*).*?(?=$|\s)', '\g<1>#hidden', conf) +f = open('$SRTEMP/conf$cf', "w") +f.write(hidden) +f.close() +HEREDOC + elif [[ -x "$(command -v perl)" ]]; then + perl -pe 's/(?<!no)((?:password|key|nonce|dnpass)\s*=\s*).*?(?=$|\s)/\1#hidden/g' \ + $cf > $SRTEMP/conf$cf + else + echo "perl or python is required to hide your passwords in dovecot's" + echo "configuration files. Either install at least one of them or" + echo "continue at your own peril. Do you want to continue (N/y)? " + read permit + if [ "$permit" != "Y" ] && [ "$permit" != "y" ]; then + exit 1 + fi + cat $cf > $SRTEMP/conf$cf + fi + fi + fi + done +} + +echo "Gathering configurations ..." +unwrap_and_hide_pass $SRTEMP/conf/dovecot.conf + +echo "Gathering system informations ..." +doveadm $conf_flag log errors > $SRTEMP/log_errors || : +ps auxwww | grep '[d]ovecot' > $SRTEMP/ps_output +doveadm $conf_flag service status > $SRTEMP/service_status || : +doveadm $conf_flag process status > $SRTEMP/process_status || : +uptime > $SRTEMP/uptime_output +doveadm $conf_flag stats dump > $SRTEMP/stats_dump || : +sleep 1 +echo -e "\n\n###################### AFTER ONE SECOND ######################\n\n" | \ + tee -a $SRTEMP/ps_output $SRTEMP/service_status $SRTEMP/process_status \ + $SRTEMP/uptime_output $SRTEMP/stats_dump > /dev/null +ps auxwww | grep '[d]ovecot' >> $SRTEMP/ps_output +doveadm $conf_flag service status >> $SRTEMP/service_status || : +doveadm $conf_flag process status >> $SRTEMP/process_status || : +uptime >> $SRTEMP/uptime_output +doveadm $conf_flag stats dump >> $SRTEMP/stats_dump || : + +cf=`pwd` +cd $SRTEMP +echo "Creating archive ..." +tar -czf `if [[ "$dest" = /* ]]; then echo $dest; else echo $cf/$dest; fi` --dereference \ + $copy_files * + +function cleanup { + if [ $keep_temp = 0 ]; then + echo "Removing temp files at $SRTEMP ..." + rm -rf $SRTEMP + else + echo "Temp files remains untouched at $SRTEMP ..." + fi +} + +trap cleanup EXIT + +echo "All done! Please report file $dest" diff --git a/src/util/gdbhelper.c b/src/util/gdbhelper.c new file mode 100644 index 0000000..f1ec2af --- /dev/null +++ b/src/util/gdbhelper.c @@ -0,0 +1,71 @@ +/* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <sys/wait.h> + +int main(int argc, char *argv[]) +{ + pid_t pid = fork(); + const char *path, *cmd; + int fd_in[2], fd_out[2], fd_log, status; + + if (argc < 2) + i_fatal("Usage: gdbhelper <program> [<args>]"); + + switch (pid) { + case 1: + i_fatal("fork() failed: %m"); + case 0: + /* child */ + (void)execvp(argv[1], argv+1); + i_fatal("execvp(%s) failed: %m", argv[1]); + default: + if (pipe(fd_in) < 0 || pipe(fd_out) < 0) + i_fatal("pipe() failed: %m"); + cmd = "handle SIGPIPE nostop\n" + "handle SIGALRM nostop\n" + "handle SIG32 nostop\n" + "cont\n" + "bt full\n" + "quit\n"; + if (write(fd_in[1], cmd, strlen(cmd)) < 0) + i_fatal("write() failed: %m"); + + if (dup2(fd_in[0], 0) < 0 || + dup2(fd_out[1], 1) < 0 || + dup2(fd_out[1], 2) < 0) + i_fatal("dup2() failed: %m"); + + cmd = t_strdup_printf("gdb %s %s", argv[1], dec2str(pid)); + if (system(cmd) < 0) + i_fatal("system() failed: %m"); + + if (wait(&status) < 0) + i_fatal("wait() failed: %m"); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + char buf[1024]; + ssize_t ret; + + path = t_strdup_printf("/tmp/gdbhelper.%s.%s", + dec2str(time(NULL)), + dec2str(pid)); + fd_log = open(path, O_CREAT | O_WRONLY, 0600); + if (fd_log < 0) + i_fatal("open(%s) failed: %m", path); + + while ((ret = read(fd_out[0], buf, sizeof(buf))) > 0) { + if (write(fd_log, buf, ret) < 0) + i_fatal("write(%s) failed: %m", path); + } + if (ret < 0) + i_fatal("read(pipe) failed: %m"); + i_close_fd(&fd_log); + } + } + return 0; +} diff --git a/src/util/health-check-settings.c b/src/util/health-check-settings.c new file mode 100644 index 0000000..82a70ac --- /dev/null +++ b/src/util/health-check-settings.c @@ -0,0 +1,31 @@ +/* Copyright (c) 2019 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "settings-parser.h" +#include "service-settings.h" + +struct service_settings health_check_service_settings = { + .name = "health-check", + .protocol = "", + .type = "", + .executable = "script -p health-check.sh", + .user = "$default_internal_user", + .group = "", + .privileged_group = "", + .extra_groups = "", + .chroot = "", + + .drop_priv_before_exec = TRUE, + + .process_min_avail = 0, + .process_limit = 0, + .client_limit = 1, + .service_count = 0, + .idle_kill = 0, + .vsz_limit = UOFF_T_MAX, + + .unix_listeners = ARRAY_INIT, + .fifo_listeners = ARRAY_INIT, + .inet_listeners = ARRAY_INIT +}; diff --git a/src/util/health-check.sh b/src/util/health-check.sh new file mode 100755 index 0000000..4604f4e --- /dev/null +++ b/src/util/health-check.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# +# Copyright (c) 2019 Dovecot authors, see the included COPYING file */ +# +# This script is intended to be called by the script service and to be +# connected to a socket using a service configuration like this: +# executable = script -p /path/to/health-check.sh +# +# This simple example merely answers "PONG\n" if the input is "PING\n". It +# stops waiting for input after $timeout which causes the process to die +# which causes the script module to close the socket. Inputs and outputs +# can be written to STDIN and STDOUT, they are duplicated file-descriptors +# if called from the script service. + +timeout=10 + +# timeout the read via trap for POSIX shell compatibility +trap "exit 0" QUIT +{ + sleep $timeout + kill -3 $$ 2>/dev/null +} & +read -r input + +exit_code=$? +cleaned_input=$(echo ${input} | sed "s/[^a-zA-Z0-9]//g") + +if [ ${exit_code} -eq 0 ] && [ "${cleaned_input}" = "PING" ];then + echo "PONG" +fi +# always exit successful +exit 0 diff --git a/src/util/maildirlock.c b/src/util/maildirlock.c new file mode 100644 index 0000000..89fbacc --- /dev/null +++ b/src/util/maildirlock.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "lib-signals.h" +#include "ioloop.h" +#include "write-full.h" +#include "file-dotlock.h" +#include "index/maildir/maildir-uidlist.h" + +#include <stdio.h> +#include <unistd.h> +#include <signal.h> + +static struct dotlock_settings dotlock_settings = { + .stale_timeout = MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT, + .use_io_notify = TRUE +}; + +static struct ioloop *ioloop; + +static void sig_die(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) +{ + io_loop_stop(ioloop); +} + +static int maildir_lock(const char *path, unsigned int timeout, + struct dotlock **dotlock_r) +{ + dotlock_settings.timeout = timeout; + dotlock_settings.use_excl_lock = getenv("DOTLOCK_USE_EXCL") != NULL; + dotlock_settings.nfs_flush = getenv("MAIL_NFS_STORAGE") != NULL; + + path = t_strconcat(path, "/" MAILDIR_UIDLIST_NAME, NULL); + return file_dotlock_create(&dotlock_settings, path, 0, dotlock_r); +} + +int main(int argc, const char *argv[]) +{ + struct dotlock *dotlock; + unsigned int timeout; + pid_t pid; + int fd[2], ret; + char c; + + if (argc != 3) { + fprintf(stderr, "Usage: maildirlock <path> <timeout>\n" + " - SIGTERM will release the lock.\n"); + return 1; + } + if (pipe(fd) != 0) { + fprintf(stderr, "pipe() failed: %s", strerror(errno)); + return 1; + } + + pid = fork(); + if (pid == (pid_t)-1) { + fprintf(stderr, "fork() failed: %s", strerror(errno)); + return 1; + } + + /* call lib_init() only after fork so that PID gets set correctly */ + lib_init(); + lib_signals_init(); + ioloop = io_loop_create(); + lib_signals_set_handler(SIGINT, LIBSIG_FLAG_DELAYED, sig_die, NULL); + lib_signals_set_handler(SIGTERM, LIBSIG_FLAG_DELAYED, sig_die, NULL); + + if (pid != 0) { + i_close_fd(&fd[1]); + ret = read(fd[0], &c, 1); + if (ret < 0) { + i_error("read(pipe) failed: %m"); + return 1; + } + if (ret != 1) { + /* locking timed out */ + return 1; + } + + printf("%s", dec2str(pid)); + return 0; + } + + /* child process - stdout has to be closed so that caller knows when + to stop reading it. */ + if (dup2(STDERR_FILENO, STDOUT_FILENO) < 0) + i_fatal("dup2() failed: %m"); + + if (str_to_uint(argv[2], &timeout) < 0) + i_fatal("Invalid timeout value: %s", argv[2]); + if (maildir_lock(argv[1], timeout, &dotlock) <= 0) + return 1; + + /* locked - send a byte */ + if (write_full(fd[1], "", 1) < 0) + i_fatal("write(pipe) failed: %m"); + + io_loop_run(ioloop); + + file_dotlock_delete(&dotlock); + lib_signals_deinit(); + + io_loop_destroy(&ioloop); + lib_deinit(); + return 0; +} diff --git a/src/util/rawlog.c b/src/util/rawlog.c new file mode 100644 index 0000000..f4e428b --- /dev/null +++ b/src/util/rawlog.c @@ -0,0 +1,428 @@ +/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" + +#include "ioloop.h" +#include "net.h" +#include "str.h" +#include "write-full.h" +#include "istream.h" +#include "ostream.h" +#include "process-title.h" +#include "restrict-access.h" +#include "time-util.h" +#include "master-service.h" + +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/socket.h> + +#define OUTBUF_THRESHOLD IO_BLOCK_SIZE +#define RAWLOG_TIMEOUT_FLUSH_MSECS 1000 + +static struct ioloop *ioloop; + +enum rawlog_flags { + RAWLOG_FLAG_LOG_INPUT = 0x01, + RAWLOG_FLAG_LOG_OUTPUT = 0x02, + RAWLOG_FLAG_LOG_TIMESTAMPS = 0x04, + RAWLOG_FLAG_LOG_BOUNDARIES = 0x10, + RAWLOG_FLAG_LOG_IP_IN_FILENAME = 0x20 +}; + +struct rawlog_proxy { + int client_in_fd, client_out_fd, server_fd; + struct io *client_io, *server_io; + struct ostream *client_output, *server_output; + struct timeout *to_flush; + + struct ostream *in_output, *out_output; + enum rawlog_flags flags; + bool prev_lf_in, prev_lf_out; +}; + +static void rawlog_proxy_destroy(struct rawlog_proxy *proxy) +{ + if (proxy->in_output != NULL) { + o_stream_uncork(proxy->in_output); + if (o_stream_finish(proxy->in_output) < 0) { + i_error("write(in) failed: %s", + o_stream_get_error(proxy->in_output)); + } + o_stream_destroy(&proxy->in_output); + } + if (proxy->out_output != NULL) { + o_stream_uncork(proxy->out_output); + if (o_stream_finish(proxy->out_output) < 0) { + i_error("write(out) failed: %s", + o_stream_get_error(proxy->out_output)); + } + o_stream_destroy(&proxy->out_output); + } + io_remove(&proxy->client_io); + io_remove(&proxy->server_io); + timeout_remove(&proxy->to_flush); + + o_stream_destroy(&proxy->client_output); + o_stream_destroy(&proxy->server_output); + + if (close(proxy->client_in_fd) < 0) + i_error("close(client_in_fd) failed: %m"); + if (close(proxy->client_out_fd) < 0) + i_error("close(client_out_fd) failed: %m"); + if (close(proxy->server_fd) < 0) + i_error("close(server_fd) failed: %m"); + i_free(proxy); + + io_loop_stop(ioloop); +} + +static void +write_with_timestamps(struct ostream *output, bool *prev_lf, + const unsigned char *data, size_t size) +{ + T_BEGIN { + const char *timestamp = t_strdup_printf("%ld.%06lu ", + (long)ioloop_timeval.tv_sec, + (unsigned long)ioloop_timeval.tv_usec); + string_t *str = t_str_new(size + 128); + size_t i; + + if (*prev_lf) + str_append(str, timestamp); + + for (i = 0; i < size; i++) { + str_append_c(str, data[i]); + if (data[i] == '\n' && i+1 != size) + str_append(str, timestamp); + } + *prev_lf = data[i-1] == '\n'; + o_stream_nsend(output, str_data(str), str_len(str)); + } T_END; +} + +static void proxy_flush_timeout(struct rawlog_proxy *proxy) +{ + bool flushed = TRUE; + + if (o_stream_flush(proxy->in_output) == 0) + flushed = FALSE; + if (o_stream_flush(proxy->out_output) == 0) + flushed = FALSE; + if (flushed) + timeout_remove(&proxy->to_flush); +} + +static void proxy_write_data(struct rawlog_proxy *proxy, struct ostream *output, + bool *prev_lf, const void *data, size_t size) +{ + if (output == NULL || output->closed || size == 0) + return; + + if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) + o_stream_nsend_str(output, "<<<\n"); + + if ((proxy->flags & RAWLOG_FLAG_LOG_TIMESTAMPS) != 0) + write_with_timestamps(output, prev_lf, data, size); + else + o_stream_nsend(output, data, size); + + if ((proxy->flags & RAWLOG_FLAG_LOG_BOUNDARIES) != 0) + o_stream_nsend_str(output, ">>>\n"); + + if (proxy->to_flush == NULL) { + proxy->to_flush = timeout_add(RAWLOG_TIMEOUT_FLUSH_MSECS, + proxy_flush_timeout, proxy); + } +} + +static void proxy_write_in(struct rawlog_proxy *proxy, + const void *data, size_t size) +{ + proxy_write_data(proxy, proxy->in_output, &proxy->prev_lf_in, + data, size); +} + +static void proxy_write_out(struct rawlog_proxy *proxy, + const void *data, size_t size) +{ + proxy_write_data(proxy, proxy->out_output, &proxy->prev_lf_out, + data, size); +} + +static void server_input(struct rawlog_proxy *proxy) +{ + unsigned char buf[OUTBUF_THRESHOLD]; + ssize_t ret; + + if (o_stream_get_buffer_used_size(proxy->client_output) > + OUTBUF_THRESHOLD) { + /* client's output buffer is already quite full. + don't send more until we're below threshold. */ + io_remove(&proxy->server_io); + return; + } + + ret = net_receive(proxy->server_fd, buf, sizeof(buf)); + if (ret > 0) { + o_stream_nsend(proxy->client_output, buf, ret); + proxy_write_out(proxy, buf, ret); + } else if (ret <= 0) + rawlog_proxy_destroy(proxy); +} + +static void client_input(struct rawlog_proxy *proxy) +{ + unsigned char buf[OUTBUF_THRESHOLD]; + ssize_t ret; + + if (o_stream_get_buffer_used_size(proxy->server_output) > + OUTBUF_THRESHOLD) { + /* proxy's output buffer is already quite full. + don't send more until we're below threshold. */ + io_remove(&proxy->client_io); + return; + } + + ret = net_receive(proxy->client_in_fd, buf, sizeof(buf)); + if (ret > 0) { + o_stream_nsend(proxy->server_output, buf, ret); + proxy_write_in(proxy, buf, ret); + } else if (ret < 0) + rawlog_proxy_destroy(proxy); +} + +static int server_output(struct rawlog_proxy *proxy) +{ + if (o_stream_flush(proxy->server_output) < 0) { + rawlog_proxy_destroy(proxy); + return 1; + } + + if (proxy->client_io == NULL && + o_stream_get_buffer_used_size(proxy->server_output) < + OUTBUF_THRESHOLD) { + /* there's again space in proxy's output buffer, so we can + read more from client. */ + proxy->client_io = io_add(proxy->client_in_fd, IO_READ, + client_input, proxy); + } + return 1; +} + +static int client_output(struct rawlog_proxy *proxy) +{ + if (o_stream_flush(proxy->client_output) < 0) { + rawlog_proxy_destroy(proxy); + return 1; + } + + if (proxy->server_io == NULL && + o_stream_get_buffer_used_size(proxy->client_output) < + OUTBUF_THRESHOLD) { + /* there's again space in client's output buffer, so we can + read more from proxy. */ + proxy->server_io = + io_add(proxy->server_fd, IO_READ, server_input, proxy); + } + return 1; +} + +static void proxy_open_logs(struct rawlog_proxy *proxy, const char *path, + const char *ip_addr) +{ + const char *fname, *timestamp; + string_t *path_prefix; + int fd; + + timestamp = t_strflocaltime("%Y%m%d-%H%M%S", time(NULL)); + path_prefix = t_str_new(128); + str_printfa(path_prefix, "%s/", path); + if (ip_addr != NULL && + (proxy->flags & RAWLOG_FLAG_LOG_IP_IN_FILENAME) != 0) + str_printfa(path_prefix, "%s-", ip_addr); + str_printfa(path_prefix, "%s-%s", timestamp, dec2str(getpid())); + + if ((proxy->flags & RAWLOG_FLAG_LOG_INPUT) != 0) { + fname = t_strdup_printf("%s.in", str_c(path_prefix)); + fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (fd == -1) { + i_error("rawlog_open: creat(%s): %m", fname); + return; + } + proxy->in_output = o_stream_create_fd_file_autoclose(&fd, 0); + o_stream_cork(proxy->in_output); + } + + if ((proxy->flags & RAWLOG_FLAG_LOG_OUTPUT) != 0) { + fname = t_strdup_printf("%s.out", str_c(path_prefix)); + fd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600); + if (fd == -1) { + i_error("rawlog_open: creat(%s): %m", fname); + o_stream_destroy(&proxy->in_output); + return; + } + proxy->out_output = o_stream_create_fd_file_autoclose(&fd, 0); + o_stream_cork(proxy->out_output); + } +} + +static struct rawlog_proxy * +rawlog_proxy_create(int client_in_fd, int client_out_fd, int server_fd, + const char *path, const char *ip_addr, + enum rawlog_flags flags) +{ + struct rawlog_proxy *proxy; + + proxy = i_new(struct rawlog_proxy, 1); + proxy->server_fd = server_fd; + proxy->server_output = o_stream_create_fd(server_fd, SIZE_MAX); + o_stream_set_no_error_handling(proxy->server_output, TRUE); + o_stream_set_flush_callback(proxy->server_output, server_output, proxy); + proxy->server_io = io_add(server_fd, IO_READ, server_input, proxy); + + proxy->client_in_fd = client_in_fd; + proxy->client_out_fd = client_out_fd; + proxy->client_output = + o_stream_create_fd(client_out_fd, SIZE_MAX); + o_stream_set_no_error_handling(proxy->client_output, TRUE); + proxy->client_io = io_add(proxy->client_in_fd, IO_READ, + client_input, proxy); + o_stream_set_flush_callback(proxy->client_output, client_output, proxy); + + fd_set_nonblock(client_in_fd, TRUE); + fd_set_nonblock(client_out_fd, TRUE); + + proxy->flags = flags; + + proxy->prev_lf_in = proxy->prev_lf_out = TRUE; + proxy_open_logs(proxy, path, ip_addr); + return proxy; +} + +static void rawlog_open(enum rawlog_flags flags) +{ + const char *chroot_dir, *home, *path; + struct stat st; + int sfd[2]; + pid_t pid; + + chroot_dir = getenv("RESTRICT_CHROOT"); + home = getenv("HOME"); + if (chroot_dir != NULL) + home = t_strconcat(chroot_dir, home, NULL); + else if (home == NULL) + home = "."; + + /* see if we want rawlog */ + path = t_strconcat(home, "/dovecot.rawlog", NULL); + if (lstat(path, &st) < 0) { + if (errno != ENOENT) + i_warning("lstat() failed for %s: %m", path); + else if (getenv("DEBUG") != NULL) + i_info("rawlog: %s doesn't exist", path); + return; + } + if (!S_ISDIR(st.st_mode)) { + if (getenv("DEBUG") != NULL) + i_info("rawlog: %s is not a directory", path); + return; + } + + if (chroot_dir != NULL) { + /* we'll chroot soon. skip over the chroot in the path. */ + path += strlen(chroot_dir); + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sfd) < 0) + i_fatal("socketpair() failed: %m"); + fd_set_nonblock(sfd[0], TRUE); + fd_set_nonblock(sfd[1], TRUE); + + pid = fork(); + if (pid < 0) + i_fatal("fork() failed: %m"); + + if (pid > 0) { + /* parent */ + if (dup2(sfd[1], 0) < 0) + i_fatal("dup2(sfd, 0)"); + if (dup2(sfd[1], 1) < 0) + i_fatal("dup2(sfd, 1)"); + i_close_fd(&sfd[0]); + i_close_fd(&sfd[1]); + return; + } + i_close_fd(&sfd[1]); + + restrict_access_by_env(0, getenv("HOME")); + + process_title_set(t_strdup_printf("[%s:%s rawlog]", getenv("USER"), + dec2str(getppid()))); + + ioloop = io_loop_create(); + (void)rawlog_proxy_create(0, 1, sfd[0], path, getenv("IP"), flags); + io_loop_run(ioloop); + io_loop_destroy(&ioloop); + + lib_deinit(); + lib_exit(0); +} + +int main(int argc, char *argv[]) +{ + const enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_DONT_SEND_STATS; + char *executable, *p; + enum rawlog_flags flags = + RAWLOG_FLAG_LOG_INPUT | RAWLOG_FLAG_LOG_OUTPUT; + int c; + + master_service = master_service_init("rawlog", service_flags, + &argc, &argv, "+f:bIt"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'f': + if (strcmp(optarg, "in") == 0) + flags &= ENUM_NEGATE(RAWLOG_FLAG_LOG_OUTPUT); + else if (strcmp(optarg, "out") == 0) + flags &= ENUM_NEGATE(RAWLOG_FLAG_LOG_INPUT); + else + i_fatal("Invalid filter: %s", optarg); + break; + case 'b': + flags |= RAWLOG_FLAG_LOG_BOUNDARIES; + break; + case 'I': + flags |= RAWLOG_FLAG_LOG_IP_IN_FILENAME; + break; + case 't': + flags |= RAWLOG_FLAG_LOG_TIMESTAMPS; + break; + default: + return FATAL_DEFAULT; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + i_fatal("Usage: rawlog [-f in|out] [-I] [-b] [-t] <binary> <arguments>"); + + master_service_init_log(master_service); + master_service_init_finish(master_service); + + executable = argv[0]; + rawlog_open(flags); + + /* hide the executable path, it's ugly */ + p = strrchr(argv[0], '/'); + if (p != NULL) argv[0] = p+1; + execv(executable, argv); + + i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable); + + /* not reached */ + return FATAL_EXEC; +} diff --git a/src/util/script-login.c b/src/util/script-login.c new file mode 100644 index 0000000..062a1a6 --- /dev/null +++ b/src/util/script-login.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "env-util.h" +#include "execv-const.h" +#include "fdpass.h" +#include "restrict-access.h" +#include "str.h" +#include "strescape.h" +#include "settings-parser.h" +#include "mail-storage-service.h" +#include "master-interface.h" +#include "master-service.h" +#include "master-service-settings.h" + +#include <unistd.h> + +#define SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR 1 +#define SCRIPT_LOGIN_READ_TIMEOUT_SECS 10 +#define ENV_USERDB_KEYS "USERDB_KEYS" +#define SCRIPT_COMM_FD 3 + +static const char **exec_args; +static bool drop_to_userdb_privileges = FALSE; + +static void client_connected(struct master_service_connection *conn) +{ + enum mail_storage_service_flags flags = + MAIL_STORAGE_SERVICE_FLAG_ALLOW_ROOT | + MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS; + string_t *instr, *keys; + const char *const *args, *key, *value, *error, *version_line, *data_line; + struct mail_storage_service_ctx *service_ctx; + struct mail_storage_service_input input; + struct mail_storage_service_user *user; + char buf[1024]; + unsigned int i, socket_count; + int fd = -1; + ssize_t ret; + + alarm(SCRIPT_LOGIN_READ_TIMEOUT_SECS); + + net_set_nonblock(conn->fd, FALSE); + instr = t_str_new(1024); + ret = fd_read(conn->fd, buf, sizeof(buf), &fd); + while (ret > 0) { + str_append_data(instr, buf, ret); + if (buf[ret-1] == '\n' && + strchr(str_c(instr), '\n')[1] != '\0') { + str_truncate(instr, str_len(instr)-1); + break; + } + + ret = read(conn->fd, buf, sizeof(buf)); + } + + version_line = str_c(instr); + data_line = strchr(version_line, '\n'); + if (data_line != NULL) + version_line = t_strdup_until(version_line, data_line++); + else + version_line = NULL; + + if (ret > 0 || version_line != NULL) { + if (version_line == NULL || + !version_string_verify(version_line, "script-login", + SCRIPT_LOGIN_PROTOCOL_VERSION_MAJOR)) { + i_fatal("Client not compatible with this binary " + "(connecting to wrong socket?)"); + } + } + + if (ret <= 0) { + if (ret < 0) + i_fatal("read() failed: %m"); + else + i_fatal("read() failed: disconnected"); + } + if (fd == -1) + i_fatal("client fd not received"); + + alarm(0); + + /* put everything to environment */ + env_clean(); + keys = t_str_new(256); + args = t_strsplit_tabescaped(data_line); + + if (str_array_length(args) < 3) + i_fatal("Missing input fields"); + + i = 0; + i_zero(&input); + input.module = "mail"; /* need to get mail_uid, mail_gid */ + input.service = "script-login"; + (void)net_addr2ip(args[i++], &input.local_ip); + (void)net_addr2ip(args[i++], &input.remote_ip); + input.username = args[i++]; + input.userdb_fields = args + i; + + env_put("LOCAL_IP", net_ip2addr(&input.local_ip)); + env_put("IP", net_ip2addr(&input.remote_ip)); + env_put("USER", input.username); + + for (; args[i] != NULL; i++) { + value = strchr(args[i], '='); + if (value != NULL) { + key = t_str_ucase(t_strdup_until(args[i], value++)); + env_put(key, value); + str_printfa(keys, "%s ", key); + } + } + env_put(ENV_USERDB_KEYS, str_c(keys)); + + master_service_init_log_with_prefix(master_service, + t_strdup_printf("script-login(%s): ", input.username)); + + if (drop_to_userdb_privileges) { + service_ctx = mail_storage_service_init(master_service, NULL, flags); + if (mail_storage_service_lookup(service_ctx, &input, &user, &error) <= 0) + i_fatal("%s", error); + mail_storage_service_restrict_setenv(service_ctx, user); + /* we can't exec anything in a chroot */ + env_remove("RESTRICT_CHROOT"); + restrict_access_by_env(0, getenv("HOME")); + } + + if (dup2(fd, STDIN_FILENO) < 0) + i_fatal("dup2() failed: %m"); + if (dup2(fd, STDOUT_FILENO) < 0) + i_fatal("dup2() failed: %m"); + if (close(fd) < 0) + i_fatal("close() failed: %m"); + if (conn->fd != SCRIPT_COMM_FD) { + if (dup2(conn->fd, SCRIPT_COMM_FD) < 0) + i_fatal("dup2() failed: %m"); + if (close(conn->fd) < 0) + i_fatal("close() failed: %m"); + } + + /* close all listener sockets */ + socket_count = master_service_get_socket_count(master_service); + for (i = 0; i < socket_count; i++) { + if (close(MASTER_LISTEN_FD_FIRST + i) < 0) + i_error("close(listener) failed: %m"); + } + if (close(MASTER_STATUS_FD) < 0) + i_error("close(status) failed: %m"); + + execvp_const(exec_args[0], exec_args); +} + +static void script_execute_finish(void) +{ + const char *keys_str, *username, *const *keys, *value; + string_t *reply = t_str_new(512); + ssize_t ret; + + keys_str = getenv(ENV_USERDB_KEYS); + if (keys_str == NULL) + i_fatal(ENV_USERDB_KEYS" environment missing"); + + username = getenv("USER"); + if (username == NULL) + i_fatal("USER environment missing"); + str_append(reply, username); + + for (keys = t_strsplit_spaces(keys_str, " "); *keys != NULL; keys++) { + value = getenv(t_str_ucase(*keys)); + if (value != NULL) { + str_append_c(reply, '\t'); + str_append_tabescaped(reply, + t_strconcat(t_str_lcase(*keys), "=", + value, NULL)); + } + } + str_append_c(reply, '\n'); + + /* finish by sending the fd to the mail process */ + ret = fd_send(SCRIPT_COMM_FD, STDOUT_FILENO, + str_data(reply), str_len(reply)); + if (ret == (ssize_t)str_len(reply)) { + /* success */ + } else { + if (ret < 0) + i_error("fd_send() failed: %m"); + else + i_error("fd_send() sent partial output"); + /* exit with 0 even though we failed. non-0 exit just makes + master log an unnecessary error. */ + } +} + +int main(int argc, char *argv[]) +{ + enum master_service_flags flags = + MASTER_SERVICE_FLAG_DONT_SEND_STATS; + int i, c; + + if (getenv(MASTER_IS_PARENT_ENV) == NULL) + flags |= MASTER_SERVICE_FLAG_STANDALONE; + + master_service = master_service_init("script-login", flags, + &argc, &argv, "+d"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'd': + drop_to_userdb_privileges = TRUE; + break; + default: + return FATAL_DEFAULT; + } + } + argc -= optind; + argv += optind; + + master_service_init_log(master_service); + + if (!drop_to_userdb_privileges && + (flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) { + /* drop to privileges defined by service settings */ + restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL); + } + + master_service_init_finish(master_service); + master_service_set_service_count(master_service, 1); + + if ((flags & MASTER_SERVICE_FLAG_STANDALONE) != 0) { + /* The last post-login script is calling us to finish login */ + script_execute_finish(); + } else { + if (argv[0] == NULL) + i_fatal("Missing script path"); + exec_args = i_new(const char *, argc + 2); + for (i = 0; i < argc; i++) + exec_args[i] = argv[i]; + exec_args[i] = PKG_LIBEXECDIR"/script-login"; + exec_args[i+1] = NULL; + + if (exec_args[0][0] != '/') { + exec_args[0] = t_strconcat(PKG_LIBEXECDIR"/", + exec_args[0], NULL); + } + + master_service_run(master_service, client_connected); + } + master_service_deinit(&master_service); + return 0; +} diff --git a/src/util/script.c b/src/util/script.c new file mode 100644 index 0000000..6ae4621 --- /dev/null +++ b/src/util/script.c @@ -0,0 +1,321 @@ +/* Copyright (c) 2010-2019 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "strescape.h" +#include "env-util.h" +#include "execv-const.h" +#include "write-full.h" +#include "restrict-access.h" +#include "master-interface.h" +#include "master-service.h" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> + +#define SCRIPT_MAJOR_VERSION 4 +#define SCRIPT_READ_TIMEOUT_SECS 10 + +static ARRAY_TYPE(const_string) exec_args; +static const char **accepted_envs; +static bool passthrough = FALSE; + +static void script_verify_version(const char *line) +{ + if (line == NULL || + !version_string_verify(line, "script", SCRIPT_MAJOR_VERSION)) { + i_fatal("Client not compatible with this binary " + "(connecting to wrong socket?)"); + } +} + + +static void +exec_child(struct master_service_connection *conn, + const char *const *args, const char *const *envs) +{ + unsigned int i, socket_count; + + if (dup2(conn->fd, STDIN_FILENO) < 0) + i_fatal("dup2() failed: %m"); + if (dup2(conn->fd, STDOUT_FILENO) < 0) + i_fatal("dup2() failed: %m"); + + /* close all fds */ + socket_count = master_service_get_socket_count(master_service); + for (i = 0; i < socket_count; i++) { + if (close(MASTER_LISTEN_FD_FIRST + i) < 0) + i_error("close(listener) failed: %m"); + } + if (close(MASTER_STATUS_FD) < 0) + i_error("close(status) failed: %m"); + if (close(conn->fd) < 0) + i_error("close(conn->fd) failed: %m"); + + for (; args != NULL && *args != NULL; args++) { + const char *arg = t_str_tabunescape(*args); + array_push_back(&exec_args, &arg); + } + array_append_zero(&exec_args); + + env_clean(); + if (envs != NULL) + env_put_array(envs); + + args = array_front(&exec_args); + execvp_const(args[0], args); +} + +static bool +parse_input(ARRAY_TYPE(const_string)* envs, const char *const **args_r, + struct master_service_connection* conn, ssize_t *output_r) +{ + string_t *input; + void *buf; + size_t prev_size, scanpos; + bool header_complete = FALSE, noreply = FALSE; + + input = t_buffer_create(IO_BLOCK_SIZE); + + /* Input contains: + + VERSION .. <lf> + [alarm=<secs> <lf>] + "noreply" | "-" (or anything really) <lf> + + arg 1 <lf> + arg 2 <lf> + ... + <lf> + DATA + + This is quite a horrible protocol. If alarm is specified, it MUST be + before "noreply". If "noreply" isn't given, something other string + (typically "-") must be given which is eaten away. + */ + alarm(SCRIPT_READ_TIMEOUT_SECS); + scanpos = 1; + while (!header_complete) { + const unsigned char *pos, *end; + + prev_size = input->used; + buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE); + + /* peek in socket input buffer */ + *output_r = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK); + if (*output_r <= 0) { + buffer_set_used_size(input, prev_size); + if (strchr(str_c(input), '\n') != NULL) + script_verify_version(t_strcut(str_c(input), '\n')); + + if (*output_r < 0) + i_fatal("recv(MSG_PEEK) failed: %m"); + + i_fatal("recv(MSG_PEEK) failed: disconnected"); + } + + /* scan for final \n\n */ + pos = CONST_PTR_OFFSET(input->data, scanpos); + end = CONST_PTR_OFFSET(input->data, prev_size + *output_r); + for (; pos < end; pos++) { + if (pos[-1] == '\n' && pos[0] == '\n') { + header_complete = TRUE; + pos++; + break; + } + } + scanpos = pos - (const unsigned char *)input->data; + + /* read data for real (up to and including \n\n) */ + *output_r = recv(conn->fd, buf, scanpos-prev_size, 0); + if (prev_size+(*output_r) != scanpos) { + if (*output_r < 0) + i_fatal("recv() failed: %m"); + if (*output_r == 0) + i_fatal("recv() failed: disconnected"); + i_fatal("recv() failed: size of definitive recv() differs from peek"); + } + buffer_set_used_size(input, scanpos); + } + alarm(0); + + /* drop the last two LFs */ + buffer_set_used_size(input, scanpos-2); + + *args_r = t_strsplit(str_c(input), "\n"); + script_verify_version(**args_r); (*args_r)++; + if (**args_r != NULL) { + const char *p; + + if (str_begins(**args_r, "alarm=")) { + unsigned int seconds; + if (str_to_uint((**args_r) + 6, &seconds) < 0) + i_fatal("invalid alarm option"); + alarm(seconds); + (*args_r)++; + } + while (str_begins(**args_r, "env_")) { + const char *envname, *env; + + env = t_str_tabunescape((**args_r)+4); + p = strchr(env, '='); + if (p == NULL) + i_fatal("invalid environment variable"); + envname = t_strdup_until((**args_r)+4, p); + + if (str_array_find(accepted_envs, envname)) + array_push_back(envs, &env); + (*args_r)++; + } + if (strcmp(**args_r, "noreply") == 0) { + noreply = TRUE; + } + if (***args_r == '\0') + i_fatal("empty options"); + (*args_r)++; + } + array_append_zero(envs); + + return noreply; +} + +static bool client_exec_script(struct master_service_connection *conn) +{ + ARRAY_TYPE(const_string) envs; + const char *const *args = NULL; + ssize_t ret; + int status; + pid_t pid; + + t_array_init(&envs, 16); + + net_set_nonblock(conn->fd, FALSE); + + if (!passthrough && parse_input(&envs, &args, conn, &ret)) { + /* parse_input returns TRUE if noreply is set in the input. + * In that case there is no need to fork and check exit + * status. Parsing the input must only happen if passthrough + * is not enabled. */ + exec_child(conn, args, array_front(&envs)); + i_unreached(); + } + + if ((pid = fork()) == (pid_t)-1) { + i_error("fork() failed: %m"); + return FALSE; + } + + if (pid == 0) { + /* child */ + if (!passthrough) + exec_child(conn, args, array_front(&envs)); + else + exec_child(conn, NULL, NULL); + i_unreached(); + } + + /* parent */ + + /* check script exit status */ + if (waitpid(pid, &status, 0) < 0) { + i_error("waitpid() failed: %m"); + return FALSE; + } else if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + if (ret != 0) { + i_error("Script terminated abnormally, exit status %d", (int)ret); + return FALSE; + } + } else if (WIFSIGNALED(status)) { + i_error("Script terminated abnormally, signal %d", WTERMSIG(status)); + return FALSE; + } else if (WIFSTOPPED(status)) { + i_fatal("Script stopped, signal %d", WSTOPSIG(status)); + return FALSE; + } else { + i_fatal("Script terminated abnormally, return status %d", status); + return FALSE; + } + return TRUE; +} + +static void client_connected(struct master_service_connection *conn) +{ + if (!passthrough) { + char response[2]; + + response[0] = client_exec_script(conn) ? '+' : '-'; + response[1] = '\n'; + if (write_full(conn->fd, &response, 2) < 0) + i_error("write(response) failed: %m"); + } else { + client_exec_script(conn); + } +} + +int main(int argc, char *argv[]) +{ + const enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_DONT_SEND_STATS; + ARRAY_TYPE(const_string) aenvs; + const char *binary; + const char *const *envs; + int c, i; + + master_service = master_service_init("script", service_flags, + &argc, &argv, "+e:p"); + + t_array_init(&aenvs, 16); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'e': + envs = t_strsplit_spaces(optarg,", \t"); + while (*envs != NULL) { + array_push_back(&aenvs, envs); + envs++; + } + break; + case 'p': + passthrough = TRUE; + break; + default: + return FATAL_DEFAULT; + } + } + argc -= optind; + argv += optind; + + array_append_zero(&aenvs); + accepted_envs = p_strarray_dup(default_pool, array_front(&aenvs)); + + master_service_init_log(master_service); + if (argv[0] == NULL) + i_fatal("Missing script path"); + restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL); + restrict_access_allow_coredumps(TRUE); + + master_service_init_finish(master_service); + master_service_set_service_count(master_service, 1); + + if (argv[0][0] == '/') + binary = argv[0]; + else + binary = t_strconcat(PKG_LIBEXECDIR"/", argv[0], NULL); + + i_array_init(&exec_args, argc + 16); + array_push_back(&exec_args, &binary); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + array_push_back(&exec_args, &arg); + } + + master_service_run(master_service, client_connected); + array_free(&exec_args); + i_free(accepted_envs); + master_service_deinit(&master_service); + return 0; +} diff --git a/src/util/tcpwrap-settings.c b/src/util/tcpwrap-settings.c new file mode 100644 index 0000000..ec7ceaf --- /dev/null +++ b/src/util/tcpwrap-settings.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "settings-parser.h" +#include "service-settings.h" + +#include <stddef.h> + +#ifdef HAVE_LIBWRAP +struct service_settings tcpwrap_service_settings = { + .name = "tcpwrap", + .protocol = "", + .type = "", + .executable = "tcpwrap", + .user = "$default_internal_user", + .group = "", + .privileged_group = "", + .extra_groups = "", + .chroot = "", + + .drop_priv_before_exec = FALSE, + + .process_min_avail = 0, + .process_limit = 0, + .client_limit = 1, + .service_count = 0, + .idle_kill = 0, + .vsz_limit = UOFF_T_MAX, + + .unix_listeners = ARRAY_INIT, + .fifo_listeners = ARRAY_INIT, + .inet_listeners = ARRAY_INIT +}; +#endif diff --git a/src/util/tcpwrap.c b/src/util/tcpwrap.c new file mode 100644 index 0000000..0d492bb --- /dev/null +++ b/src/util/tcpwrap.c @@ -0,0 +1,131 @@ +/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "fdpass.h" +#include "write-full.h" +#include "restrict-access.h" +#include "master-service.h" + +#include <unistd.h> +#include <syslog.h> +#include <tcpd.h> + +struct tcpwrap_client { + int fd; + struct io *io; + struct timeout *to; +}; + +#define INPUT_TIMEOUT_MSECS (1000*10) + +/* for tcpwrap library */ +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; + +static struct tcpwrap_client *tcpwrap_client = NULL; + +static void tcpwrap_client_destroy(struct tcpwrap_client **client); + +static void tcpwrap_client_handle(struct tcpwrap_client *client, int check_fd, + const char *daemon_name) +{ + struct request_info request; + + request_init(&request, RQ_DAEMON, daemon_name, + RQ_FILE, check_fd, 0); + fromhost(&request); + + if (!hosts_access(&request)) + (void)write_full(client->fd, "0\n", 2); + else + (void)write_full(client->fd, "1\n", 2); + lib_exit(0); +} + +static void tcpwrap_client_input(struct tcpwrap_client *client) +{ + unsigned char buf[1024]; + ssize_t ret; + int check_fd = -1; + + ret = fd_read(client->fd, buf, sizeof(buf), &check_fd); + if (ret <= 0) { + i_error("fd_read() failed: %m"); + } else if (ret > 1 && (size_t)ret < sizeof(buf) && buf[ret-1] == '\n') { + tcpwrap_client_handle(client, check_fd, t_strndup(buf, ret-1)); + } else { + i_error("Invalid input from client"); + } + + i_close_fd(&check_fd); + tcpwrap_client_destroy(&client); +} + +static void tcpwrap_client_timeout(struct tcpwrap_client *client) +{ + tcpwrap_client_destroy(&client); +} + +static struct tcpwrap_client *tcpwrap_client_create(int fd) +{ + struct tcpwrap_client *client; + + client = i_new(struct tcpwrap_client, 1); + client->fd = fd; + client->io = io_add(fd, IO_READ, tcpwrap_client_input, client); + client->to = timeout_add(INPUT_TIMEOUT_MSECS, tcpwrap_client_timeout, + client); + return client; +} + +static void tcpwrap_client_destroy(struct tcpwrap_client **_client) +{ + struct tcpwrap_client *client = *_client; + + *_client = NULL; + + timeout_remove(&client->to); + io_remove(&client->io); + if (close(client->fd) < 0) + i_error("close() failed: %m"); + i_free(client); + + tcpwrap_client = NULL; + master_service_client_connection_destroyed(master_service); +} + +static void client_connected(struct master_service_connection *conn) +{ + if (tcpwrap_client != NULL) { + i_error("tcpwrap must be configured with client_limit=1"); + return; + } + + master_service_client_connection_accept(conn); + tcpwrap_client = tcpwrap_client_create(conn->fd); +} + +int main(int argc, char *argv[]) +{ + const enum master_service_flags service_flags = + MASTER_SERVICE_FLAG_DONT_SEND_STATS; + + master_service = master_service_init("tcpwrap", service_flags, + &argc, &argv, ""); + if (master_getopt(master_service) > 0) + return FATAL_DEFAULT; + + master_service_init_log(master_service); + restrict_access_by_env(RESTRICT_ACCESS_FLAG_ALLOW_ROOT, NULL); + restrict_access_allow_coredumps(TRUE); + + master_service_init_finish(master_service); + + master_service_run(master_service, client_connected); + if (tcpwrap_client != NULL) + tcpwrap_client_destroy(&tcpwrap_client); + + master_service_deinit(&master_service); + return 0; +} diff --git a/src/util/test-fs.c b/src/util/test-fs.c new file mode 100644 index 0000000..ecad781 --- /dev/null +++ b/src/util/test-fs.c @@ -0,0 +1,449 @@ +/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ioloop.h" +#include "istream.h" +#include "ostream.h" +#include "guid.h" +#include "llist.h" +#include "master-service.h" +#include "dict.h" +#include "fs-api.h" + +#define DEFAULT_MAX_PARALLEL_OPS 30 +#define DEFAULT_FILES_COUNT 100 +/* Allow +-10% difference in number of files */ +#define FILES_COUNT_APPROX 0.9 + +struct test_read { + struct test_read *prev, *next; + + struct test_ctx *ctx; + struct fs_file *file; + struct istream *input; + struct io *io; +}; + +struct test_write { + struct test_write *prev, *next; + + struct test_ctx *ctx; + struct fs_file *file; +}; + +struct test_delete { + struct test_delete *prev, *next; + + struct test_ctx *ctx; + struct fs_file *file; +}; + +struct test_iter { + struct test_iter *prev, *next; + + struct test_ctx *ctx; + pool_t pool; + ARRAY_TYPE(const_string) files; + struct fs_iter *iter; + bool object_ids; +}; + +struct test_ctx { + struct fs *fs; + const char *prefix; + bool sync_only, async_only; + unsigned int files_count; + unsigned int max_parallel_ops; + + pool_t files_pool; + ARRAY_TYPE(const_string) files; + + struct timeout *to; + struct test_read *reads; + struct test_write *writes; + struct test_delete *deletes; + struct test_iter *iters; + unsigned int running_op_count; + + unsigned int total_reads, total_writes, total_deletes, total_iters; +}; + +static struct ioloop *root_ioloop; + +static void test_more(struct test_ctx *ctx); +static void test_read_callback(struct test_read *r); + +static bool test_want_async(struct test_ctx *ctx) +{ + if (ctx->async_only) + return TRUE; + if (ctx->sync_only) + return FALSE; + return (i_rand_limit(2) == 0); +} + +static void test_op_finish(struct test_ctx *ctx) +{ + i_assert(ctx->running_op_count > 0); + ctx->running_op_count--; + + if (ctx->to == NULL) + ctx->to = timeout_add_short_to(root_ioloop, 0, test_more, ctx); +} + +static void test_write_finish(struct test_write *w) +{ + DLLIST_REMOVE(&w->ctx->writes, w); + fs_file_deinit(&w->file); + + w->ctx->total_writes++; + test_op_finish(w->ctx); + i_free(w); +} + +static void test_write_callback(struct test_write *w) +{ + int ret; + + ret = fs_write_stream_finish_async(w->file); + if (ret < 0) { + i_error("fs_write_stream() failed: %s", + fs_file_last_error(w->file)); + } + if (ret != 0) + test_write_finish(w); +} + +static void test_next_op_write(struct test_ctx *ctx) +{ + struct test_write *w; + struct istream *input, *input2; + struct ostream *output; + const char *path; + + w = i_new(struct test_write, 1); + w->ctx = ctx; + DLLIST_PREPEND(&ctx->writes, w); + + path = t_strconcat(ctx->prefix, guid_generate(), NULL); + w->file = fs_file_init(ctx->fs, path, FS_OPEN_MODE_REPLACE | + (test_want_async(ctx) ? FS_OPEN_FLAG_ASYNC : 0)); + input = i_stream_create_file("/dev/urandom", IO_BLOCK_SIZE); + input2 = i_stream_create_limit(input, i_rand_limit(1024*1024)); + output = fs_write_stream(w->file); + switch (o_stream_send_istream(output, input2)) { + case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: + break; + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: + case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: + i_unreached(); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: + i_fatal("read(/dev/urandom) failed: %s", + i_stream_get_error(input)); + case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: + i_fatal("write() failed: %s", + o_stream_get_error(output)); + } + i_stream_unref(&input); + i_stream_unref(&input2); + + int ret = fs_write_stream_finish(w->file, &output); + if (ret < 0) { + i_error("fs_write_stream(%s) failed: %s", + path, fs_file_last_error(w->file)); + } + if (ret == 0) + fs_file_set_async_callback(w->file, test_write_callback, w); + else + test_write_finish(w); +} + +static void test_iter_finish(struct test_iter *i) +{ + const char *error; + + DLLIST_REMOVE(&i->ctx->iters, i); + + if (fs_iter_deinit(&i->iter, &error) < 0) { + i_error("fs_iter_deinit() failed: %s", error); + pool_unref(&i->pool); + } else { + pool_unref(&i->ctx->files_pool); + i->ctx->files_pool = i->pool; + i->ctx->files = i->files; + } + + i->ctx->total_iters++; + test_op_finish(i->ctx); + i_free(i); +} + +static void test_iter_callback(struct test_iter *i) +{ + const char *fname; + + while ((fname = fs_iter_next(i->iter)) != NULL) { + if (i->object_ids) { + /* skip object ID */ + fname = strchr(fname, '/'); + i_assert(fname != NULL); + fname++; + } + fname = p_strdup(i->pool, fname); + array_push_back(&i->files, &fname); + } + + if (!fs_iter_have_more(i->iter)) + test_iter_finish(i); +} + +static void test_next_op_iter(struct test_ctx *ctx) +{ + struct test_iter *i; + + i = i_new(struct test_iter, 1); + i->ctx = ctx; + i->pool = pool_alloconly_create(MEMPOOL_GROWING"test iter", 256); + p_array_init(&i->files, i->pool, 16); + DLLIST_PREPEND(&ctx->iters, i); + + i->object_ids = (fs_get_properties(ctx->fs) & + FS_PROPERTY_OBJECTIDS) == 0 ? + FALSE : (i_rand_limit(2) == 0); + i->iter = fs_iter_init(ctx->fs, ctx->prefix, + (test_want_async(ctx) ? FS_ITER_FLAG_ASYNC : 0) | + (i->object_ids ? FS_ITER_FLAG_OBJECTIDS : 0)); + fs_iter_set_async_callback(i->iter, test_iter_callback, i); + test_iter_callback(i); +} + +static void test_read_finish(struct test_read *r) +{ + DLLIST_REMOVE(&r->ctx->reads, r); + io_remove(&r->io); + i_stream_unref(&r->input); + fs_file_deinit(&r->file); + + r->ctx->total_reads++; + test_op_finish(r->ctx); + i_free(r); +} + +static void test_read_callback(struct test_read *r) +{ + const unsigned char *data; + size_t size; + int ret; + + while ((ret = i_stream_read_more(r->input, &data, &size)) > 0) + i_stream_skip(r->input, size); + if (ret == 0) { + if (r->io == NULL) { + r->io = io_add_istream(r->input, test_read_callback, r); + fs_file_set_async_callback(r->file, test_read_callback, r); + } + return; + } + i_assert(ret == -1); + if (r->input->stream_errno != 0 && + r->input->stream_errno != ENOENT) + i_error("read() failed: %s", i_stream_get_error(r->input)); + test_read_finish(r); +} + +static void test_next_read(struct test_ctx *ctx) +{ + struct test_read *r; + + r = i_new(struct test_read, 1); + r->ctx = ctx; + DLLIST_PREPEND(&ctx->reads, r); + + const char *const *fnamep = + array_idx(&ctx->files, i_rand_limit(array_count(&ctx->files))); + const char *path = t_strconcat(ctx->prefix, *fnamep, NULL); + r->file = fs_file_init(ctx->fs, path, FS_OPEN_MODE_READONLY | + (test_want_async(ctx) ? FS_OPEN_FLAG_ASYNC : 0)); + r->input = fs_read_stream(r->file, IO_BLOCK_SIZE); + test_read_callback(r); +} + +static void test_delete_finish(struct test_delete *d) +{ + DLLIST_REMOVE(&d->ctx->deletes, d); + fs_file_deinit(&d->file); + + d->ctx->total_deletes++; + test_op_finish(d->ctx); + i_free(d); +} + +static void test_delete_callback(struct test_delete *d) +{ + int ret = fs_delete(d->file); + if (ret < 0 && errno != ENOENT) { + if (errno == EAGAIN) + return; + i_error("fs_delete() failed: %s", fs_file_last_error(d->file)); + } + test_delete_finish(d); +} + +static void test_next_delete(struct test_ctx *ctx) +{ + struct test_delete *d; + + d = i_new(struct test_delete, 1); + d->ctx = ctx; + DLLIST_PREPEND(&ctx->deletes, d); + + const char *const *fnamep = + array_idx(&ctx->files, i_rand_limit(array_count(&ctx->files))); + const char *path = t_strconcat(ctx->prefix, *fnamep, NULL); + d->file = fs_file_init(ctx->fs, path, FS_OPEN_MODE_READONLY | + (test_want_async(ctx) ? FS_OPEN_FLAG_ASYNC : 0)); + int ret = fs_delete(d->file); + if (ret < 0 && errno == EAGAIN) { + fs_file_set_async_callback(d->file, test_delete_callback, d); + return; + } + if (ret < 0 && errno != ENOENT) + i_error("fs_delete() failed: %s", fs_file_last_error(d->file)); + test_delete_finish(d); +} + +static void test_next_op(struct test_ctx *ctx) +{ + switch (i_rand_limit(4)) { + case 0: + if (array_is_created(&ctx->files) && + array_count(&ctx->files) * FILES_COUNT_APPROX > ctx->files_count) + break; + ctx->running_op_count++; + test_next_op_write(ctx); + break; + case 1: + ctx->running_op_count++; + test_next_op_iter(ctx); + break; + case 2: + if (!array_is_created(&ctx->files) || + array_count(&ctx->files) == 0) + break; + ctx->running_op_count++; + test_next_read(ctx); + break; + case 3: + if (!array_is_created(&ctx->files) || + array_count(&ctx->files) / FILES_COUNT_APPROX < ctx->files_count) + break; + ctx->running_op_count++; + test_next_delete(ctx); + break; + } +} + +static void test_more(struct test_ctx *ctx) +{ + timeout_remove(&ctx->to); + /* note that all operations may be synchronous */ + for (unsigned int i = ctx->running_op_count; i < ctx->max_parallel_ops; i++) + test_next_op(ctx); + if (ctx->to == NULL) { + ctx->to = timeout_add(ctx->running_op_count == 0 ? 0 : + i_rand_limit(100), test_more, ctx); + } +} + +static void stats_output(struct test_ctx *ctx) +{ + printf("%u iters, %u reads, %u writes, %u deletes\n", + ctx->total_iters, ctx->total_reads, ctx->total_writes, + ctx->total_deletes); +} + +int main(int argc, char *argv[]) +{ + struct fs_settings set; + struct test_ctx ctx; + const char *error; + unsigned int timeout_secs = 0; + struct timeout *to_stop = NULL; + int c; + + i_zero(&ctx); + ctx.max_parallel_ops = DEFAULT_MAX_PARALLEL_OPS; + ctx.files_count = DEFAULT_FILES_COUNT; + + i_zero(&set); + set.base_dir = PKG_RUNDIR; + master_service = master_service_init("test-fs", + MASTER_SERVICE_FLAG_STANDALONE, + &argc, &argv, "Daf:p:st:u:"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'D': + set.debug = TRUE; + break; + case 'a': + ctx.async_only = TRUE; + break; + case 'f': + if (str_to_uint(optarg, &ctx.files_count) < 0) + i_fatal("Invalid -f parameter: %s", optarg); + break; + case 'p': + if (str_to_uint(optarg, &ctx.max_parallel_ops) < 0) + i_fatal("Invalid -p parameter: %s", optarg); + break; + case 's': + ctx.sync_only = TRUE; + break; + case 'u': + set.username = optarg; + break; + case 't': + if (str_to_uint(optarg, &timeout_secs) < 0) + i_fatal("Invalid -t parameter: %s", optarg); + break; + default: + return FATAL_DEFAULT; + } + } + argc -= optind; + argv += optind; + + if (argc != 3) + i_fatal("Usage: [-a|-s] [-D] [-f <files#>] [-p <max ops>] [-t <secs>] [-u <user>] <driver> <args> <prefix>"); + + master_service_init_finish(master_service); + dict_drivers_register_builtin(); + + if (fs_init(argv[0], argv[1], &set, &ctx.fs, &error) < 0) + i_fatal("fs_init() failed: %s", error); + ctx.prefix = argv[2]; + + root_ioloop = current_ioloop; + test_more(&ctx); + if (timeout_secs != 0) + to_stop = timeout_add(timeout_secs*1000, io_loop_stop, current_ioloop); + struct timeout *to_stats = timeout_add(1000, stats_output, &ctx); + io_loop_run(current_ioloop); + timeout_remove(&to_stats); + timeout_remove(&to_stop); + + while (ctx.writes != NULL) + test_write_finish(ctx.writes); + while (ctx.iters != NULL) + test_iter_finish(ctx.iters); + while (ctx.reads != NULL) + test_read_finish(ctx.reads); + while (ctx.deletes != NULL) + test_delete_finish(ctx.deletes); + + stats_output(&ctx); + timeout_remove(&ctx.to); + fs_deinit(&ctx.fs); + master_service_deinit(&master_service); +} |