@@ -13,6 +13,7 @@ use std::time::{Duration, Instant};
1313use crate :: { embedded_runtime, ffi, nft_ruleset, procguard, rootfs} ;
1414
1515pub const VM_RUNTIME_DIR_ENV : & str = "OPENSHELL_VM_RUNTIME_DIR" ;
16+ const KRUN_INIT_PID1_ENV : & str = "KRUN_INIT_PID1=1" ;
1617
1718/// PID of the VM worker process (libkrun fork or QEMU). Zero when not running.
1819/// Used by the SIGTERM/SIGINT handler to forward signals to the VM.
@@ -833,15 +834,7 @@ fn run_libkrun_vm(config: &VmLaunchConfig) -> Result<(), String> {
833834
834835 vm. set_console_output ( & config. console_output ) ?;
835836
836- let env = if config. env . is_empty ( ) {
837- vec ! [
838- "HOME=/root" . to_string( ) ,
839- "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" . to_string( ) ,
840- "TERM=xterm" . to_string( ) ,
841- ]
842- } else {
843- config. env . clone ( )
844- } ;
837+ let env = libkrun_guest_env ( config) ;
845838 vm. set_exec ( & config. exec_path , & config. args , & env) ?;
846839
847840 let pid = unsafe { libc:: fork ( ) } ;
@@ -889,6 +882,26 @@ fn run_libkrun_vm(config: &VmLaunchConfig) -> Result<(), String> {
889882 }
890883}
891884
885+ fn libkrun_guest_env ( config : & VmLaunchConfig ) -> Vec < String > {
886+ let mut env = if config. env . is_empty ( ) {
887+ vec ! [
888+ "HOME=/root" . to_string( ) ,
889+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" . to_string( ) ,
890+ "TERM=xterm" . to_string( ) ,
891+ ]
892+ } else {
893+ config. env . clone ( )
894+ } ;
895+
896+ // libkrun normally keeps /init.krun as PID 1 and forks the configured
897+ // executable. OpenShell's guest init is itself an init process and ends
898+ // by exec'ing the supervisor, so ask libkrun to exec it directly. Keep
899+ // this driver-owned setting authoritative over sandbox image/user env.
900+ env. retain ( |value| !value. starts_with ( "KRUN_INIT_PID1=" ) ) ;
901+ env. push ( KRUN_INIT_PID1_ENV . to_string ( ) ) ;
902+ env
903+ }
904+
892905pub fn validate_runtime_dir ( dir : & Path ) -> Result < ( ) , String > {
893906 if !dir. is_dir ( ) {
894907 return Err ( format ! (
@@ -1438,6 +1451,44 @@ mod tests {
14381451 assert ! ( env. contains( & "GPU_ENABLED=true" . to_string( ) ) ) ;
14391452 }
14401453
1454+ #[ test]
1455+ fn libkrun_guest_env_runs_guest_init_as_pid_one ( ) {
1456+ let env = libkrun_guest_env ( & qemu_config ( ) ) ;
1457+
1458+ assert ! ( env. contains( & "OPENSHELL_ENDPOINT=http://10.0.128.1:8080" . to_string( ) ) ) ;
1459+ assert ! ( env. contains( & KRUN_INIT_PID1_ENV . to_string( ) ) ) ;
1460+ }
1461+
1462+ #[ test]
1463+ fn libkrun_guest_env_overrides_caller_pid_one_setting ( ) {
1464+ let mut config = qemu_config ( ) ;
1465+ config. env . extend ( [
1466+ "KRUN_INIT_PID1=0" . to_string ( ) ,
1467+ "KRUN_INIT_PID1=unexpected" . to_string ( ) ,
1468+ ] ) ;
1469+
1470+ let env = libkrun_guest_env ( & config) ;
1471+ let pid_one_settings = env
1472+ . iter ( )
1473+ . filter ( |value| value. starts_with ( "KRUN_INIT_PID1=" ) )
1474+ . collect :: < Vec < _ > > ( ) ;
1475+
1476+ assert_eq ! ( pid_one_settings. len( ) , 1 ) ;
1477+ assert_eq ! ( pid_one_settings[ 0 ] , KRUN_INIT_PID1_ENV ) ;
1478+ }
1479+
1480+ #[ test]
1481+ fn libkrun_guest_env_keeps_defaults_when_no_env_is_configured ( ) {
1482+ let mut config = qemu_config ( ) ;
1483+ config. env . clear ( ) ;
1484+
1485+ let env = libkrun_guest_env ( & config) ;
1486+
1487+ assert ! ( env. contains( & "HOME=/root" . to_string( ) ) ) ;
1488+ assert ! ( env. contains( & "TERM=xterm" . to_string( ) ) ) ;
1489+ assert ! ( env. contains( & KRUN_INIT_PID1_ENV . to_string( ) ) ) ;
1490+ }
1491+
14411492 #[ test]
14421493 fn kernel_cmdline_keeps_guest_init_metadata_out_of_proc_cmdline ( ) {
14431494 let cmdline = build_kernel_cmdline ( & qemu_config ( ) ) ;
0 commit comments