Skip to content

Commit

Permalink
Merge pull request #602 from nix-community/shell-wrapper-race-condition
Browse files Browse the repository at this point in the history
fix: shell-wrapper sometimes can't launch wrapped shell due to race condition
  • Loading branch information
K900 authored Dec 10, 2024
2 parents 49b4afd + d75f479 commit dee4425
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 2 deletions.
14 changes: 14 additions & 0 deletions tests/slow-activation/delay-activation.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{ pkgs, lib, ... }:

{
imports = [
<nixos-wsl/modules>
];

wsl.enable = true;

system.activationScripts."00-delay" = ''
echo "Delaying activation for 15 seconds..."
sleep 15s
'';
}
22 changes: 22 additions & 0 deletions tests/slow-activation/slow-activation.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
BeforeAll {
. $PSScriptRoot/../lib/lib.ps1
}

Describe "Slow Activation Script" {
BeforeAll {
$distro = [Distro]::new()
}

It "should not cause starting a shell to fail" {
$distro.InstallConfig("$PSScriptRoot/delay-activation.nix", "boot")
$distro.Shutdown()

$distro.Launch("echo 'TEST'") | Select-Object -Last 1 | Tee-Object -Variable output
$output | Should -BeExactly "TEST"
$LASTEXITCODE | Should -Be 0
}

AfterAll {
$distro.Uninstall()
}
}
2 changes: 1 addition & 1 deletion utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ rust-version = "1.69.0"

[dependencies]
anyhow = { version = "1.0.82", features = ["backtrace"] }
nix = { version = "0.29.0", features = ["mount", "process"] }
nix = { version = "0.29.0", features = ["mount", "process", "user", "inotify"] }
log = "0.4.21"
kernlog = "0.3.1"
systemd-journal-logger = "2.1.1"
Expand Down
34 changes: 33 additions & 1 deletion utils/src/shell_wrapper.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use anyhow::{anyhow, Context};
use log::{error, info, warn, LevelFilter};
use nix::libc::{sigaction, PT_NULL, SIGCHLD, SIG_IGN};
use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
use std::mem::MaybeUninit;
use std::os::unix::process::CommandExt;
use std::path::Path;
use std::process::Command;
use std::{env, fs::read_link};
use systemd_journal_logger::JournalLog;
Expand All @@ -17,6 +19,28 @@ fn real_main() -> anyhow::Result<()> {
// Therefore we dereference our symlink to get whatever it was originally.
let shell = read_link(exe_dir.join("shell")).context("when locating the wrapped shell")?;

if shell.starts_with("/run/current-system/sw/bin/")
&& !Path::new("/run/current-system").exists()
{
let inotify = Inotify::init(InitFlags::empty()).context("When initializing inotify")?;

// Watch changes in /run to re-check if the activation script has finished
let wd = inotify.add_watch("/run", AddWatchFlags::IN_CREATE).unwrap();

let mut warning = false;

// Check if the activation script has finished by now
while !Path::new("/run/current-system").exists() {
if (!warning) {
warning = true;
warn!("Activation script has not finished! Waiting for /run/current-system/sw/bin to exist");
}
let events = inotify
.read_events()
.context("When reading inotify events")?;
}
}

// Set the SHELL environment variable to the wrapped shell instead of the wrapper
let shell_env = env::var_os("SHELL");
if shell_env == Some(exe.into()) {
Expand Down Expand Up @@ -112,7 +136,15 @@ fn main() {
.context("When installing journal logger")
})
{
warn!("Error while setting up journal logger: {:?}", err);
if nix::unistd::geteuid().is_root() {
// Journal isn't available during early boot. Try to use kmsg instead
if let Err(err) = kernlog::init().context("When initializing kernel logger") {
warn!("Error while setting up kernel logger: {:?}", err);
}
} else {
// Users can't access the kernel log
warn!("Error while setting up journal logger: {:?}", err);
}
}

log::set_max_level(LevelFilter::Info);
Expand Down

0 comments on commit dee4425

Please sign in to comment.