Skip to content

Commit

Permalink
Introduce flock_or_die() and use it in various places
Browse files Browse the repository at this point in the history
Let's avoid weird error cases caused by two instances of mkosi trying
to currently do stuff with the same output by taking a BSD lock when
trying to do something with the output.
  • Loading branch information
DaanDeMeyer committed Mar 14, 2024
1 parent 8a1342e commit 480673f
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 6 deletions.
10 changes: 8 additions & 2 deletions mkosi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
from mkosi.util import (
flatten,
flock,
flock_or_die,
format_rlimit,
make_executable,
one_zero,
Expand Down Expand Up @@ -3633,7 +3634,7 @@ def run_shell(args: Args, config: Config) -> None:
if config.ephemeral:
fname = stack.enter_context(copy_ephemeral(config, config.output_dir_or_cwd() / config.output))
else:
fname = config.output_dir_or_cwd() / config.output
fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output))

if config.output_format == OutputFormat.disk and args.verb == Verb.boot:
run(
Expand Down Expand Up @@ -3955,7 +3956,12 @@ def run_clean(args: Args, config: Config, *, resources: Path) -> None:
remove_package_cache = args.force > 2

if outputs := list(config.output_dir_or_cwd().glob(f"{config.output}*")):
with complete_step(f"Removing output files of {config.name()} image…"):
with (
complete_step(f"Removing output files of {config.name()} image…"),
flock_or_die(config.output_dir_or_cwd() / config.output)
if (config.output_dir_or_cwd() / config.output).exists()
else contextlib.nullcontext()
):
rmtree(*outputs)

if remove_build_cache:
Expand Down
7 changes: 4 additions & 3 deletions mkosi/qemu.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from mkosi.tree import copy_tree, rmtree
from mkosi.types import PathString
from mkosi.user import INVOKING_USER, become_root
from mkosi.util import StrEnum, flatten
from mkosi.util import StrEnum, flatten, flock, flock_or_die
from mkosi.versioncomp import GenericVersion

QEMU_KVM_DEVICE_VERSION = GenericVersion("9.0")
Expand Down Expand Up @@ -426,7 +426,8 @@ def copy() -> None:
sandbox=config.sandbox,
)

fork_and_wait(copy)
with flock(src):
fork_and_wait(copy)
yield tmp
finally:
def rm() -> None:
Expand Down Expand Up @@ -705,7 +706,7 @@ def run_qemu(args: Args, config: Config) -> None:
copy_ephemeral(config, config.output_dir_or_cwd() / config.output_with_compression)
)
else:
fname = config.output_dir_or_cwd() / config.output_with_compression
fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output_with_compression))

if config.output_format == OutputFormat.disk and config.runtime_size:
run(
Expand Down
15 changes: 15 additions & 0 deletions mkosi/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import contextlib
import copy
import enum
import errno
import fcntl
import functools
import importlib
Expand All @@ -20,6 +21,7 @@
from types import ModuleType
from typing import Any, Callable, Optional, TypeVar, no_type_check

from mkosi.log import die
from mkosi.types import PathString

T = TypeVar("T")
Expand Down Expand Up @@ -142,6 +144,19 @@ def flock(path: Path, flags: int = fcntl.LOCK_EX) -> Iterator[int]:
os.close(fd)


@contextlib.contextmanager
def flock_or_die(path: Path) -> Iterator[Path]:
try:
with flock(path, fcntl.LOCK_EX|fcntl.LOCK_NB):
yield path
except OSError as e:
if e.errno != errno.EWOULDBLOCK:
raise e

die(f"Cannot lock {path} as it is locked by another process",
hint="Maybe another mkosi process is still using it?")


@contextlib.contextmanager
def scopedenv(env: Mapping[str, Any]) -> Iterator[None]:
old = copy.deepcopy(os.environ)
Expand Down
3 changes: 2 additions & 1 deletion mkosi/vmspawn.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from mkosi.run import run
from mkosi.types import PathString
from mkosi.util import flock_or_die


def run_vmspawn(args: Args, config: Config) -> None:
Expand Down Expand Up @@ -79,7 +80,7 @@ def run_vmspawn(args: Args, config: Config) -> None:
if config.ephemeral:
fname = stack.enter_context(copy_ephemeral(config, config.output_dir_or_cwd() / config.output))
else:
fname = config.output_dir_or_cwd() / config.output
fname = stack.enter_context(flock_or_die(config.output_dir_or_cwd() / config.output))

if config.output_format == OutputFormat.disk and config.runtime_size:
run(
Expand Down

0 comments on commit 480673f

Please sign in to comment.