lorax: Move default tmp dir to /var/tmp/lorax

If systemd's tmpfiles.d timer is executed while lorax is running it will
remove any files and directories older than 30 days. This is what has
been causing the occasional error where /proc/ would seem to vanish
during the install.

Upstream has proposed this solution, https://github.com/systemd/systemd/pull/11482
but until that is released we need a work-around to protect the lorax
files.

This commit does several things:

* Move the default tmpdir from /var/tmp/ to /var/tmp/lorax/
* Add a lorax.conf tmpfiles.d file that prevents systemd-tmpfiles from
  removing anything under /var/tmp/lorax/
* Add an exit handler to lorax so that temporary directories are removed on
  exit or on a python traceback.
* Use flock to lock access to the tempdir while lorax is running.
* Remove any unlocked tempdirs named /var/tmp/lorax/lorax.* at startup

Note that the exit handler will not remove the tempdir if lorax is
killed with a signal -- those are being caught by dnf and prevent the
exit handler from running.

systemd-tmpfiles cannot clean up the tempdirs at boot time because they
contain files labeled as shadow_t, so we have to remove those when lorax
runs. It uses the flock to prevent removing any directories created by
parallel instances of lorax and only removes ones that are unlocked.
Worst case they will be around until the first run of lorax after a
reboot.

If you want to keep the working directory around for debugging purposes
use --workdir /var/tmp/lorax/my-workdir and it won't be removed by
lorax.

Resolves: rhbz#1668520
This commit is contained in:
Brian C. Lane 2019-01-18 12:06:18 -08:00
parent fffb03d5c3
commit 81ce55a1e5
4 changed files with 64 additions and 3 deletions

View File

