diff mbox series

[4/4] e2scrub: add service (cron, systemd) support

Message ID 152087129979.4129.17793206888283838985.stgit@magnolia
State Superseded, archived
Headers show
Series e2scrub: online fsck for ext4 | expand

Commit Message

Darrick Wong March 12, 2018, 4:14 p.m. UTC
From: Darrick J. Wong <darrick.wong@oracle.com>

Add the ability to run the e2scrub utilities as a periodically scheduled
system service.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
 MCONFIG.in                     |    5 +
 configure                      |  179 ++++++++++++++++++++++++++++++++++++++++
 configure.ac                   |   74 ++++++++++++++++-
 debian/e2fsprogs.files         |    3 +
 debian/e2fsprogs.postinst      |   19 ++++
 debian/e2fsprogs.postrm        |   35 ++++++++
 scrub/Makefile.in              |   90 ++++++++++++++++++++
 scrub/e2scrub.in               |   38 +++++++-
 scrub/e2scrub@.service.in      |   20 ++++
 scrub/e2scrub_all.cron.in      |    2 
 scrub/e2scrub_all.in           |   38 ++++++++
 scrub/e2scrub_all.service.in   |   10 ++
 scrub/e2scrub_all.timer.in     |   11 ++
 scrub/e2scrub_all_cron.in      |   68 +++++++++++++++
 scrub/e2scrub_fail.in          |   25 ++++++
 scrub/e2scrub_fail@.service.in |   10 ++
 scrub/e2scrub_reap.in          |   48 +++++++++++
 scrub/e2scrub_reap.service.in  |   21 +++++
 util/subst.conf.in             |    3 +
 19 files changed, 684 insertions(+), 15 deletions(-)
 create mode 100644 debian/e2fsprogs.postrm
 create mode 100644 scrub/e2scrub@.service.in
 create mode 100644 scrub/e2scrub_all.cron.in
 create mode 100644 scrub/e2scrub_all.service.in
 create mode 100644 scrub/e2scrub_all.timer.in
 create mode 100644 scrub/e2scrub_all_cron.in
 create mode 100644 scrub/e2scrub_fail.in
 create mode 100644 scrub/e2scrub_fail@.service.in
 create mode 100644 scrub/e2scrub_reap.in
 create mode 100644 scrub/e2scrub_reap.service.in

Comments

Lukas Czerner March 13, 2018, 6:29 a.m. UTC | #1
On Mon, Mar 12, 2018 at 09:14:59AM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
> 
> Add the ability to run the e2scrub utilities as a periodically scheduled
> system service.

comment bellow

> 
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> ---
>  MCONFIG.in                     |    5 +
>  configure                      |  179 ++++++++++++++++++++++++++++++++++++++++
>  configure.ac                   |   74 ++++++++++++++++-
>  debian/e2fsprogs.files         |    3 +
>  debian/e2fsprogs.postinst      |   19 ++++
>  debian/e2fsprogs.postrm        |   35 ++++++++
>  scrub/Makefile.in              |   90 ++++++++++++++++++++
>  scrub/e2scrub.in               |   38 +++++++-
>  scrub/e2scrub@.service.in      |   20 ++++
>  scrub/e2scrub_all.cron.in      |    2 
>  scrub/e2scrub_all.in           |   38 ++++++++
>  scrub/e2scrub_all.service.in   |   10 ++
>  scrub/e2scrub_all.timer.in     |   11 ++
>  scrub/e2scrub_all_cron.in      |   68 +++++++++++++++
>  scrub/e2scrub_fail.in          |   25 ++++++
>  scrub/e2scrub_fail@.service.in |   10 ++
>  scrub/e2scrub_reap.in          |   48 +++++++++++
>  scrub/e2scrub_reap.service.in  |   21 +++++
>  util/subst.conf.in             |    3 +
>  19 files changed, 684 insertions(+), 15 deletions(-)
>  create mode 100644 debian/e2fsprogs.postrm
>  create mode 100644 scrub/e2scrub@.service.in
>  create mode 100644 scrub/e2scrub_all.cron.in
>  create mode 100644 scrub/e2scrub_all.service.in
>  create mode 100644 scrub/e2scrub_all.timer.in
>  create mode 100644 scrub/e2scrub_all_cron.in
>  create mode 100644 scrub/e2scrub_fail.in
>  create mode 100644 scrub/e2scrub_fail@.service.in
>  create mode 100644 scrub/e2scrub_reap.in
>  create mode 100644 scrub/e2scrub_reap.service.in
> 
> 
> diff --git a/MCONFIG.in b/MCONFIG.in
> index adeb5bd..ee83554 100644
> --- a/MCONFIG.in
> +++ b/MCONFIG.in
> @@ -32,9 +32,14 @@ man8dir = $(mandir)/man8
>  infodir = @infodir@
>  datadir = @datadir@
>  pkgconfigdir = $(libdir)/pkgconfig
> +pkglibdir = $(libdir)/e2fsprogs
>  
>  HAVE_UDEV = @have_udev@
>  UDEV_RULES_DIR = @pkg_udev_rules_dir@
> +HAVE_CROND = @have_crond@
> +CROND_DIR = @crond_dir@
> +HAVE_SYSTEMD = @have_systemd@
> +SYSTEMD_SYSTEM_UNIT_DIR = @systemd_system_unit_dir@
>  
>  @SET_MAKE@
>  
> diff --git a/configure b/configure
> index 986e057..f6a0dea 100755
> --- a/configure
> +++ b/configure
> @@ -625,6 +625,12 @@ gl_use_threads_default=
>  ac_func_list=
>  ac_subst_vars='LTLIBOBJS
>  LIBOBJS
> +systemd_system_unit_dir
> +have_systemd
> +systemd_LIBS
> +systemd_CFLAGS
> +crond_dir
> +have_crond
>  pkg_udev_rules_dir
>  have_udev
>  udev_LIBS
> @@ -899,6 +905,8 @@ with_libintl_prefix
>  enable_fuse2fs
>  with_multiarch
>  with_udev_rules_dir
> +with_crond_dir
> +with_systemd_unit_dir
>  '
>        ac_precious_vars='build_alias
>  host_alias
> @@ -913,7 +921,9 @@ PKG_CONFIG
>  PKG_CONFIG_PATH
>  PKG_CONFIG_LIBDIR
>  udev_CFLAGS
> -udev_LIBS'
> +udev_LIBS
> +systemd_CFLAGS
> +systemd_LIBS'
>  
>  
>  # Initialize some variables set by options.
> @@ -1591,6 +1601,9 @@ Optional Packages:
>    --with-multiarch=ARCH specify the multiarch triplet
>    --with-udev-rules-dir[=DIR]
>                            Install udev rules into DIR.
> +  --with-crond-dir[=DIR]  Install system crontabs into DIR.
> +  --with-systemd-unit-dir[=DIR]
> +                          Install systemd system units into DIR.
>  
>  Some influential environment variables:
>    CC          C compiler command
> @@ -1608,6 +1621,10 @@ Some influential environment variables:
>                path overriding pkg-config's built-in search path
>    udev_CFLAGS C compiler flags for udev, overriding pkg-config
>    udev_LIBS   linker flags for udev, overriding pkg-config
> +  systemd_CFLAGS
> +              C compiler flags for systemd, overriding pkg-config
> +  systemd_LIBS
> +              linker flags for systemd, overriding pkg-config
>  
>  Use these variables to override the choices made by `configure' or to help
>  it to find libraries and programs with nonstandard names/locations.
> @@ -13803,6 +13820,7 @@ else
>  fi
>  
>  fi
> +
>  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can link with -static" >&5
>  $as_echo_n "checking whether we can link with -static... " >&6; }
>  if ${ac_cv_e2fsprogs_use_static+:} false; then :
> @@ -14009,6 +14027,165 @@ fi
>  
>  
>  
> +
> +# Check whether --with-crond_dir was given.
> +if test "${with_crond_dir+set}" = set; then :
> +  withval=$with_crond_dir;
> +else
> +  with_crond_dir=yes
> +fi
> +
> +if test "x${with_crond_dir}" != "xno"; then :
> +
> +	if test "x${with_crond_dir}" = "xyes"; then :
> +
> +		if test -d "/etc/cron.d"; then :
> +  with_crond_dir="/etc/cron.d"
> +fi
> +
> +fi
> +	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for system crontab dir" >&5
> +$as_echo_n "checking for system crontab dir... " >&6; }
> +	crond_dir="${with_crond_dir}"
> +	if test -n "${crond_dir}"; then :
> +
> +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${crond_dir}" >&5
> +$as_echo "${crond_dir}" >&6; }
> +		have_crond="yes"
> +
> +else
> +
> +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +		have_crond="no"
> +
> +fi
> +
> +else
> +
> +	have_crond="disabled"
> +
> +fi
> +
> +
> +
> +
> +# Check whether --with-systemd_unit_dir was given.
> +if test "${with_systemd_unit_dir+set}" = set; then :
> +  withval=$with_systemd_unit_dir;
> +else
> +  with_systemd_unit_dir=yes
> +fi
> +
> +if test "x${with_systemd_unit_dir}" != "xno"; then :
> +
> +	if test "x${with_systemd_unit_dir}" = "xyes"; then :
> +
> +
> +pkg_failed=no
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd" >&5
> +$as_echo_n "checking for systemd... " >&6; }
> +
> +if test -n "$systemd_CFLAGS"; then
> +    pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
> + elif test -n "$PKG_CONFIG"; then
> +    if test -n "$PKG_CONFIG" && \
> +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
> +  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; then
> +  pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "systemd" 2>/dev/null`
> +		      test "x$?" != "x0" && pkg_failed=yes
> +else
> +  pkg_failed=yes
> +fi
> + else
> +    pkg_failed=untried
> +fi
> +if test -n "$systemd_LIBS"; then
> +    pkg_cv_systemd_LIBS="$systemd_LIBS"
> + elif test -n "$PKG_CONFIG"; then
> +    if test -n "$PKG_CONFIG" && \
> +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
> +  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
> +  ac_status=$?
> +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> +  test $ac_status = 0; }; then
> +  pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "systemd" 2>/dev/null`
> +		      test "x$?" != "x0" && pkg_failed=yes
> +else
> +  pkg_failed=yes
> +fi
> + else
> +    pkg_failed=untried
> +fi
> +
> +
> +
> +if test $pkg_failed = yes; then
> +   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +
> +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
> +        _pkg_short_errors_supported=yes
> +else
> +        _pkg_short_errors_supported=no
> +fi
> +        if test $_pkg_short_errors_supported = yes; then
> +	        systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "systemd" 2>&1`
> +        else
> +	        systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "systemd" 2>&1`
> +        fi
> +	# Put the nasty error message in config.log where it belongs
> +	echo "$systemd_PKG_ERRORS" >&5
> +
> +
> +			with_systemd_unit_dir=""
> +
> +elif test $pkg_failed = untried; then
> +     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +
> +			with_systemd_unit_dir=""
> +
> +else
> +	systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
> +	systemd_LIBS=$pkg_cv_systemd_LIBS
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> +$as_echo "yes" >&6; }
> +
> +			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
> +
> +fi
> +
> +
> +fi
> +	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd system unit dir" >&5
> +$as_echo_n "checking for systemd system unit dir... " >&6; }
> +	systemd_system_unit_dir="${with_systemd_unit_dir}"
> +	if test -n "${systemd_system_unit_dir}"; then :
> +
> +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemd_system_unit_dir}" >&5
> +$as_echo "${systemd_system_unit_dir}" >&6; }
> +		have_systemd="yes"
> +
> +else
> +
> +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> +$as_echo "no" >&6; }
> +		have_systemd="no"
> +
> +fi
> +
> +else
> +
> +	have_systemd="disabled"
> +
> +fi
> +
> +
> +
>  test -d lib || mkdir lib
>  test -d include || mkdir include
>  test -d include/linux || mkdir include/linux
> diff --git a/configure.ac b/configure.ac
> index 6e549c5..5a2c8be 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1392,7 +1392,8 @@ else
>      libdir=$libdir/$withval
>      root_libdir=$root_libdir/$withval
>  fi
> -)dnl
> +)
> +dnl
>  dnl
>  dnl See if -static works.  This could fail if the linker does not
>  dnl support -static, or if required external libraries are not available
> @@ -1514,6 +1515,77 @@ AC_SUBST(have_udev)
>  AC_SUBST(pkg_udev_rules_dir)
>  
>  dnl
> +dnl Where do cron jobs go?
> +dnl
> +AC_ARG_WITH([crond_dir],
> +  [AS_HELP_STRING([--with-crond-dir@<:@=DIR@:>@],
> +	[Install system crontabs into DIR.])],
> +  [],
> +  [with_crond_dir=yes])
> +AS_IF([test "x${with_crond_dir}" != "xno"],
> +  [
> +	AS_IF([test "x${with_crond_dir}" = "xyes"],
> +	  [
> +		AS_IF([test -d "/etc/cron.d"],
> +		  [with_crond_dir="/etc/cron.d"])
> +	  ])
> +	AC_MSG_CHECKING([for system crontab dir])
> +	crond_dir="${with_crond_dir}"
> +	AS_IF([test -n "${crond_dir}"],
> +	  [
> +		AC_MSG_RESULT(${crond_dir})
> +		have_crond="yes"
> +	  ],
> +	  [
> +		AC_MSG_RESULT(no)
> +		have_crond="no"
> +	  ])
> +  ],
> +  [
> +	have_crond="disabled"
> +  ])
> +AC_SUBST(have_crond)
> +AC_SUBST(crond_dir)
> +
> +dnl
> +dnl Where do systemd services go?
> +dnl
> +AC_ARG_WITH([systemd_unit_dir],
> +  [AS_HELP_STRING([--with-systemd-unit-dir@<:@=DIR@:>@],
> +	[Install systemd system units into DIR.])],
> +  [],
> +  [with_systemd_unit_dir=yes])
> +AS_IF([test "x${with_systemd_unit_dir}" != "xno"],
> +  [
> +	AS_IF([test "x${with_systemd_unit_dir}" = "xyes"],
> +	  [
> +		PKG_CHECK_MODULES([systemd], [systemd],
> +		  [
> +			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
> +		  ], [
> +			with_systemd_unit_dir=""
> +		  ])
> +		m4_pattern_allow([^PKG_(MAJOR|MINOR|BUILD|REVISION)$])
> +	  ])
> +	AC_MSG_CHECKING([for systemd system unit dir])
> +	systemd_system_unit_dir="${with_systemd_unit_dir}"
> +	AS_IF([test -n "${systemd_system_unit_dir}"],
> +	  [
> +		AC_MSG_RESULT(${systemd_system_unit_dir})
> +		have_systemd="yes"
> +	  ],
> +	  [
> +		AC_MSG_RESULT(no)
> +		have_systemd="no"
> +	  ])
> +  ],
> +  [
> +	have_systemd="disabled"
> +  ])
> +AC_SUBST(have_systemd)
> +AC_SUBST(systemd_system_unit_dir)
> +
> +dnl
>  dnl Make our output files, being sure that we create the some miscellaneous 
>  dnl directories
>  dnl
> diff --git a/debian/e2fsprogs.files b/debian/e2fsprogs.files
> index 78720fe..e0e49ce 100644
> --- a/debian/e2fsprogs.files
> +++ b/debian/e2fsprogs.files
> @@ -1,6 +1,9 @@
>  sbin
>  usr/bin
> +usr/lib
>  usr/sbin
>  usr/share/man
>  etc
>  lib/udev/rules.d
> +lib/systemd/system
> +usr/lib
> diff --git a/debian/e2fsprogs.postinst b/debian/e2fsprogs.postinst
> index 00ac363..e7acb0e 100644
> --- a/debian/e2fsprogs.postinst
> +++ b/debian/e2fsprogs.postinst
> @@ -10,4 +10,23 @@ fi
>  
>  #DEBHELPER#
>  
> +# debhelper doesn't know what timers are...
> +update_svc() {
> +	deb-systemd-helper unmask "$1" >/dev/null || true
> +
> +	if deb-systemd-helper --quiet was-enabled "$1"; then
> +		deb-systemd-helper enable "$1" >/dev/null || true
> +	else
> +		deb-systemd-helper update-state "$1" >/dev/null || true
> +	fi
> +}
> +update_svc e2scrub_all.timer
> +update_svc e2scrub_reap.service
> +
> +# Start our new services
> +if [ -d /run/systemd/system ]; then
> +	systemctl --system daemon-reload >/dev/null || true
> +	deb-systemd-invoke start e2scrub_all.timer >/dev/null || true
> +fi
> +
>  exit 0
> diff --git a/debian/e2fsprogs.postrm b/debian/e2fsprogs.postrm
> new file mode 100644
> index 0000000..32cb642
> --- /dev/null
> +++ b/debian/e2fsprogs.postrm
> @@ -0,0 +1,35 @@
> +#!/bin/sh
> +
> +update_svc() {
> +	deb-systemd-helper mask "$1" >/dev/null || true
> +
> +	if deb-systemd-helper --quiet was-enabled "$1"; then
> +		# Enables the unit on first installation, creates new
> +		# symlinks on upgrades if the unit file has changed.
> +		deb-systemd-helper disable "$1" >/dev/null || true
> +	fi
> +}
> +
> +if [ "$1" != "upgrade" ]; then
> +	# Abort on error.
> +	set -e
> +
> +	if [ -x /usr/sbin/update-initramfs -a \
> +		-e /etc/initramfs-tools/initramfs.conf ]; then
> +	    update-initramfs -u
> +	fi
> +
> +	#DEBHELPER#
> +
> +	# debhelper doesn't know what timers are...
> +	update_svc e2scrub_all.timer
> +	update_svc e2scrub_reap.service
> +
> +	# Start our new services
> +	if [ -d /run/systemd/system ]; then
> +		deb-systemd-invoke stop e2scrub_all.timer >/dev/null || true
> +	fi
> +fi
> +
> +exit 0
> +
> diff --git a/scrub/Makefile.in b/scrub/Makefile.in
> index 1744941..cc2e9ce 100644
> --- a/scrub/Makefile.in
> +++ b/scrub/Makefile.in
> @@ -22,7 +22,23 @@ INSTALL_TGT	+= install-udev
>  UNINSTALL_TGT	+= uninstall-udev
>  endif
>  
> -all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES)
> +ifeq ($(HAVE_CROND),yes)
> +CRONTABS	= e2scrub_all.cron
> +LIBPROGS	+= e2scrub_reap e2scrub_all_cron
> +INSTALLDIRS_TGT	+= installdirs-crond installdirs-libprogs
> +INSTALL_TGT	+= install-crond install-libprogs
> +UNINSTALL_TGT	+= uninstall-crond uninstall-libprogs
> +endif
> +
> +ifeq ($(HAVE_SYSTEMD),yes)
> +SERVICE_FILES	= e2scrub@.service e2scrub_all.service e2scrub_all.timer e2scrub_fail@.service e2scrub_reap.service
> +LIBPROGS	+= e2scrub_fail e2scrub_reap
> +INSTALLDIRS_TGT	+= installdirs-systemd installdirs-libprogs
> +INSTALL_TGT	+= install-systemd install-libprogs
> +UNINSTALL_TGT	+= uninstall-systemd uninstall-libprogs
> +endif
> +
> +all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES) $(SERVICE_FILES) $(CRONTABS) $(LIBPROGS)
>  
>  e2scrub: $(DEP_SUBSTITUTE) e2scrub.in
>  	$(E) "	SUBST $@"
> @@ -34,6 +50,21 @@ e2scrub_all: e2scrub_all.in
>  	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all.in $@
>  	$(Q) chmod a+x $@
>  
> +e2scrub_fail: e2scrub_fail.in
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_fail.in $@
> +	$(Q) chmod a+x $@
> +
> +e2scrub_reap: e2scrub_reap.in
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_reap.in $@
> +	$(Q) chmod a+x $@
> +
> +e2scrub_all_cron: e2scrub_all_cron.in
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all_cron.in $@
> +	$(Q) chmod a+x $@
> +
>  %.8: %.8.in $(DEP_SUBSTITUTE)
>  	$(E) "	SUBST $@"
>  	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> @@ -46,10 +77,34 @@ e2scrub_all: e2scrub_all.in
>  	$(E) "	SUBST $@"
>  	$(Q) $(SUBSTITUTE_UPTIME) $< $@
>  
> +%.service: %.service.in $(DEP_SUBSTITUTE)
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> +
> +%.cron: %.cron.in $(DEP_SUBSTITUTE)
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> +
> +%.timer: %.timer.in $(DEP_SUBSTITUTE)
> +	$(E) "	SUBST $@"
> +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> +
>  installdirs-udev:
>  	$(E) "	MKINSTALLDIRS $(UDEV_RULES_DIR)"
>  	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(UDEV_RULES_DIR)
>  
> +installdirs-crond:
> +	$(E) "	MKINSTALLDIRS $(CROND_DIR)"
> +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(CROND_DIR)
> +
> +installdirs-libprogs:
> +	$(E) "	MKINSTALLDIRS $(pkglibdir)"
> +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(pkglibdir)
> +
> +installdirs-systemd:
> +	$(E) "	MKINSTALLDIRS $(SYSTEMD_SYSTEM_UNIT_DIR)"
> +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)
> +
>  installdirs: $(INSTALLDIRS_TGT)
>  	$(E) "	MKINSTALLDIRS $(root_sbindir) $(man8dir) $(root_sysconfdir)"
>  	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(root_sbindir) \
> @@ -61,6 +116,24 @@ install-udev:
>  		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
>  	done
>  
> +install-crond:
> +	$(Q) for i in $(CRONTABS); do \
> +		$(ES) "	INSTALL $(CROND_DIR)/$$i"; \
> +		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(CROND_DIR)/$$i; \
> +	done
> +
> +install-libprogs: $(LIBPROGS)
> +	$(Q) for i in $(LIBPROGS); do \
> +		$(ES) "	INSTALL $(pkglibdir)/$$i"; \
> +		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(pkglibdir)/$$i; \
> +	done
> +
> +install-systemd: $(SERVICE_FILES)
> +	$(Q) for i in $(SERVICE_FILES); do \
> +		$(ES) "	INSTALL_DATA $(SYSTEMD_SYSTEM_UNIT_DIR)/$$i"; \
> +		$(INSTALL_DATA) $$i $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
> +	done
> +
>  install: $(PROGS) $(MANPAGES) $(FMANPAGES) installdirs $(INSTALL_TGT)
>  	$(Q) for i in $(PROGS); do \
>  		$(ES) "	INSTALL $(root_sbindir)/$$i"; \
> @@ -83,6 +156,21 @@ uninstall-udev:
>  		$(RM) -f $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
>  	done
>  
> +uninstall-crond:
> +	for i in $(CRONTABS); do \
> +		$(RM) -f $(DESTDIR)$(CROND_DIR)/$$i; \
> +	done
> +
> +uninstall-libprogs:
> +	for i in $(LIBPROGS); do \
> +		$(RM) -f $(DESTDIR)$(pkglibdir)/$$i; \
> +	done
> +
> +uninstall-systemd:
> +	for i in $(SERVICE_FILES); do \
> +		$(RM) -f $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
> +	done
> +
>  uninstall: $(UNINSTALL_TGT)
>  	for i in $(PROGS); do \
>  		$(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \
> diff --git a/scrub/e2scrub.in b/scrub/e2scrub.in
> index f77ec6f..a19b6bb 100644
> --- a/scrub/e2scrub.in
> +++ b/scrub/e2scrub.in
> @@ -44,12 +44,34 @@ print_version() {
>  	echo "e2scrub @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
>  }
>  
> +exitcode() {
> +	ret="$1"
> +
> +	# If we're being run as a service, the return code must fit the LSB
> +	# init script action error guidelines, which is to say that we
> +	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
> +	# section 22.2) and hope the admin will scan the log for what
> +	# actually happened.
> +
> +	# We have to sleep 2 seconds here because journald uses the pid to
> +	# connect our log messages to the systemd service.  This is critical
> +	# for capturing all the log messages if the scrub fails, because the
> +	# fail service uses the service name to gather log messages for the
> +	# error report.
> +	if [ -n "${SERVICE_MODE}" ]; then
> +		test "${ret}" -ne 0 && ret=1
> +		sleep 2
> +	fi
> +
> +	exit "${ret}"
> +}
> +
>  while getopts "rtV" opt; do
>  	case "${opt}" in
>  	"r") reap=1;;
>  	"t") fstrim=1;;
> -	"V") print_version; exit 0;;
> -	*) print_help; exit 2;;
> +	"V") print_version; exitcode 0;;
> +	*) print_help; exitcode 2;;
>  	esac
>  done
>  shift "$((OPTIND - 1))"
> @@ -57,18 +79,18 @@ shift "$((OPTIND - 1))"
>  dev="$1"
>  if [ -z "${dev}" ]; then
>  	print_help
> -	exit 1
> +	exitcode 1
>  elif [ ! -b "${dev}" ]; then
>  	echo "${dev}: Not a block device?"
>  	print_help
> -	exit 16
> +	exitcode 16
>  fi
>  
>  # Make sure this is an LVM device we can snapshot
>  eval $(lvs --nameprefixes -o name,vgname --noheadings "${dev}")
>  if [ -z "${LVM2_VG_NAME}" ] || [ -z "${LVM2_LV_NAME}" ]; then
>  	echo "${dev}: Not a LVM device."
> -	exit 16
> +	exitcode 16
>  fi
>  start_time="$(date +'%Y%m%d%H%M%S')"
>  snap="${LVM2_LV_NAME}.e2scrub"
> @@ -80,7 +102,7 @@ ext[234])
>  	;;
>  *)
>  	echo "${dev}: Filesystem of type ${fstype} not supported."
> -	exit 16
> +	exitcode 16
>  	;;
>  esac
>  
> @@ -128,7 +150,7 @@ if [ "${reap}" -gt 0 ]; then
>  fi
>  if ! setup; then
>  	echo "Snapshot of ${dev} FAILED, will not check!"
> -	exit 1
> +	exitcode 1
>  fi
>  
>  # Check and react
> @@ -167,4 +189,4 @@ else
>  	fi
>  fi
>  
> -exit "${ret}"
> +exitcode "${ret}"
> diff --git a/scrub/e2scrub@.service.in b/scrub/e2scrub@.service.in
> new file mode 100644
> index 0000000..496f894
> --- /dev/null
> +++ b/scrub/e2scrub@.service.in
> @@ -0,0 +1,20 @@
> +[Unit]
> +Description=Online ext4 Metadata Check for %I
> +OnFailure=e2scrub_fail@%i.service
> +Documentation=man:e2scrub(8)
> +
> +[Service]
> +Type=oneshot
> +WorkingDirectory=/
> +PrivateNetwork=true
> +ProtectSystem=true
> +ProtectHome=read-only
> +PrivateTmp=yes
> +AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
> +NoNewPrivileges=yes
> +User=root
> +IOSchedulingClass=idle
> +CPUSchedulingPolicy=idle
> +Environment=SERVICE_MODE=1
> +ExecStart=@root_sbindir@/e2scrub -t %I
> +SyslogIdentifier=%N
> diff --git a/scrub/e2scrub_all.cron.in b/scrub/e2scrub_all.cron.in
> new file mode 100644
> index 0000000..0cb5f1f
> --- /dev/null
> +++ b/scrub/e2scrub_all.cron.in
> @@ -0,0 +1,2 @@
> +30 3 * * 0 root test -e /run/systemd/system || @pkglibdir@/e2scrub_all_cron
> +10 3 * * * root test -e /run/systemd/system || @pkglibdir@/e2scrub_reap
> diff --git a/scrub/e2scrub_all.in b/scrub/e2scrub_all.in
> index 453981b..fbf2e95 100644
> --- a/scrub/e2scrub_all.in
> +++ b/scrub/e2scrub_all.in
> @@ -30,10 +30,32 @@ print_version() {
>  	echo "e2scrub_all @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
>  }
>  
> +exitcode() {
> +	ret="$1"
> +
> +	# If we're being run as a service, the return code must fit the LSB
> +	# init script action error guidelines, which is to say that we
> +	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
> +	# section 22.2) and hope the admin will scan the log for what
> +	# actually happened.
> +
> +	# We have to sleep 2 seconds here because journald uses the pid to
> +	# connect our log messages to the systemd service.  This is critical
> +	# for capturing all the log messages if the scrub fails, because the
> +	# fail service uses the service name to gather log messages for the
> +	# error report.
> +	if [ -n "${SERVICE_MODE}" ]; then
> +		test "${ret}" -ne 0 && ret=1
> +		sleep 2
> +	fi
> +
> +	exit "${ret}"
> +}
> +
>  while getopts "V" opt; do
>  	case "${opt}" in
> -	"V") print_version; exit 0;;
> -	*) print_help; exit 2;;
> +	"V") print_version; exitcode 0;;
> +	*) print_help; exitcode 2;;
>  	esac
>  done
>  shift "$((OPTIND - 1))"
> @@ -49,7 +71,15 @@ lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role
>  	# Skip non-ext[234]
>  	blkid -p -n "${types}" "${dev}" > /dev/null 2>&1 || continue
>  
> -	${DBG} "@root_sbindir@/e2scrub" "${dev}"
> +	if type systemctl > /dev/null 2>&1; then
> +		${DBG} systemctl start "e2scrub@${dev}" 2> /dev/null
> +		res=$?
> +		if [ "${res}" -ne 0 ] && [ "${res}" -ne 1 ]; then
> +			${DBG} "@root_sbindir@/e2scrub" "${dev}"
> +		fi
> +	else
> +		${DBG} "@root_sbindir@/e2scrub" "${dev}"
> +	fi
>  done
>  
> -exit 0
> +exitcode 0
> diff --git a/scrub/e2scrub_all.service.in b/scrub/e2scrub_all.service.in
> new file mode 100644
> index 0000000..bc05184
> --- /dev/null
> +++ b/scrub/e2scrub_all.service.in
> @@ -0,0 +1,10 @@
> +[Unit]
> +Description=Online ext4 Metadata Check for All Filesystems
> +ConditionACPower=true
> +Documentation=man:e2scrub_all(8)
> +
> +[Service]
> +Type=oneshot
> +Environment=SERVICE_MODE=1
> +ExecStart=@root_sbindir@/e2scrub_all
> +SyslogIdentifier=e2scrub_all
> diff --git a/scrub/e2scrub_all.timer.in b/scrub/e2scrub_all.timer.in
> new file mode 100644
> index 0000000..3d558bb
> --- /dev/null
> +++ b/scrub/e2scrub_all.timer.in
> @@ -0,0 +1,11 @@
> +[Unit]
> +Description=Periodic ext4 Online Metadata Check for All Filesystems
> +
> +[Timer]
> +# Run on Sunday at 3:10am, to avoid running afoul of DST changes
> +OnCalendar=Sun *-*-* 03:10:00
> +RandomizedDelaySec=60
> +Persistent=true
> +
> +[Install]
> +WantedBy=timers.target
> diff --git a/scrub/e2scrub_all_cron.in b/scrub/e2scrub_all_cron.in
> new file mode 100644
> index 0000000..f9cff87
> --- /dev/null
> +++ b/scrub/e2scrub_all_cron.in
> @@ -0,0 +1,68 @@
> +#!/bin/bash
> +
> +#  Copyright (C) 2018 Oracle.  All Rights Reserved.
> +#
> +#  Author: Darrick J. Wong <darrick.wong@oracle.com>
> +#
> +#  This program is free software; you can redistribute it and/or
> +#  modify it under the terms of the GNU General Public License
> +#  as published by the Free Software Foundation; either version 2
> +#  of the License, or (at your option) any later version.
> +#
> +#  This program is distributed in the hope that it would be useful,
> +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +#  GNU General Public License for more details.
> +#
> +#  You should have received a copy of the GNU General Public License
> +#  along with this program; if not, write the Free Software Foundation,
> +#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> +
> +# Run e2scrub_all from a cronjob if we don't have systemd and we're not
> +# running on AC power.
> +
> +on_ac_power() {
> +	local any_known=no
> +
> +	# try sysfs power class first
> +	if [ -d /sys/class/power_supply ]; then
> +		for psu in /sys/class/power_supply/*; do
> +			if [ -r "$psu/type" ]; then
> +				type=$(cat "$psu/type")
> +
> +				# ignore batteries
> +				[ "$type" = "Battery" ] && continue
> +
> +				online=$(cat "$psu/online")
> +
> +				[ "$online" = 1 ] && return 0
> +				[ "$online" = 0 ] && any_known=yes
> +			fi
> +		done
> +
> +		[ "$any_known" = "yes" ] && return 1
> +	fi
> +
> +	# else fall back to AC adapters in /proc
> +	if [ -d /proc/acpi/ac_adapter ]; then
> +		for ac in /proc/acpi/ac_adapter/*; do
> +			if [ -r "$ac/state" ]; then
> +				grep -q on-line "$ac/state" && return 0
> +				grep -q off-line "$ac/state" && any_known=yes
> +			elif [ -r "$ac/status" ]; then
> +				grep -q on-line "$ac/status" && return 0
> +				grep -q off-line "$ac/status" && any_known=yes
> +			fi
> +		done
> +
> +		[ "$any_known" = "yes" ] && return 1
> +	fi
> +
> +	# Can't tell, just assume we're on AC.
> +	return 0
> +}
> +
> +test -e /run/systemd/system && exit 0
> +on_ac_power || exit 0
> +
> +exec @root_sbindir@/e2scrub_all
> diff --git a/scrub/e2scrub_fail.in b/scrub/e2scrub_fail.in
> new file mode 100644
> index 0000000..f27197a
> --- /dev/null
> +++ b/scrub/e2scrub_fail.in
> @@ -0,0 +1,25 @@
> +#!/bin/bash
> +
> +# Email logs of failed e2scrub unit runs when the systemd service fails.
> +
> +recipient="$1"
> +test -z "${recipient}" && exit 0
> +device="$2"
> +test -z "${device}" && exit 0
> +hostname="$(hostname -f 2>/dev/null)"
> +test -z "${hostname}" && hostname="${HOSTNAME}"
> +if ! type sendmail > /dev/null 2>&1; then
> +	echo "$0: sendmail program not found."
> +	exit 1
> +fi
> +
> +(cat << ENDL
> +To: $1
> +From: <e2scrub@${hostname}>
> +Subject: e2scrub failure on ${device}
> +
> +So sorry, the automatic e2scrub of ${device} on ${hostname} failed.
> +
> +A log of what happened follows:
> +ENDL
> +systemctl status --full --lines 4294967295 "e2scrub@${device}") | sendmail -t -i
> diff --git a/scrub/e2scrub_fail@.service.in b/scrub/e2scrub_fail@.service.in
> new file mode 100644
> index 0000000..df87949
> --- /dev/null
> +++ b/scrub/e2scrub_fail@.service.in
> @@ -0,0 +1,10 @@
> +[Unit]
> +Description=Online ext4 Metadata Check Failure Reporting for %I
> +
> +[Service]
> +Type=oneshot
> +Environment=EMAIL_ADDR=root
> +ExecStart=@pkglibdir@/e2scrub_fail "${EMAIL_ADDR}" %I
> +User=mail
> +Group=mail
> +SupplementaryGroups=systemd-journal
> diff --git a/scrub/e2scrub_reap.in b/scrub/e2scrub_reap.in
> new file mode 100644
> index 0000000..0fde0e6
> --- /dev/null
> +++ b/scrub/e2scrub_reap.in
> @@ -0,0 +1,48 @@
> +#!/bin/bash
> +
> +#  Copyright (C) 2018 Oracle.  All Rights Reserved.
> +#
> +#  Author: Darrick J. Wong <darrick.wong@oracle.com>
> +#
> +#  This program is free software; you can redistribute it and/or
> +#  modify it under the terms of the GNU General Public License
> +#  as published by the Free Software Foundation; either version 2
> +#  of the License, or (at your option) any later version.
> +#
> +#  This program is distributed in the hope that it would be useful,
> +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +#  GNU General Public License for more details.
> +#
> +#  You should have received a copy of the GNU General Public License
> +#  along with this program; if not, write the Free Software Foundation,
> +#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> +
> +# Clean up leftover e2scrub snapshots
> +
> +# Find any ${lvname}.e2scrub snapshot and delete it
> +lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role extra; do
> +	# Parse error?
> +	test -n "${extra}" || continue
> +	# Is this really a snapshot?
> +	echo "${role}" | grep -q "snapshot" || continue
> +	# An e2scrub snapshot?
> +	echo "${lv}" | grep -q ".e2scrub$" || continue
> +
> +	# Check that the e2scrub snap still exists after waiting a brief period.
> +	# This is how try to avoid deleting a snap from underneath a running
> +	# scrub.
> +	dev="/dev/${vg}/${lv}"
> +	test -e "${dev}" || continue
> +	sleep 2
> +	test -e "${dev}" || continue
> +
> +	# Remove and wait for removal to succeed.
> +	lvremove -f "${vg}/${lv}" 3>&-
> +	while [ -e "${dev}" ] && [ "$?" -eq "5" ]; do
> +		sleep 0.5
> +		lvremove -f "${vg}/${lv}" 3>&-
> +	done
> +done
> +
> +exitcode 0
> diff --git a/scrub/e2scrub_reap.service.in b/scrub/e2scrub_reap.service.in
> new file mode 100644
> index 0000000..38eefc4
> --- /dev/null
> +++ b/scrub/e2scrub_reap.service.in
> @@ -0,0 +1,21 @@
> +[Unit]
> +Description=Remove Stale Online ext4 Metadata Check Snapshots
> +
> +[Service]
> +Type=oneshot
> +WorkingDirectory=/
> +PrivateNetwork=true
> +ProtectSystem=true
> +ProtectHome=read-only
> +PrivateTmp=yes
> +AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
> +NoNewPrivileges=yes
> +User=root
> +IOSchedulingClass=idle
> +CPUSchedulingPolicy=idle
> +ExecStart=@pkglibdir@/e2scrub_reap

I thought that the idea was to use e2scrub -r and get rid of the
e2scrub_reap.

-Lukas

> +SyslogIdentifier=%N
> +RemainAfterExit=no
> +
> +[Install]
> +WantedBy=default.target
> diff --git a/util/subst.conf.in b/util/subst.conf.in
> index 6bf658d..0da4554 100644
> --- a/util/subst.conf.in
> +++ b/util/subst.conf.in
> @@ -21,3 +21,6 @@ JDEV
>  TDB_MAN_COMMENT		@TDB_MAN_COMMENT@
>  root_sbindir		@root_sbindir@
>  root_bindir		@root_bindir@
> +libdir			@libdir@
> +$exec_prefix		@exec_prefix@
> +pkglibdir		@libdir@/e2fsprogs
>
Darrick Wong March 13, 2018, 11:33 p.m. UTC | #2
On Tue, Mar 13, 2018 at 07:29:53AM +0100, Lukas Czerner wrote:
> On Mon, Mar 12, 2018 at 09:14:59AM -0700, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> > 
> > Add the ability to run the e2scrub utilities as a periodically scheduled
> > system service.
> 
> comment bellow
> 
> > 
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
> > ---
> >  MCONFIG.in                     |    5 +
> >  configure                      |  179 ++++++++++++++++++++++++++++++++++++++++
> >  configure.ac                   |   74 ++++++++++++++++-
> >  debian/e2fsprogs.files         |    3 +
> >  debian/e2fsprogs.postinst      |   19 ++++
> >  debian/e2fsprogs.postrm        |   35 ++++++++
> >  scrub/Makefile.in              |   90 ++++++++++++++++++++
> >  scrub/e2scrub.in               |   38 +++++++-
> >  scrub/e2scrub@.service.in      |   20 ++++
> >  scrub/e2scrub_all.cron.in      |    2 
> >  scrub/e2scrub_all.in           |   38 ++++++++
> >  scrub/e2scrub_all.service.in   |   10 ++
> >  scrub/e2scrub_all.timer.in     |   11 ++
> >  scrub/e2scrub_all_cron.in      |   68 +++++++++++++++
> >  scrub/e2scrub_fail.in          |   25 ++++++
> >  scrub/e2scrub_fail@.service.in |   10 ++
> >  scrub/e2scrub_reap.in          |   48 +++++++++++
> >  scrub/e2scrub_reap.service.in  |   21 +++++
> >  util/subst.conf.in             |    3 +
> >  19 files changed, 684 insertions(+), 15 deletions(-)
> >  create mode 100644 debian/e2fsprogs.postrm
> >  create mode 100644 scrub/e2scrub@.service.in
> >  create mode 100644 scrub/e2scrub_all.cron.in
> >  create mode 100644 scrub/e2scrub_all.service.in
> >  create mode 100644 scrub/e2scrub_all.timer.in
> >  create mode 100644 scrub/e2scrub_all_cron.in
> >  create mode 100644 scrub/e2scrub_fail.in
> >  create mode 100644 scrub/e2scrub_fail@.service.in
> >  create mode 100644 scrub/e2scrub_reap.in
> >  create mode 100644 scrub/e2scrub_reap.service.in
> > 
> > 
> > diff --git a/MCONFIG.in b/MCONFIG.in
> > index adeb5bd..ee83554 100644
> > --- a/MCONFIG.in
> > +++ b/MCONFIG.in
> > @@ -32,9 +32,14 @@ man8dir = $(mandir)/man8
> >  infodir = @infodir@
> >  datadir = @datadir@
> >  pkgconfigdir = $(libdir)/pkgconfig
> > +pkglibdir = $(libdir)/e2fsprogs
> >  
> >  HAVE_UDEV = @have_udev@
> >  UDEV_RULES_DIR = @pkg_udev_rules_dir@
> > +HAVE_CROND = @have_crond@
> > +CROND_DIR = @crond_dir@
> > +HAVE_SYSTEMD = @have_systemd@
> > +SYSTEMD_SYSTEM_UNIT_DIR = @systemd_system_unit_dir@
> >  
> >  @SET_MAKE@
> >  
> > diff --git a/configure b/configure
> > index 986e057..f6a0dea 100755
> > --- a/configure
> > +++ b/configure
> > @@ -625,6 +625,12 @@ gl_use_threads_default=
> >  ac_func_list=
> >  ac_subst_vars='LTLIBOBJS
> >  LIBOBJS
> > +systemd_system_unit_dir
> > +have_systemd
> > +systemd_LIBS
> > +systemd_CFLAGS
> > +crond_dir
> > +have_crond
> >  pkg_udev_rules_dir
> >  have_udev
> >  udev_LIBS
> > @@ -899,6 +905,8 @@ with_libintl_prefix
> >  enable_fuse2fs
> >  with_multiarch
> >  with_udev_rules_dir
> > +with_crond_dir
> > +with_systemd_unit_dir
> >  '
> >        ac_precious_vars='build_alias
> >  host_alias
> > @@ -913,7 +921,9 @@ PKG_CONFIG
> >  PKG_CONFIG_PATH
> >  PKG_CONFIG_LIBDIR
> >  udev_CFLAGS
> > -udev_LIBS'
> > +udev_LIBS
> > +systemd_CFLAGS
> > +systemd_LIBS'
> >  
> >  
> >  # Initialize some variables set by options.
> > @@ -1591,6 +1601,9 @@ Optional Packages:
> >    --with-multiarch=ARCH specify the multiarch triplet
> >    --with-udev-rules-dir[=DIR]
> >                            Install udev rules into DIR.
> > +  --with-crond-dir[=DIR]  Install system crontabs into DIR.
> > +  --with-systemd-unit-dir[=DIR]
> > +                          Install systemd system units into DIR.
> >  
> >  Some influential environment variables:
> >    CC          C compiler command
> > @@ -1608,6 +1621,10 @@ Some influential environment variables:
> >                path overriding pkg-config's built-in search path
> >    udev_CFLAGS C compiler flags for udev, overriding pkg-config
> >    udev_LIBS   linker flags for udev, overriding pkg-config
> > +  systemd_CFLAGS
> > +              C compiler flags for systemd, overriding pkg-config
> > +  systemd_LIBS
> > +              linker flags for systemd, overriding pkg-config
> >  
> >  Use these variables to override the choices made by `configure' or to help
> >  it to find libraries and programs with nonstandard names/locations.
> > @@ -13803,6 +13820,7 @@ else
> >  fi
> >  
> >  fi
> > +
> >  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can link with -static" >&5
> >  $as_echo_n "checking whether we can link with -static... " >&6; }
> >  if ${ac_cv_e2fsprogs_use_static+:} false; then :
> > @@ -14009,6 +14027,165 @@ fi
> >  
> >  
> >  
> > +
> > +# Check whether --with-crond_dir was given.
> > +if test "${with_crond_dir+set}" = set; then :
> > +  withval=$with_crond_dir;
> > +else
> > +  with_crond_dir=yes
> > +fi
> > +
> > +if test "x${with_crond_dir}" != "xno"; then :
> > +
> > +	if test "x${with_crond_dir}" = "xyes"; then :
> > +
> > +		if test -d "/etc/cron.d"; then :
> > +  with_crond_dir="/etc/cron.d"
> > +fi
> > +
> > +fi
> > +	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for system crontab dir" >&5
> > +$as_echo_n "checking for system crontab dir... " >&6; }
> > +	crond_dir="${with_crond_dir}"
> > +	if test -n "${crond_dir}"; then :
> > +
> > +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${crond_dir}" >&5
> > +$as_echo "${crond_dir}" >&6; }
> > +		have_crond="yes"
> > +
> > +else
> > +
> > +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> > +$as_echo "no" >&6; }
> > +		have_crond="no"
> > +
> > +fi
> > +
> > +else
> > +
> > +	have_crond="disabled"
> > +
> > +fi
> > +
> > +
> > +
> > +
> > +# Check whether --with-systemd_unit_dir was given.
> > +if test "${with_systemd_unit_dir+set}" = set; then :
> > +  withval=$with_systemd_unit_dir;
> > +else
> > +  with_systemd_unit_dir=yes
> > +fi
> > +
> > +if test "x${with_systemd_unit_dir}" != "xno"; then :
> > +
> > +	if test "x${with_systemd_unit_dir}" = "xyes"; then :
> > +
> > +
> > +pkg_failed=no
> > +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd" >&5
> > +$as_echo_n "checking for systemd... " >&6; }
> > +
> > +if test -n "$systemd_CFLAGS"; then
> > +    pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
> > + elif test -n "$PKG_CONFIG"; then
> > +    if test -n "$PKG_CONFIG" && \
> > +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
> > +  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
> > +  ac_status=$?
> > +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> > +  test $ac_status = 0; }; then
> > +  pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "systemd" 2>/dev/null`
> > +		      test "x$?" != "x0" && pkg_failed=yes
> > +else
> > +  pkg_failed=yes
> > +fi
> > + else
> > +    pkg_failed=untried
> > +fi
> > +if test -n "$systemd_LIBS"; then
> > +    pkg_cv_systemd_LIBS="$systemd_LIBS"
> > + elif test -n "$PKG_CONFIG"; then
> > +    if test -n "$PKG_CONFIG" && \
> > +    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
> > +  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
> > +  ac_status=$?
> > +  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
> > +  test $ac_status = 0; }; then
> > +  pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "systemd" 2>/dev/null`
> > +		      test "x$?" != "x0" && pkg_failed=yes
> > +else
> > +  pkg_failed=yes
> > +fi
> > + else
> > +    pkg_failed=untried
> > +fi
> > +
> > +
> > +
> > +if test $pkg_failed = yes; then
> > +   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> > +$as_echo "no" >&6; }
> > +
> > +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
> > +        _pkg_short_errors_supported=yes
> > +else
> > +        _pkg_short_errors_supported=no
> > +fi
> > +        if test $_pkg_short_errors_supported = yes; then
> > +	        systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "systemd" 2>&1`
> > +        else
> > +	        systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "systemd" 2>&1`
> > +        fi
> > +	# Put the nasty error message in config.log where it belongs
> > +	echo "$systemd_PKG_ERRORS" >&5
> > +
> > +
> > +			with_systemd_unit_dir=""
> > +
> > +elif test $pkg_failed = untried; then
> > +     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> > +$as_echo "no" >&6; }
> > +
> > +			with_systemd_unit_dir=""
> > +
> > +else
> > +	systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
> > +	systemd_LIBS=$pkg_cv_systemd_LIBS
> > +        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
> > +$as_echo "yes" >&6; }
> > +
> > +			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
> > +
> > +fi
> > +
> > +
> > +fi
> > +	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd system unit dir" >&5
> > +$as_echo_n "checking for systemd system unit dir... " >&6; }
> > +	systemd_system_unit_dir="${with_systemd_unit_dir}"
> > +	if test -n "${systemd_system_unit_dir}"; then :
> > +
> > +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemd_system_unit_dir}" >&5
> > +$as_echo "${systemd_system_unit_dir}" >&6; }
> > +		have_systemd="yes"
> > +
> > +else
> > +
> > +		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
> > +$as_echo "no" >&6; }
> > +		have_systemd="no"
> > +
> > +fi
> > +
> > +else
> > +
> > +	have_systemd="disabled"
> > +
> > +fi
> > +
> > +
> > +
> >  test -d lib || mkdir lib
> >  test -d include || mkdir include
> >  test -d include/linux || mkdir include/linux
> > diff --git a/configure.ac b/configure.ac
> > index 6e549c5..5a2c8be 100644
> > --- a/configure.ac
> > +++ b/configure.ac
> > @@ -1392,7 +1392,8 @@ else
> >      libdir=$libdir/$withval
> >      root_libdir=$root_libdir/$withval
> >  fi
> > -)dnl
> > +)
> > +dnl
> >  dnl
> >  dnl See if -static works.  This could fail if the linker does not
> >  dnl support -static, or if required external libraries are not available
> > @@ -1514,6 +1515,77 @@ AC_SUBST(have_udev)
> >  AC_SUBST(pkg_udev_rules_dir)
> >  
> >  dnl
> > +dnl Where do cron jobs go?
> > +dnl
> > +AC_ARG_WITH([crond_dir],
> > +  [AS_HELP_STRING([--with-crond-dir@<:@=DIR@:>@],
> > +	[Install system crontabs into DIR.])],
> > +  [],
> > +  [with_crond_dir=yes])
> > +AS_IF([test "x${with_crond_dir}" != "xno"],
> > +  [
> > +	AS_IF([test "x${with_crond_dir}" = "xyes"],
> > +	  [
> > +		AS_IF([test -d "/etc/cron.d"],
> > +		  [with_crond_dir="/etc/cron.d"])
> > +	  ])
> > +	AC_MSG_CHECKING([for system crontab dir])
> > +	crond_dir="${with_crond_dir}"
> > +	AS_IF([test -n "${crond_dir}"],
> > +	  [
> > +		AC_MSG_RESULT(${crond_dir})
> > +		have_crond="yes"
> > +	  ],
> > +	  [
> > +		AC_MSG_RESULT(no)
> > +		have_crond="no"
> > +	  ])
> > +  ],
> > +  [
> > +	have_crond="disabled"
> > +  ])
> > +AC_SUBST(have_crond)
> > +AC_SUBST(crond_dir)
> > +
> > +dnl
> > +dnl Where do systemd services go?
> > +dnl
> > +AC_ARG_WITH([systemd_unit_dir],
> > +  [AS_HELP_STRING([--with-systemd-unit-dir@<:@=DIR@:>@],
> > +	[Install systemd system units into DIR.])],
> > +  [],
> > +  [with_systemd_unit_dir=yes])
> > +AS_IF([test "x${with_systemd_unit_dir}" != "xno"],
> > +  [
> > +	AS_IF([test "x${with_systemd_unit_dir}" = "xyes"],
> > +	  [
> > +		PKG_CHECK_MODULES([systemd], [systemd],
> > +		  [
> > +			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
> > +		  ], [
> > +			with_systemd_unit_dir=""
> > +		  ])
> > +		m4_pattern_allow([^PKG_(MAJOR|MINOR|BUILD|REVISION)$])
> > +	  ])
> > +	AC_MSG_CHECKING([for systemd system unit dir])
> > +	systemd_system_unit_dir="${with_systemd_unit_dir}"
> > +	AS_IF([test -n "${systemd_system_unit_dir}"],
> > +	  [
> > +		AC_MSG_RESULT(${systemd_system_unit_dir})
> > +		have_systemd="yes"
> > +	  ],
> > +	  [
> > +		AC_MSG_RESULT(no)
> > +		have_systemd="no"
> > +	  ])
> > +  ],
> > +  [
> > +	have_systemd="disabled"
> > +  ])
> > +AC_SUBST(have_systemd)
> > +AC_SUBST(systemd_system_unit_dir)
> > +
> > +dnl
> >  dnl Make our output files, being sure that we create the some miscellaneous 
> >  dnl directories
> >  dnl
> > diff --git a/debian/e2fsprogs.files b/debian/e2fsprogs.files
> > index 78720fe..e0e49ce 100644
> > --- a/debian/e2fsprogs.files
> > +++ b/debian/e2fsprogs.files
> > @@ -1,6 +1,9 @@
> >  sbin
> >  usr/bin
> > +usr/lib
> >  usr/sbin
> >  usr/share/man
> >  etc
> >  lib/udev/rules.d
> > +lib/systemd/system
> > +usr/lib
> > diff --git a/debian/e2fsprogs.postinst b/debian/e2fsprogs.postinst
> > index 00ac363..e7acb0e 100644
> > --- a/debian/e2fsprogs.postinst
> > +++ b/debian/e2fsprogs.postinst
> > @@ -10,4 +10,23 @@ fi
> >  
> >  #DEBHELPER#
> >  
> > +# debhelper doesn't know what timers are...
> > +update_svc() {
> > +	deb-systemd-helper unmask "$1" >/dev/null || true
> > +
> > +	if deb-systemd-helper --quiet was-enabled "$1"; then
> > +		deb-systemd-helper enable "$1" >/dev/null || true
> > +	else
> > +		deb-systemd-helper update-state "$1" >/dev/null || true
> > +	fi
> > +}
> > +update_svc e2scrub_all.timer
> > +update_svc e2scrub_reap.service
> > +
> > +# Start our new services
> > +if [ -d /run/systemd/system ]; then
> > +	systemctl --system daemon-reload >/dev/null || true
> > +	deb-systemd-invoke start e2scrub_all.timer >/dev/null || true
> > +fi
> > +
> >  exit 0
> > diff --git a/debian/e2fsprogs.postrm b/debian/e2fsprogs.postrm
> > new file mode 100644
> > index 0000000..32cb642
> > --- /dev/null
> > +++ b/debian/e2fsprogs.postrm
> > @@ -0,0 +1,35 @@
> > +#!/bin/sh
> > +
> > +update_svc() {
> > +	deb-systemd-helper mask "$1" >/dev/null || true
> > +
> > +	if deb-systemd-helper --quiet was-enabled "$1"; then
> > +		# Enables the unit on first installation, creates new
> > +		# symlinks on upgrades if the unit file has changed.
> > +		deb-systemd-helper disable "$1" >/dev/null || true
> > +	fi
> > +}
> > +
> > +if [ "$1" != "upgrade" ]; then
> > +	# Abort on error.
> > +	set -e
> > +
> > +	if [ -x /usr/sbin/update-initramfs -a \
> > +		-e /etc/initramfs-tools/initramfs.conf ]; then
> > +	    update-initramfs -u
> > +	fi
> > +
> > +	#DEBHELPER#
> > +
> > +	# debhelper doesn't know what timers are...
> > +	update_svc e2scrub_all.timer
> > +	update_svc e2scrub_reap.service
> > +
> > +	# Start our new services
> > +	if [ -d /run/systemd/system ]; then
> > +		deb-systemd-invoke stop e2scrub_all.timer >/dev/null || true
> > +	fi
> > +fi
> > +
> > +exit 0
> > +
> > diff --git a/scrub/Makefile.in b/scrub/Makefile.in
> > index 1744941..cc2e9ce 100644
> > --- a/scrub/Makefile.in
> > +++ b/scrub/Makefile.in
> > @@ -22,7 +22,23 @@ INSTALL_TGT	+= install-udev
> >  UNINSTALL_TGT	+= uninstall-udev
> >  endif
> >  
> > -all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES)
> > +ifeq ($(HAVE_CROND),yes)
> > +CRONTABS	= e2scrub_all.cron
> > +LIBPROGS	+= e2scrub_reap e2scrub_all_cron
> > +INSTALLDIRS_TGT	+= installdirs-crond installdirs-libprogs
> > +INSTALL_TGT	+= install-crond install-libprogs
> > +UNINSTALL_TGT	+= uninstall-crond uninstall-libprogs
> > +endif
> > +
> > +ifeq ($(HAVE_SYSTEMD),yes)
> > +SERVICE_FILES	= e2scrub@.service e2scrub_all.service e2scrub_all.timer e2scrub_fail@.service e2scrub_reap.service
> > +LIBPROGS	+= e2scrub_fail e2scrub_reap
> > +INSTALLDIRS_TGT	+= installdirs-systemd installdirs-libprogs
> > +INSTALL_TGT	+= install-systemd install-libprogs
> > +UNINSTALL_TGT	+= uninstall-systemd uninstall-libprogs
> > +endif
> > +
> > +all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES) $(SERVICE_FILES) $(CRONTABS) $(LIBPROGS)
> >  
> >  e2scrub: $(DEP_SUBSTITUTE) e2scrub.in
> >  	$(E) "	SUBST $@"
> > @@ -34,6 +50,21 @@ e2scrub_all: e2scrub_all.in
> >  	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all.in $@
> >  	$(Q) chmod a+x $@
> >  
> > +e2scrub_fail: e2scrub_fail.in
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_fail.in $@
> > +	$(Q) chmod a+x $@
> > +
> > +e2scrub_reap: e2scrub_reap.in
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_reap.in $@
> > +	$(Q) chmod a+x $@
> > +
> > +e2scrub_all_cron: e2scrub_all_cron.in
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all_cron.in $@
> > +	$(Q) chmod a+x $@
> > +
> >  %.8: %.8.in $(DEP_SUBSTITUTE)
> >  	$(E) "	SUBST $@"
> >  	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> > @@ -46,10 +77,34 @@ e2scrub_all: e2scrub_all.in
> >  	$(E) "	SUBST $@"
> >  	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> >  
> > +%.service: %.service.in $(DEP_SUBSTITUTE)
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> > +
> > +%.cron: %.cron.in $(DEP_SUBSTITUTE)
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> > +
> > +%.timer: %.timer.in $(DEP_SUBSTITUTE)
> > +	$(E) "	SUBST $@"
> > +	$(Q) $(SUBSTITUTE_UPTIME) $< $@
> > +
> >  installdirs-udev:
> >  	$(E) "	MKINSTALLDIRS $(UDEV_RULES_DIR)"
> >  	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(UDEV_RULES_DIR)
> >  
> > +installdirs-crond:
> > +	$(E) "	MKINSTALLDIRS $(CROND_DIR)"
> > +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(CROND_DIR)
> > +
> > +installdirs-libprogs:
> > +	$(E) "	MKINSTALLDIRS $(pkglibdir)"
> > +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(pkglibdir)
> > +
> > +installdirs-systemd:
> > +	$(E) "	MKINSTALLDIRS $(SYSTEMD_SYSTEM_UNIT_DIR)"
> > +	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)
> > +
> >  installdirs: $(INSTALLDIRS_TGT)
> >  	$(E) "	MKINSTALLDIRS $(root_sbindir) $(man8dir) $(root_sysconfdir)"
> >  	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(root_sbindir) \
> > @@ -61,6 +116,24 @@ install-udev:
> >  		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
> >  	done
> >  
> > +install-crond:
> > +	$(Q) for i in $(CRONTABS); do \
> > +		$(ES) "	INSTALL $(CROND_DIR)/$$i"; \
> > +		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(CROND_DIR)/$$i; \
> > +	done
> > +
> > +install-libprogs: $(LIBPROGS)
> > +	$(Q) for i in $(LIBPROGS); do \
> > +		$(ES) "	INSTALL $(pkglibdir)/$$i"; \
> > +		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(pkglibdir)/$$i; \
> > +	done
> > +
> > +install-systemd: $(SERVICE_FILES)
> > +	$(Q) for i in $(SERVICE_FILES); do \
> > +		$(ES) "	INSTALL_DATA $(SYSTEMD_SYSTEM_UNIT_DIR)/$$i"; \
> > +		$(INSTALL_DATA) $$i $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
> > +	done
> > +
> >  install: $(PROGS) $(MANPAGES) $(FMANPAGES) installdirs $(INSTALL_TGT)
> >  	$(Q) for i in $(PROGS); do \
> >  		$(ES) "	INSTALL $(root_sbindir)/$$i"; \
> > @@ -83,6 +156,21 @@ uninstall-udev:
> >  		$(RM) -f $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
> >  	done
> >  
> > +uninstall-crond:
> > +	for i in $(CRONTABS); do \
> > +		$(RM) -f $(DESTDIR)$(CROND_DIR)/$$i; \
> > +	done
> > +
> > +uninstall-libprogs:
> > +	for i in $(LIBPROGS); do \
> > +		$(RM) -f $(DESTDIR)$(pkglibdir)/$$i; \
> > +	done
> > +
> > +uninstall-systemd:
> > +	for i in $(SERVICE_FILES); do \
> > +		$(RM) -f $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
> > +	done
> > +
> >  uninstall: $(UNINSTALL_TGT)
> >  	for i in $(PROGS); do \
> >  		$(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \
> > diff --git a/scrub/e2scrub.in b/scrub/e2scrub.in
> > index f77ec6f..a19b6bb 100644
> > --- a/scrub/e2scrub.in
> > +++ b/scrub/e2scrub.in
> > @@ -44,12 +44,34 @@ print_version() {
> >  	echo "e2scrub @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
> >  }
> >  
> > +exitcode() {
> > +	ret="$1"
> > +
> > +	# If we're being run as a service, the return code must fit the LSB
> > +	# init script action error guidelines, which is to say that we
> > +	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
> > +	# section 22.2) and hope the admin will scan the log for what
> > +	# actually happened.
> > +
> > +	# We have to sleep 2 seconds here because journald uses the pid to
> > +	# connect our log messages to the systemd service.  This is critical
> > +	# for capturing all the log messages if the scrub fails, because the
> > +	# fail service uses the service name to gather log messages for the
> > +	# error report.
> > +	if [ -n "${SERVICE_MODE}" ]; then
> > +		test "${ret}" -ne 0 && ret=1
> > +		sleep 2
> > +	fi
> > +
> > +	exit "${ret}"
> > +}
> > +
> >  while getopts "rtV" opt; do
> >  	case "${opt}" in
> >  	"r") reap=1;;
> >  	"t") fstrim=1;;
> > -	"V") print_version; exit 0;;
> > -	*) print_help; exit 2;;
> > +	"V") print_version; exitcode 0;;
> > +	*) print_help; exitcode 2;;
> >  	esac
> >  done
> >  shift "$((OPTIND - 1))"
> > @@ -57,18 +79,18 @@ shift "$((OPTIND - 1))"
> >  dev="$1"
> >  if [ -z "${dev}" ]; then
> >  	print_help
> > -	exit 1
> > +	exitcode 1
> >  elif [ ! -b "${dev}" ]; then
> >  	echo "${dev}: Not a block device?"
> >  	print_help
> > -	exit 16
> > +	exitcode 16
> >  fi
> >  
> >  # Make sure this is an LVM device we can snapshot
> >  eval $(lvs --nameprefixes -o name,vgname --noheadings "${dev}")
> >  if [ -z "${LVM2_VG_NAME}" ] || [ -z "${LVM2_LV_NAME}" ]; then
> >  	echo "${dev}: Not a LVM device."
> > -	exit 16
> > +	exitcode 16
> >  fi
> >  start_time="$(date +'%Y%m%d%H%M%S')"
> >  snap="${LVM2_LV_NAME}.e2scrub"
> > @@ -80,7 +102,7 @@ ext[234])
> >  	;;
> >  *)
> >  	echo "${dev}: Filesystem of type ${fstype} not supported."
> > -	exit 16
> > +	exitcode 16
> >  	;;
> >  esac
> >  
> > @@ -128,7 +150,7 @@ if [ "${reap}" -gt 0 ]; then
> >  fi
> >  if ! setup; then
> >  	echo "Snapshot of ${dev} FAILED, will not check!"
> > -	exit 1
> > +	exitcode 1
> >  fi
> >  
> >  # Check and react
> > @@ -167,4 +189,4 @@ else
> >  	fi
> >  fi
> >  
> > -exit "${ret}"
> > +exitcode "${ret}"
> > diff --git a/scrub/e2scrub@.service.in b/scrub/e2scrub@.service.in
> > new file mode 100644
> > index 0000000..496f894
> > --- /dev/null
> > +++ b/scrub/e2scrub@.service.in
> > @@ -0,0 +1,20 @@
> > +[Unit]
> > +Description=Online ext4 Metadata Check for %I
> > +OnFailure=e2scrub_fail@%i.service
> > +Documentation=man:e2scrub(8)
> > +
> > +[Service]
> > +Type=oneshot
> > +WorkingDirectory=/
> > +PrivateNetwork=true
> > +ProtectSystem=true
> > +ProtectHome=read-only
> > +PrivateTmp=yes
> > +AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
> > +NoNewPrivileges=yes
> > +User=root
> > +IOSchedulingClass=idle
> > +CPUSchedulingPolicy=idle
> > +Environment=SERVICE_MODE=1
> > +ExecStart=@root_sbindir@/e2scrub -t %I
> > +SyslogIdentifier=%N
> > diff --git a/scrub/e2scrub_all.cron.in b/scrub/e2scrub_all.cron.in
> > new file mode 100644
> > index 0000000..0cb5f1f
> > --- /dev/null
> > +++ b/scrub/e2scrub_all.cron.in
> > @@ -0,0 +1,2 @@
> > +30 3 * * 0 root test -e /run/systemd/system || @pkglibdir@/e2scrub_all_cron
> > +10 3 * * * root test -e /run/systemd/system || @pkglibdir@/e2scrub_reap
> > diff --git a/scrub/e2scrub_all.in b/scrub/e2scrub_all.in
> > index 453981b..fbf2e95 100644
> > --- a/scrub/e2scrub_all.in
> > +++ b/scrub/e2scrub_all.in
> > @@ -30,10 +30,32 @@ print_version() {
> >  	echo "e2scrub_all @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
> >  }
> >  
> > +exitcode() {
> > +	ret="$1"
> > +
> > +	# If we're being run as a service, the return code must fit the LSB
> > +	# init script action error guidelines, which is to say that we
> > +	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
> > +	# section 22.2) and hope the admin will scan the log for what
> > +	# actually happened.
> > +
> > +	# We have to sleep 2 seconds here because journald uses the pid to
> > +	# connect our log messages to the systemd service.  This is critical
> > +	# for capturing all the log messages if the scrub fails, because the
> > +	# fail service uses the service name to gather log messages for the
> > +	# error report.
> > +	if [ -n "${SERVICE_MODE}" ]; then
> > +		test "${ret}" -ne 0 && ret=1
> > +		sleep 2
> > +	fi
> > +
> > +	exit "${ret}"
> > +}
> > +
> >  while getopts "V" opt; do
> >  	case "${opt}" in
> > -	"V") print_version; exit 0;;
> > -	*) print_help; exit 2;;
> > +	"V") print_version; exitcode 0;;
> > +	*) print_help; exitcode 2;;
> >  	esac
> >  done
> >  shift "$((OPTIND - 1))"
> > @@ -49,7 +71,15 @@ lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role
> >  	# Skip non-ext[234]
> >  	blkid -p -n "${types}" "${dev}" > /dev/null 2>&1 || continue
> >  
> > -	${DBG} "@root_sbindir@/e2scrub" "${dev}"
> > +	if type systemctl > /dev/null 2>&1; then
> > +		${DBG} systemctl start "e2scrub@${dev}" 2> /dev/null
> > +		res=$?
> > +		if [ "${res}" -ne 0 ] && [ "${res}" -ne 1 ]; then
> > +			${DBG} "@root_sbindir@/e2scrub" "${dev}"
> > +		fi
> > +	else
> > +		${DBG} "@root_sbindir@/e2scrub" "${dev}"
> > +	fi
> >  done
> >  
> > -exit 0
> > +exitcode 0
> > diff --git a/scrub/e2scrub_all.service.in b/scrub/e2scrub_all.service.in
> > new file mode 100644
> > index 0000000..bc05184
> > --- /dev/null
> > +++ b/scrub/e2scrub_all.service.in
> > @@ -0,0 +1,10 @@
> > +[Unit]
> > +Description=Online ext4 Metadata Check for All Filesystems
> > +ConditionACPower=true
> > +Documentation=man:e2scrub_all(8)
> > +
> > +[Service]
> > +Type=oneshot
> > +Environment=SERVICE_MODE=1
> > +ExecStart=@root_sbindir@/e2scrub_all
> > +SyslogIdentifier=e2scrub_all
> > diff --git a/scrub/e2scrub_all.timer.in b/scrub/e2scrub_all.timer.in
> > new file mode 100644
> > index 0000000..3d558bb
> > --- /dev/null
> > +++ b/scrub/e2scrub_all.timer.in
> > @@ -0,0 +1,11 @@
> > +[Unit]
> > +Description=Periodic ext4 Online Metadata Check for All Filesystems
> > +
> > +[Timer]
> > +# Run on Sunday at 3:10am, to avoid running afoul of DST changes
> > +OnCalendar=Sun *-*-* 03:10:00
> > +RandomizedDelaySec=60
> > +Persistent=true
> > +
> > +[Install]
> > +WantedBy=timers.target
> > diff --git a/scrub/e2scrub_all_cron.in b/scrub/e2scrub_all_cron.in
> > new file mode 100644
> > index 0000000..f9cff87
> > --- /dev/null
> > +++ b/scrub/e2scrub_all_cron.in
> > @@ -0,0 +1,68 @@
> > +#!/bin/bash
> > +
> > +#  Copyright (C) 2018 Oracle.  All Rights Reserved.
> > +#
> > +#  Author: Darrick J. Wong <darrick.wong@oracle.com>
> > +#
> > +#  This program is free software; you can redistribute it and/or
> > +#  modify it under the terms of the GNU General Public License
> > +#  as published by the Free Software Foundation; either version 2
> > +#  of the License, or (at your option) any later version.
> > +#
> > +#  This program is distributed in the hope that it would be useful,
> > +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +#  GNU General Public License for more details.
> > +#
> > +#  You should have received a copy of the GNU General Public License
> > +#  along with this program; if not, write the Free Software Foundation,
> > +#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> > +
> > +# Run e2scrub_all from a cronjob if we don't have systemd and we're not
> > +# running on AC power.
> > +
> > +on_ac_power() {
> > +	local any_known=no
> > +
> > +	# try sysfs power class first
> > +	if [ -d /sys/class/power_supply ]; then
> > +		for psu in /sys/class/power_supply/*; do
> > +			if [ -r "$psu/type" ]; then
> > +				type=$(cat "$psu/type")
> > +
> > +				# ignore batteries
> > +				[ "$type" = "Battery" ] && continue
> > +
> > +				online=$(cat "$psu/online")
> > +
> > +				[ "$online" = 1 ] && return 0
> > +				[ "$online" = 0 ] && any_known=yes
> > +			fi
> > +		done
> > +
> > +		[ "$any_known" = "yes" ] && return 1
> > +	fi
> > +
> > +	# else fall back to AC adapters in /proc
> > +	if [ -d /proc/acpi/ac_adapter ]; then
> > +		for ac in /proc/acpi/ac_adapter/*; do
> > +			if [ -r "$ac/state" ]; then
> > +				grep -q on-line "$ac/state" && return 0
> > +				grep -q off-line "$ac/state" && any_known=yes
> > +			elif [ -r "$ac/status" ]; then
> > +				grep -q on-line "$ac/status" && return 0
> > +				grep -q off-line "$ac/status" && any_known=yes
> > +			fi
> > +		done
> > +
> > +		[ "$any_known" = "yes" ] && return 1
> > +	fi
> > +
> > +	# Can't tell, just assume we're on AC.
> > +	return 0
> > +}
> > +
> > +test -e /run/systemd/system && exit 0
> > +on_ac_power || exit 0
> > +
> > +exec @root_sbindir@/e2scrub_all
> > diff --git a/scrub/e2scrub_fail.in b/scrub/e2scrub_fail.in
> > new file mode 100644
> > index 0000000..f27197a
> > --- /dev/null
> > +++ b/scrub/e2scrub_fail.in
> > @@ -0,0 +1,25 @@
> > +#!/bin/bash
> > +
> > +# Email logs of failed e2scrub unit runs when the systemd service fails.
> > +
> > +recipient="$1"
> > +test -z "${recipient}" && exit 0
> > +device="$2"
> > +test -z "${device}" && exit 0
> > +hostname="$(hostname -f 2>/dev/null)"
> > +test -z "${hostname}" && hostname="${HOSTNAME}"
> > +if ! type sendmail > /dev/null 2>&1; then
> > +	echo "$0: sendmail program not found."
> > +	exit 1
> > +fi
> > +
> > +(cat << ENDL
> > +To: $1
> > +From: <e2scrub@${hostname}>
> > +Subject: e2scrub failure on ${device}
> > +
> > +So sorry, the automatic e2scrub of ${device} on ${hostname} failed.
> > +
> > +A log of what happened follows:
> > +ENDL
> > +systemctl status --full --lines 4294967295 "e2scrub@${device}") | sendmail -t -i
> > diff --git a/scrub/e2scrub_fail@.service.in b/scrub/e2scrub_fail@.service.in
> > new file mode 100644
> > index 0000000..df87949
> > --- /dev/null
> > +++ b/scrub/e2scrub_fail@.service.in
> > @@ -0,0 +1,10 @@
> > +[Unit]
> > +Description=Online ext4 Metadata Check Failure Reporting for %I
> > +
> > +[Service]
> > +Type=oneshot
> > +Environment=EMAIL_ADDR=root
> > +ExecStart=@pkglibdir@/e2scrub_fail "${EMAIL_ADDR}" %I
> > +User=mail
> > +Group=mail
> > +SupplementaryGroups=systemd-journal
> > diff --git a/scrub/e2scrub_reap.in b/scrub/e2scrub_reap.in
> > new file mode 100644
> > index 0000000..0fde0e6
> > --- /dev/null
> > +++ b/scrub/e2scrub_reap.in
> > @@ -0,0 +1,48 @@
> > +#!/bin/bash
> > +
> > +#  Copyright (C) 2018 Oracle.  All Rights Reserved.
> > +#
> > +#  Author: Darrick J. Wong <darrick.wong@oracle.com>
> > +#
> > +#  This program is free software; you can redistribute it and/or
> > +#  modify it under the terms of the GNU General Public License
> > +#  as published by the Free Software Foundation; either version 2
> > +#  of the License, or (at your option) any later version.
> > +#
> > +#  This program is distributed in the hope that it would be useful,
> > +#  but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > +#  GNU General Public License for more details.
> > +#
> > +#  You should have received a copy of the GNU General Public License
> > +#  along with this program; if not, write the Free Software Foundation,
> > +#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
> > +
> > +# Clean up leftover e2scrub snapshots
> > +
> > +# Find any ${lvname}.e2scrub snapshot and delete it
> > +lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role extra; do
> > +	# Parse error?
> > +	test -n "${extra}" || continue
> > +	# Is this really a snapshot?
> > +	echo "${role}" | grep -q "snapshot" || continue
> > +	# An e2scrub snapshot?
> > +	echo "${lv}" | grep -q ".e2scrub$" || continue
> > +
> > +	# Check that the e2scrub snap still exists after waiting a brief period.
> > +	# This is how try to avoid deleting a snap from underneath a running
> > +	# scrub.
> > +	dev="/dev/${vg}/${lv}"
> > +	test -e "${dev}" || continue
> > +	sleep 2
> > +	test -e "${dev}" || continue
> > +
> > +	# Remove and wait for removal to succeed.
> > +	lvremove -f "${vg}/${lv}" 3>&-
> > +	while [ -e "${dev}" ] && [ "$?" -eq "5" ]; do
> > +		sleep 0.5
> > +		lvremove -f "${vg}/${lv}" 3>&-
> > +	done
> > +done
> > +
> > +exitcode 0
> > diff --git a/scrub/e2scrub_reap.service.in b/scrub/e2scrub_reap.service.in
> > new file mode 100644
> > index 0000000..38eefc4
> > --- /dev/null
> > +++ b/scrub/e2scrub_reap.service.in
> > @@ -0,0 +1,21 @@
> > +[Unit]
> > +Description=Remove Stale Online ext4 Metadata Check Snapshots
> > +
> > +[Service]
> > +Type=oneshot
> > +WorkingDirectory=/
> > +PrivateNetwork=true
> > +ProtectSystem=true
> > +ProtectHome=read-only
> > +PrivateTmp=yes
> > +AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
> > +NoNewPrivileges=yes
> > +User=root
> > +IOSchedulingClass=idle
> > +CPUSchedulingPolicy=idle
> > +ExecStart=@pkglibdir@/e2scrub_reap
> 
> I thought that the idea was to use e2scrub -r and get rid of the
> e2scrub_reap.

e2scrub -r only gets rid of one lvm volume's e2scrub snapshot, so you
need a utility to find them all and nuke them.  That used to be
e2scrub_reap, but I've folded that into "e2scrub_all -r".

--D

> -Lukas
> 
> > +SyslogIdentifier=%N
> > +RemainAfterExit=no
> > +
> > +[Install]
> > +WantedBy=default.target
> > diff --git a/util/subst.conf.in b/util/subst.conf.in
> > index 6bf658d..0da4554 100644
> > --- a/util/subst.conf.in
> > +++ b/util/subst.conf.in
> > @@ -21,3 +21,6 @@ JDEV
> >  TDB_MAN_COMMENT		@TDB_MAN_COMMENT@
> >  root_sbindir		@root_sbindir@
> >  root_bindir		@root_bindir@
> > +libdir			@libdir@
> > +$exec_prefix		@exec_prefix@
> > +pkglibdir		@libdir@/e2fsprogs
> >
diff mbox series

Patch

diff --git a/MCONFIG.in b/MCONFIG.in
index adeb5bd..ee83554 100644
--- a/MCONFIG.in
+++ b/MCONFIG.in
@@ -32,9 +32,14 @@  man8dir = $(mandir)/man8
 infodir = @infodir@
 datadir = @datadir@
 pkgconfigdir = $(libdir)/pkgconfig
+pkglibdir = $(libdir)/e2fsprogs
 
 HAVE_UDEV = @have_udev@
 UDEV_RULES_DIR = @pkg_udev_rules_dir@
+HAVE_CROND = @have_crond@
+CROND_DIR = @crond_dir@
+HAVE_SYSTEMD = @have_systemd@
+SYSTEMD_SYSTEM_UNIT_DIR = @systemd_system_unit_dir@
 
 @SET_MAKE@
 
diff --git a/configure b/configure
index 986e057..f6a0dea 100755
--- a/configure
+++ b/configure
@@ -625,6 +625,12 @@  gl_use_threads_default=
 ac_func_list=
 ac_subst_vars='LTLIBOBJS
 LIBOBJS
+systemd_system_unit_dir
+have_systemd
+systemd_LIBS
+systemd_CFLAGS
+crond_dir
+have_crond
 pkg_udev_rules_dir
 have_udev
 udev_LIBS
@@ -899,6 +905,8 @@  with_libintl_prefix
 enable_fuse2fs
 with_multiarch
 with_udev_rules_dir
+with_crond_dir
+with_systemd_unit_dir
 '
       ac_precious_vars='build_alias
 host_alias
@@ -913,7 +921,9 @@  PKG_CONFIG
 PKG_CONFIG_PATH
 PKG_CONFIG_LIBDIR
 udev_CFLAGS
-udev_LIBS'
+udev_LIBS
+systemd_CFLAGS
+systemd_LIBS'
 
 
 # Initialize some variables set by options.
@@ -1591,6 +1601,9 @@  Optional Packages:
   --with-multiarch=ARCH specify the multiarch triplet
   --with-udev-rules-dir[=DIR]
                           Install udev rules into DIR.
+  --with-crond-dir[=DIR]  Install system crontabs into DIR.
+  --with-systemd-unit-dir[=DIR]
+                          Install systemd system units into DIR.
 
 Some influential environment variables:
   CC          C compiler command
@@ -1608,6 +1621,10 @@  Some influential environment variables:
               path overriding pkg-config's built-in search path
   udev_CFLAGS C compiler flags for udev, overriding pkg-config
   udev_LIBS   linker flags for udev, overriding pkg-config
+  systemd_CFLAGS
+              C compiler flags for systemd, overriding pkg-config
+  systemd_LIBS
+              linker flags for systemd, overriding pkg-config
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -13803,6 +13820,7 @@  else
 fi
 
 fi
+
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we can link with -static" >&5
 $as_echo_n "checking whether we can link with -static... " >&6; }
 if ${ac_cv_e2fsprogs_use_static+:} false; then :
@@ -14009,6 +14027,165 @@  fi
 
 
 
+
+# Check whether --with-crond_dir was given.
+if test "${with_crond_dir+set}" = set; then :
+  withval=$with_crond_dir;
+else
+  with_crond_dir=yes
+fi
+
+if test "x${with_crond_dir}" != "xno"; then :
+
+	if test "x${with_crond_dir}" = "xyes"; then :
+
+		if test -d "/etc/cron.d"; then :
+  with_crond_dir="/etc/cron.d"
+fi
+
+fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for system crontab dir" >&5
+$as_echo_n "checking for system crontab dir... " >&6; }
+	crond_dir="${with_crond_dir}"
+	if test -n "${crond_dir}"; then :
+
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${crond_dir}" >&5
+$as_echo "${crond_dir}" >&6; }
+		have_crond="yes"
+
+else
+
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		have_crond="no"
+
+fi
+
+else
+
+	have_crond="disabled"
+
+fi
+
+
+
+
+# Check whether --with-systemd_unit_dir was given.
+if test "${with_systemd_unit_dir+set}" = set; then :
+  withval=$with_systemd_unit_dir;
+else
+  with_systemd_unit_dir=yes
+fi
+
+if test "x${with_systemd_unit_dir}" != "xno"; then :
+
+	if test "x${with_systemd_unit_dir}" = "xyes"; then :
+
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd" >&5
+$as_echo_n "checking for systemd... " >&6; }
+
+if test -n "$systemd_CFLAGS"; then
+    pkg_cv_systemd_CFLAGS="$systemd_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_systemd_CFLAGS=`$PKG_CONFIG --cflags "systemd" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$systemd_LIBS"; then
+    pkg_cv_systemd_LIBS="$systemd_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"systemd\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "systemd") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_systemd_LIBS=`$PKG_CONFIG --libs "systemd" 2>/dev/null`
+		      test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+	        systemd_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "systemd" 2>&1`
+        else
+	        systemd_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "systemd" 2>&1`
+        fi
+	# Put the nasty error message in config.log where it belongs
+	echo "$systemd_PKG_ERRORS" >&5
+
+
+			with_systemd_unit_dir=""
+
+elif test $pkg_failed = untried; then
+     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+			with_systemd_unit_dir=""
+
+else
+	systemd_CFLAGS=$pkg_cv_systemd_CFLAGS
+	systemd_LIBS=$pkg_cv_systemd_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
+
+fi
+
+
+fi
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemd system unit dir" >&5
+$as_echo_n "checking for systemd system unit dir... " >&6; }
+	systemd_system_unit_dir="${with_systemd_unit_dir}"
+	if test -n "${systemd_system_unit_dir}"; then :
+
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemd_system_unit_dir}" >&5
+$as_echo "${systemd_system_unit_dir}" >&6; }
+		have_systemd="yes"
+
+else
+
+		{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+		have_systemd="no"
+
+fi
+
+else
+
+	have_systemd="disabled"
+
+fi
+
+
+
 test -d lib || mkdir lib
 test -d include || mkdir include
 test -d include/linux || mkdir include/linux
diff --git a/configure.ac b/configure.ac
index 6e549c5..5a2c8be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1392,7 +1392,8 @@  else
     libdir=$libdir/$withval
     root_libdir=$root_libdir/$withval
 fi
-)dnl
+)
+dnl
 dnl
 dnl See if -static works.  This could fail if the linker does not
 dnl support -static, or if required external libraries are not available
@@ -1514,6 +1515,77 @@  AC_SUBST(have_udev)
 AC_SUBST(pkg_udev_rules_dir)
 
 dnl
+dnl Where do cron jobs go?
+dnl
+AC_ARG_WITH([crond_dir],
+  [AS_HELP_STRING([--with-crond-dir@<:@=DIR@:>@],
+	[Install system crontabs into DIR.])],
+  [],
+  [with_crond_dir=yes])
+AS_IF([test "x${with_crond_dir}" != "xno"],
+  [
+	AS_IF([test "x${with_crond_dir}" = "xyes"],
+	  [
+		AS_IF([test -d "/etc/cron.d"],
+		  [with_crond_dir="/etc/cron.d"])
+	  ])
+	AC_MSG_CHECKING([for system crontab dir])
+	crond_dir="${with_crond_dir}"
+	AS_IF([test -n "${crond_dir}"],
+	  [
+		AC_MSG_RESULT(${crond_dir})
+		have_crond="yes"
+	  ],
+	  [
+		AC_MSG_RESULT(no)
+		have_crond="no"
+	  ])
+  ],
+  [
+	have_crond="disabled"
+  ])
+AC_SUBST(have_crond)
+AC_SUBST(crond_dir)
+
+dnl
+dnl Where do systemd services go?
+dnl
+AC_ARG_WITH([systemd_unit_dir],
+  [AS_HELP_STRING([--with-systemd-unit-dir@<:@=DIR@:>@],
+	[Install systemd system units into DIR.])],
+  [],
+  [with_systemd_unit_dir=yes])
+AS_IF([test "x${with_systemd_unit_dir}" != "xno"],
+  [
+	AS_IF([test "x${with_systemd_unit_dir}" = "xyes"],
+	  [
+		PKG_CHECK_MODULES([systemd], [systemd],
+		  [
+			with_systemd_unit_dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"
+		  ], [
+			with_systemd_unit_dir=""
+		  ])
+		m4_pattern_allow([^PKG_(MAJOR|MINOR|BUILD|REVISION)$])
+	  ])
+	AC_MSG_CHECKING([for systemd system unit dir])
+	systemd_system_unit_dir="${with_systemd_unit_dir}"
+	AS_IF([test -n "${systemd_system_unit_dir}"],
+	  [
+		AC_MSG_RESULT(${systemd_system_unit_dir})
+		have_systemd="yes"
+	  ],
+	  [
+		AC_MSG_RESULT(no)
+		have_systemd="no"
+	  ])
+  ],
+  [
+	have_systemd="disabled"
+  ])
+AC_SUBST(have_systemd)
+AC_SUBST(systemd_system_unit_dir)
+
+dnl
 dnl Make our output files, being sure that we create the some miscellaneous 
 dnl directories
 dnl
diff --git a/debian/e2fsprogs.files b/debian/e2fsprogs.files
index 78720fe..e0e49ce 100644
--- a/debian/e2fsprogs.files
+++ b/debian/e2fsprogs.files
@@ -1,6 +1,9 @@ 
 sbin
 usr/bin
+usr/lib
 usr/sbin
 usr/share/man
 etc
 lib/udev/rules.d
+lib/systemd/system
+usr/lib
diff --git a/debian/e2fsprogs.postinst b/debian/e2fsprogs.postinst
index 00ac363..e7acb0e 100644
--- a/debian/e2fsprogs.postinst
+++ b/debian/e2fsprogs.postinst
@@ -10,4 +10,23 @@  fi
 
 #DEBHELPER#
 
+# debhelper doesn't know what timers are...
+update_svc() {
+	deb-systemd-helper unmask "$1" >/dev/null || true
+
+	if deb-systemd-helper --quiet was-enabled "$1"; then
+		deb-systemd-helper enable "$1" >/dev/null || true
+	else
+		deb-systemd-helper update-state "$1" >/dev/null || true
+	fi
+}
+update_svc e2scrub_all.timer
+update_svc e2scrub_reap.service
+
+# Start our new services
+if [ -d /run/systemd/system ]; then
+	systemctl --system daemon-reload >/dev/null || true
+	deb-systemd-invoke start e2scrub_all.timer >/dev/null || true
+fi
+
 exit 0
diff --git a/debian/e2fsprogs.postrm b/debian/e2fsprogs.postrm
new file mode 100644
index 0000000..32cb642
--- /dev/null
+++ b/debian/e2fsprogs.postrm
@@ -0,0 +1,35 @@ 
+#!/bin/sh
+
+update_svc() {
+	deb-systemd-helper mask "$1" >/dev/null || true
+
+	if deb-systemd-helper --quiet was-enabled "$1"; then
+		# Enables the unit on first installation, creates new
+		# symlinks on upgrades if the unit file has changed.
+		deb-systemd-helper disable "$1" >/dev/null || true
+	fi
+}
+
+if [ "$1" != "upgrade" ]; then
+	# Abort on error.
+	set -e
+
+	if [ -x /usr/sbin/update-initramfs -a \
+		-e /etc/initramfs-tools/initramfs.conf ]; then
+	    update-initramfs -u
+	fi
+
+	#DEBHELPER#
+
+	# debhelper doesn't know what timers are...
+	update_svc e2scrub_all.timer
+	update_svc e2scrub_reap.service
+
+	# Start our new services
+	if [ -d /run/systemd/system ]; then
+		deb-systemd-invoke stop e2scrub_all.timer >/dev/null || true
+	fi
+fi
+
+exit 0
+
diff --git a/scrub/Makefile.in b/scrub/Makefile.in
index 1744941..cc2e9ce 100644
--- a/scrub/Makefile.in
+++ b/scrub/Makefile.in
@@ -22,7 +22,23 @@  INSTALL_TGT	+= install-udev
 UNINSTALL_TGT	+= uninstall-udev
 endif
 
-all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES)
+ifeq ($(HAVE_CROND),yes)
+CRONTABS	= e2scrub_all.cron
+LIBPROGS	+= e2scrub_reap e2scrub_all_cron
+INSTALLDIRS_TGT	+= installdirs-crond installdirs-libprogs
+INSTALL_TGT	+= install-crond install-libprogs
+UNINSTALL_TGT	+= uninstall-crond uninstall-libprogs
+endif
+
+ifeq ($(HAVE_SYSTEMD),yes)
+SERVICE_FILES	= e2scrub@.service e2scrub_all.service e2scrub_all.timer e2scrub_fail@.service e2scrub_reap.service
+LIBPROGS	+= e2scrub_fail e2scrub_reap
+INSTALLDIRS_TGT	+= installdirs-systemd installdirs-libprogs
+INSTALL_TGT	+= install-systemd install-libprogs
+UNINSTALL_TGT	+= uninstall-systemd uninstall-libprogs
+endif
+
+all:: $(PROGS) $(MANPAGES) $(CONFFILES) $(UDEV_RULES) $(SERVICE_FILES) $(CRONTABS) $(LIBPROGS)
 
 e2scrub: $(DEP_SUBSTITUTE) e2scrub.in
 	$(E) "	SUBST $@"
@@ -34,6 +50,21 @@  e2scrub_all: e2scrub_all.in
 	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all.in $@
 	$(Q) chmod a+x $@
 
+e2scrub_fail: e2scrub_fail.in
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_fail.in $@
+	$(Q) chmod a+x $@
+
+e2scrub_reap: e2scrub_reap.in
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_reap.in $@
+	$(Q) chmod a+x $@
+
+e2scrub_all_cron: e2scrub_all_cron.in
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/e2scrub_all_cron.in $@
+	$(Q) chmod a+x $@
+
 %.8: %.8.in $(DEP_SUBSTITUTE)
 	$(E) "	SUBST $@"
 	$(Q) $(SUBSTITUTE_UPTIME) $< $@
@@ -46,10 +77,34 @@  e2scrub_all: e2scrub_all.in
 	$(E) "	SUBST $@"
 	$(Q) $(SUBSTITUTE_UPTIME) $< $@
 
+%.service: %.service.in $(DEP_SUBSTITUTE)
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $< $@
+
+%.cron: %.cron.in $(DEP_SUBSTITUTE)
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $< $@
+
+%.timer: %.timer.in $(DEP_SUBSTITUTE)
+	$(E) "	SUBST $@"
+	$(Q) $(SUBSTITUTE_UPTIME) $< $@
+
 installdirs-udev:
 	$(E) "	MKINSTALLDIRS $(UDEV_RULES_DIR)"
 	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(UDEV_RULES_DIR)
 
+installdirs-crond:
+	$(E) "	MKINSTALLDIRS $(CROND_DIR)"
+	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(CROND_DIR)
+
+installdirs-libprogs:
+	$(E) "	MKINSTALLDIRS $(pkglibdir)"
+	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(pkglibdir)
+
+installdirs-systemd:
+	$(E) "	MKINSTALLDIRS $(SYSTEMD_SYSTEM_UNIT_DIR)"
+	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)
+
 installdirs: $(INSTALLDIRS_TGT)
 	$(E) "	MKINSTALLDIRS $(root_sbindir) $(man8dir) $(root_sysconfdir)"
 	$(Q) $(MKINSTALLDIRS) $(DESTDIR)$(root_sbindir) \
@@ -61,6 +116,24 @@  install-udev:
 		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
 	done
 
+install-crond:
+	$(Q) for i in $(CRONTABS); do \
+		$(ES) "	INSTALL $(CROND_DIR)/$$i"; \
+		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(CROND_DIR)/$$i; \
+	done
+
+install-libprogs: $(LIBPROGS)
+	$(Q) for i in $(LIBPROGS); do \
+		$(ES) "	INSTALL $(pkglibdir)/$$i"; \
+		$(INSTALL_PROGRAM) $$i $(DESTDIR)$(pkglibdir)/$$i; \
+	done
+
+install-systemd: $(SERVICE_FILES)
+	$(Q) for i in $(SERVICE_FILES); do \
+		$(ES) "	INSTALL_DATA $(SYSTEMD_SYSTEM_UNIT_DIR)/$$i"; \
+		$(INSTALL_DATA) $$i $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
+	done
+
 install: $(PROGS) $(MANPAGES) $(FMANPAGES) installdirs $(INSTALL_TGT)
 	$(Q) for i in $(PROGS); do \
 		$(ES) "	INSTALL $(root_sbindir)/$$i"; \
@@ -83,6 +156,21 @@  uninstall-udev:
 		$(RM) -f $(DESTDIR)$(UDEV_RULES_DIR)/96-$$i; \
 	done
 
+uninstall-crond:
+	for i in $(CRONTABS); do \
+		$(RM) -f $(DESTDIR)$(CROND_DIR)/$$i; \
+	done
+
+uninstall-libprogs:
+	for i in $(LIBPROGS); do \
+		$(RM) -f $(DESTDIR)$(pkglibdir)/$$i; \
+	done
+
+uninstall-systemd:
+	for i in $(SERVICE_FILES); do \
+		$(RM) -f $(DESTDIR)$(SYSTEMD_SYSTEM_UNIT_DIR)/$$i; \
+	done
+
 uninstall: $(UNINSTALL_TGT)
 	for i in $(PROGS); do \
 		$(RM) -f $(DESTDIR)$(root_sbindir)/$$i; \
diff --git a/scrub/e2scrub.in b/scrub/e2scrub.in
index f77ec6f..a19b6bb 100644
--- a/scrub/e2scrub.in
+++ b/scrub/e2scrub.in
@@ -44,12 +44,34 @@  print_version() {
 	echo "e2scrub @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
 }
 
+exitcode() {
+	ret="$1"
+
+	# If we're being run as a service, the return code must fit the LSB
+	# init script action error guidelines, which is to say that we
+	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
+	# section 22.2) and hope the admin will scan the log for what
+	# actually happened.
+
+	# We have to sleep 2 seconds here because journald uses the pid to
+	# connect our log messages to the systemd service.  This is critical
+	# for capturing all the log messages if the scrub fails, because the
+	# fail service uses the service name to gather log messages for the
+	# error report.
+	if [ -n "${SERVICE_MODE}" ]; then
+		test "${ret}" -ne 0 && ret=1
+		sleep 2
+	fi
+
+	exit "${ret}"
+}
+
 while getopts "rtV" opt; do
 	case "${opt}" in
 	"r") reap=1;;
 	"t") fstrim=1;;
-	"V") print_version; exit 0;;
-	*) print_help; exit 2;;
+	"V") print_version; exitcode 0;;
+	*) print_help; exitcode 2;;
 	esac
 done
 shift "$((OPTIND - 1))"
@@ -57,18 +79,18 @@  shift "$((OPTIND - 1))"
 dev="$1"
 if [ -z "${dev}" ]; then
 	print_help
-	exit 1
+	exitcode 1
 elif [ ! -b "${dev}" ]; then
 	echo "${dev}: Not a block device?"
 	print_help
-	exit 16
+	exitcode 16
 fi
 
 # Make sure this is an LVM device we can snapshot
 eval $(lvs --nameprefixes -o name,vgname --noheadings "${dev}")
 if [ -z "${LVM2_VG_NAME}" ] || [ -z "${LVM2_LV_NAME}" ]; then
 	echo "${dev}: Not a LVM device."
-	exit 16
+	exitcode 16
 fi
 start_time="$(date +'%Y%m%d%H%M%S')"
 snap="${LVM2_LV_NAME}.e2scrub"
@@ -80,7 +102,7 @@  ext[234])
 	;;
 *)
 	echo "${dev}: Filesystem of type ${fstype} not supported."
-	exit 16
+	exitcode 16
 	;;
 esac
 
@@ -128,7 +150,7 @@  if [ "${reap}" -gt 0 ]; then
 fi
 if ! setup; then
 	echo "Snapshot of ${dev} FAILED, will not check!"
-	exit 1
+	exitcode 1
 fi
 
 # Check and react
@@ -167,4 +189,4 @@  else
 	fi
 fi
 
-exit "${ret}"
+exitcode "${ret}"
diff --git a/scrub/e2scrub@.service.in b/scrub/e2scrub@.service.in
new file mode 100644
index 0000000..496f894
--- /dev/null
+++ b/scrub/e2scrub@.service.in
@@ -0,0 +1,20 @@ 
+[Unit]
+Description=Online ext4 Metadata Check for %I
+OnFailure=e2scrub_fail@%i.service
+Documentation=man:e2scrub(8)
+
+[Service]
+Type=oneshot
+WorkingDirectory=/
+PrivateNetwork=true
+ProtectSystem=true
+ProtectHome=read-only
+PrivateTmp=yes
+AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
+NoNewPrivileges=yes
+User=root
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+Environment=SERVICE_MODE=1
+ExecStart=@root_sbindir@/e2scrub -t %I
+SyslogIdentifier=%N
diff --git a/scrub/e2scrub_all.cron.in b/scrub/e2scrub_all.cron.in
new file mode 100644
index 0000000..0cb5f1f
--- /dev/null
+++ b/scrub/e2scrub_all.cron.in
@@ -0,0 +1,2 @@ 
+30 3 * * 0 root test -e /run/systemd/system || @pkglibdir@/e2scrub_all_cron
+10 3 * * * root test -e /run/systemd/system || @pkglibdir@/e2scrub_reap
diff --git a/scrub/e2scrub_all.in b/scrub/e2scrub_all.in
index 453981b..fbf2e95 100644
--- a/scrub/e2scrub_all.in
+++ b/scrub/e2scrub_all.in
@@ -30,10 +30,32 @@  print_version() {
 	echo "e2scrub_all @E2FSPROGS_VERSION@ (@E2FSPROGS_DATE@)"
 }
 
+exitcode() {
+	ret="$1"
+
+	# If we're being run as a service, the return code must fit the LSB
+	# init script action error guidelines, which is to say that we
+	# compress all errors to 1 ("generic or unspecified error", LSB 5.0
+	# section 22.2) and hope the admin will scan the log for what
+	# actually happened.
+
+	# We have to sleep 2 seconds here because journald uses the pid to
+	# connect our log messages to the systemd service.  This is critical
+	# for capturing all the log messages if the scrub fails, because the
+	# fail service uses the service name to gather log messages for the
+	# error report.
+	if [ -n "${SERVICE_MODE}" ]; then
+		test "${ret}" -ne 0 && ret=1
+		sleep 2
+	fi
+
+	exit "${ret}"
+}
+
 while getopts "V" opt; do
 	case "${opt}" in
-	"V") print_version; exit 0;;
-	*) print_help; exit 2;;
+	"V") print_version; exitcode 0;;
+	*) print_help; exitcode 2;;
 	esac
 done
 shift "$((OPTIND - 1))"
@@ -49,7 +71,15 @@  lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role
 	# Skip non-ext[234]
 	blkid -p -n "${types}" "${dev}" > /dev/null 2>&1 || continue
 
-	${DBG} "@root_sbindir@/e2scrub" "${dev}"
+	if type systemctl > /dev/null 2>&1; then
+		${DBG} systemctl start "e2scrub@${dev}" 2> /dev/null
+		res=$?
+		if [ "${res}" -ne 0 ] && [ "${res}" -ne 1 ]; then
+			${DBG} "@root_sbindir@/e2scrub" "${dev}"
+		fi
+	else
+		${DBG} "@root_sbindir@/e2scrub" "${dev}"
+	fi
 done
 
-exit 0
+exitcode 0
diff --git a/scrub/e2scrub_all.service.in b/scrub/e2scrub_all.service.in
new file mode 100644
index 0000000..bc05184
--- /dev/null
+++ b/scrub/e2scrub_all.service.in
@@ -0,0 +1,10 @@ 
+[Unit]
+Description=Online ext4 Metadata Check for All Filesystems
+ConditionACPower=true
+Documentation=man:e2scrub_all(8)
+
+[Service]
+Type=oneshot
+Environment=SERVICE_MODE=1
+ExecStart=@root_sbindir@/e2scrub_all
+SyslogIdentifier=e2scrub_all
diff --git a/scrub/e2scrub_all.timer.in b/scrub/e2scrub_all.timer.in
new file mode 100644
index 0000000..3d558bb
--- /dev/null
+++ b/scrub/e2scrub_all.timer.in
@@ -0,0 +1,11 @@ 
+[Unit]
+Description=Periodic ext4 Online Metadata Check for All Filesystems
+
+[Timer]
+# Run on Sunday at 3:10am, to avoid running afoul of DST changes
+OnCalendar=Sun *-*-* 03:10:00
+RandomizedDelaySec=60
+Persistent=true
+
+[Install]
+WantedBy=timers.target
diff --git a/scrub/e2scrub_all_cron.in b/scrub/e2scrub_all_cron.in
new file mode 100644
index 0000000..f9cff87
--- /dev/null
+++ b/scrub/e2scrub_all_cron.in
@@ -0,0 +1,68 @@ 
+#!/bin/bash
+
+#  Copyright (C) 2018 Oracle.  All Rights Reserved.
+#
+#  Author: Darrick J. Wong <darrick.wong@oracle.com>
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it would be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write the Free Software Foundation,
+#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+
+# Run e2scrub_all from a cronjob if we don't have systemd and we're not
+# running on AC power.
+
+on_ac_power() {
+	local any_known=no
+
+	# try sysfs power class first
+	if [ -d /sys/class/power_supply ]; then
+		for psu in /sys/class/power_supply/*; do
+			if [ -r "$psu/type" ]; then
+				type=$(cat "$psu/type")
+
+				# ignore batteries
+				[ "$type" = "Battery" ] && continue
+
+				online=$(cat "$psu/online")
+
+				[ "$online" = 1 ] && return 0
+				[ "$online" = 0 ] && any_known=yes
+			fi
+		done
+
+		[ "$any_known" = "yes" ] && return 1
+	fi
+
+	# else fall back to AC adapters in /proc
+	if [ -d /proc/acpi/ac_adapter ]; then
+		for ac in /proc/acpi/ac_adapter/*; do
+			if [ -r "$ac/state" ]; then
+				grep -q on-line "$ac/state" && return 0
+				grep -q off-line "$ac/state" && any_known=yes
+			elif [ -r "$ac/status" ]; then
+				grep -q on-line "$ac/status" && return 0
+				grep -q off-line "$ac/status" && any_known=yes
+			fi
+		done
+
+		[ "$any_known" = "yes" ] && return 1
+	fi
+
+	# Can't tell, just assume we're on AC.
+	return 0
+}
+
+test -e /run/systemd/system && exit 0
+on_ac_power || exit 0
+
+exec @root_sbindir@/e2scrub_all
diff --git a/scrub/e2scrub_fail.in b/scrub/e2scrub_fail.in
new file mode 100644
index 0000000..f27197a
--- /dev/null
+++ b/scrub/e2scrub_fail.in
@@ -0,0 +1,25 @@ 
+#!/bin/bash
+
+# Email logs of failed e2scrub unit runs when the systemd service fails.
+
+recipient="$1"
+test -z "${recipient}" && exit 0
+device="$2"
+test -z "${device}" && exit 0
+hostname="$(hostname -f 2>/dev/null)"
+test -z "${hostname}" && hostname="${HOSTNAME}"
+if ! type sendmail > /dev/null 2>&1; then
+	echo "$0: sendmail program not found."
+	exit 1
+fi
+
+(cat << ENDL
+To: $1
+From: <e2scrub@${hostname}>
+Subject: e2scrub failure on ${device}
+
+So sorry, the automatic e2scrub of ${device} on ${hostname} failed.
+
+A log of what happened follows:
+ENDL
+systemctl status --full --lines 4294967295 "e2scrub@${device}") | sendmail -t -i
diff --git a/scrub/e2scrub_fail@.service.in b/scrub/e2scrub_fail@.service.in
new file mode 100644
index 0000000..df87949
--- /dev/null
+++ b/scrub/e2scrub_fail@.service.in
@@ -0,0 +1,10 @@ 
+[Unit]
+Description=Online ext4 Metadata Check Failure Reporting for %I
+
+[Service]
+Type=oneshot
+Environment=EMAIL_ADDR=root
+ExecStart=@pkglibdir@/e2scrub_fail "${EMAIL_ADDR}" %I
+User=mail
+Group=mail
+SupplementaryGroups=systemd-journal
diff --git a/scrub/e2scrub_reap.in b/scrub/e2scrub_reap.in
new file mode 100644
index 0000000..0fde0e6
--- /dev/null
+++ b/scrub/e2scrub_reap.in
@@ -0,0 +1,48 @@ 
+#!/bin/bash
+
+#  Copyright (C) 2018 Oracle.  All Rights Reserved.
+#
+#  Author: Darrick J. Wong <darrick.wong@oracle.com>
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it would be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write the Free Software Foundation,
+#  Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+
+# Clean up leftover e2scrub snapshots
+
+# Find any ${lvname}.e2scrub snapshot and delete it
+lvs -o vg_name,lv_name,lv_role --noheadings 2> /dev/null | while read vg lv role extra; do
+	# Parse error?
+	test -n "${extra}" || continue
+	# Is this really a snapshot?
+	echo "${role}" | grep -q "snapshot" || continue
+	# An e2scrub snapshot?
+	echo "${lv}" | grep -q ".e2scrub$" || continue
+
+	# Check that the e2scrub snap still exists after waiting a brief period.
+	# This is how try to avoid deleting a snap from underneath a running
+	# scrub.
+	dev="/dev/${vg}/${lv}"
+	test -e "${dev}" || continue
+	sleep 2
+	test -e "${dev}" || continue
+
+	# Remove and wait for removal to succeed.
+	lvremove -f "${vg}/${lv}" 3>&-
+	while [ -e "${dev}" ] && [ "$?" -eq "5" ]; do
+		sleep 0.5
+		lvremove -f "${vg}/${lv}" 3>&-
+	done
+done
+
+exitcode 0
diff --git a/scrub/e2scrub_reap.service.in b/scrub/e2scrub_reap.service.in
new file mode 100644
index 0000000..38eefc4
--- /dev/null
+++ b/scrub/e2scrub_reap.service.in
@@ -0,0 +1,21 @@ 
+[Unit]
+Description=Remove Stale Online ext4 Metadata Check Snapshots
+
+[Service]
+Type=oneshot
+WorkingDirectory=/
+PrivateNetwork=true
+ProtectSystem=true
+ProtectHome=read-only
+PrivateTmp=yes
+AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO
+NoNewPrivileges=yes
+User=root
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+ExecStart=@pkglibdir@/e2scrub_reap
+SyslogIdentifier=%N
+RemainAfterExit=no
+
+[Install]
+WantedBy=default.target
diff --git a/util/subst.conf.in b/util/subst.conf.in
index 6bf658d..0da4554 100644
--- a/util/subst.conf.in
+++ b/util/subst.conf.in
@@ -21,3 +21,6 @@  JDEV
 TDB_MAN_COMMENT		@TDB_MAN_COMMENT@
 root_sbindir		@root_sbindir@
 root_bindir		@root_bindir@
+libdir			@libdir@
+$exec_prefix		@exec_prefix@
+pkglibdir		@libdir@/e2fsprogs