@ -117,6 +117,7 @@ rm -f $RPM_BUILD_ROOT/%{_tmpfilesdir}/lorax-composer.conf
%{_datadir}/lorax/* %{_datadir}/lorax/*
%exclude %{_datadir}/lorax/composer %exclude %{_datadir}/lorax/composer
%{_mandir}/man1/*.1* %{_mandir}/man1/*.1*
%{_tmpfilesdir}/lorax.conf
%changelog %changelog
* Wed Aug 15 2018 Brian C. Lane <bcl@redhat.com> 19.7.19-1 * Wed Aug 15 2018 Brian C. Lane <bcl@redhat.com> 19.7.19-1

View File

@ -10,7 +10,8 @@ data_files = [("/etc/lorax", ["etc/lorax.conf"]),
("/etc/lorax", ["etc/composer.conf"]), ("/etc/lorax", ["etc/composer.conf"]),
("/usr/lib/systemd/system", ["systemd/lorax-composer.service", ("/usr/lib/systemd/system", ["systemd/lorax-composer.service",
"systemd/lorax-composer.socket"]), "systemd/lorax-composer.socket"]),
("/usr/lib/tmpfiles.d/", ["systemd/lorax-composer.conf"])] ("/usr/lib/tmpfiles.d/", ["systemd/lorax-composer.conf",
"systemd/lorax.conf"])]
# shared files # shared files
for root, dnames, fnames in os.walk("share"): for root, dnames, fnames in os.walk("share"):

View File

@ -30,6 +30,9 @@ pylorax_log = logging.getLogger("pylorax")
yum_log = logging.getLogger("yum") yum_log = logging.getLogger("yum")
import atexit
import fcntl
from glob import glob
import sys import sys
import os import os
import shutil import shutil
@ -46,6 +49,42 @@ from pylorax import DRACUT_DEFAULT
VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), pylorax.vernum) VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), pylorax.vernum)
def exit_handler(tempdir):
"""Handle cleanup of tmpdir, if it still exists
"""
if not tempdir:
return
if os.path.exists(tempdir):
log.info("Cleaning up tempdir - %s", tempdir)
shutil.rmtree(tempdir)
def remove_tempdirs():
"""Delete all unlocked tempdirs under tempfile.gettempdir
When lorax crashes it can leave behind tempdirs, which cannot be cleaned up by
systemd-tmpfiles (SELinux restricts a complete cleanup).
So we lock them while in use and cleanup all the ones that are not locked
when lorax starts.
"""
for d in glob(os.path.join(tempfile.gettempdir(), "lorax.*")):
if not os.path.isdir(d):
continue
try:
dir_fd = os.open(d, os.O_RDONLY|os.O_DIRECTORY)
fcntl.flock(dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError:
# lock failed, skip this directory
os.close(dir_fd)
continue
# Lock succeeded, remove the directory
log.info("Removing old tempdir %s", d)
shutil.rmtree(d)
os.close(dir_fd)
def setup_logging(opts): def setup_logging(opts):
# Setup logging to console and to logfile # Setup logging to console and to logfile
log.setLevel(logging.DEBUG) log.setLevel(logging.DEBUG)
@ -134,7 +173,7 @@ def main(args):
action="store_false", default=True, dest="doupgrade") action="store_false", default=True, dest="doupgrade")
optional.add_option("--logfile", default="./lorax.log", optional.add_option("--logfile", default="./lorax.log",
help="Path to logfile") help="Path to logfile")
optional.add_option("--tmp", default="/var/tmp", optional.add_option("--tmp", default="/var/tmp/lorax",
help="Top level temporary directory" ) help="Top level temporary directory" )
optional.add_option("--add-template", dest="add_templates", optional.add_option("--add-template", dest="add_templates",
action="append", help="Additional template for runtime image", action="append", help="Additional template for runtime image",
@ -196,10 +235,20 @@ def main(args):
setup_logging(opts) setup_logging(opts)
log.info("Lorax %s", pylorax.vernum) log.info("Lorax %s", pylorax.vernum)
if not os.path.exists(opts.tmp):
os.makedirs(opts.tmp)
tempfile.tempdir = opts.tmp tempfile.tempdir = opts.tmp
# Remove any orphaned lorax tempdirs
remove_tempdirs()
# create the temporary directory for lorax # create the temporary directory for lorax
tempdir = tempfile.mkdtemp(prefix="lorax.", dir=tempfile.gettempdir()) tempdir = tempfile.mkdtemp(prefix="lorax.")
# register an exit handler to cleanup the temporary directory
atexit.register(exit_handler, tempdir)
# create the yumbase object # create the yumbase object
installtree = os.path.join(tempdir, "installtree") installtree = os.path.join(tempdir, "installtree")
@ -207,6 +256,10 @@ def main(args):
yumtempdir = os.path.join(tempdir, "yum") yumtempdir = os.path.join(tempdir, "yum")
os.mkdir(yumtempdir) os.mkdir(yumtempdir)
# Obtain an exclusive lock on the tempdir
dir_fd = os.open(tempdir, os.O_RDONLY|os.O_DIRECTORY)
fcntl.flock(dir_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
yb = get_yum_base_object(installtree, opts.source, opts.mirrorlist, yb = get_yum_base_object(installtree, opts.source, opts.mirrorlist,
opts.repo_files, opts.repo_files,
yumtempdir, opts.proxy, opts.excludepkgs, yumtempdir, opts.proxy, opts.excludepkgs,
@ -246,6 +299,9 @@ def main(args):
remove_temp=True, remove_temp=True,
user_dracut_args=opts.dracut_args) user_dracut_args=opts.dracut_args)
# Release the lock on the tempdir
os.close(dir_fd)
def get_yum_base_object(installroot, repositories, mirrorlists=None, repo_files=None, def get_yum_base_object(installroot, repositories, mirrorlists=None, repo_files=None,
tempdir="/var/tmp", proxy=None, excludepkgs=None, tempdir="/var/tmp", proxy=None, excludepkgs=None,

3
systemd/lorax.conf Normal file
View File

@ -0,0 +1,3 @@
# Prevent systemd from removing installtree files
# This should eventually be fixed by - https://github.com/systemd/systemd/pull/11482
x /var/tmp/lorax 750 root root