Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions litebox_common_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ pub struct Winsize {

pub const TCGETS: u32 = 0x5401;
pub const TCSETS: u32 = 0x5402;
pub const TIOCGPGRP: u32 = 0x540F;
pub const TIOCSPGRP: u32 = 0x5410;
pub const TIOCGWINSZ: u32 = 0x5413;
pub const FIONBIO: u32 = 0x5421;
pub const FIOCLEX: u32 = 0x5451;
Expand All @@ -601,6 +603,10 @@ pub enum IoctlArg<Platform: litebox::platform::RawPointerProvider> {
TCGETS(Platform::RawMutPointer<Termios>),
/// Set the current serial port settings.
TCSETS(Platform::RawConstPointer<Termios>),
/// Get the process group ID of the foreground process group on the terminal.
TIOCGPGRP(Platform::RawMutPointer<i32>),
/// Set the foreground process group ID on the terminal.
TIOCSPGRP(Platform::RawConstPointer<i32>),
/// Get window size.
TIOCGWINSZ(Platform::RawMutPointer<Winsize>),
/// Obtain device unit number, which can be used to generate
Expand Down Expand Up @@ -2217,6 +2223,14 @@ pub enum SyscallRequest<Platform: litebox::platform::RawPointerProvider> {
},
Getpid,
Getppid,
Getpgrp,
Setpgid {
pid: i32,
pgid: i32,
},
Getpgid {
pid: i32,
},
Getuid,
Geteuid,
Getgid,
Expand Down Expand Up @@ -2408,6 +2422,8 @@ impl<Platform: litebox::platform::RawPointerProvider> SyscallRequest<Platform> {
match cmd {
TCGETS => IoctlArg::TCGETS(ctx.sys_req_ptr(2)),
TCSETS => IoctlArg::TCSETS(ctx.sys_req_ptr(2)),
TIOCGPGRP => IoctlArg::TIOCGPGRP(ctx.sys_req_ptr(2)),
TIOCSPGRP => IoctlArg::TIOCSPGRP(ctx.sys_req_ptr(2)),
TIOCGWINSZ => IoctlArg::TIOCGWINSZ(ctx.sys_req_ptr(2)),
TIOCGPTN => IoctlArg::TIOCGPTN(ctx.sys_req_ptr(2)),
FIONBIO => IoctlArg::FIONBIO(ctx.sys_req_ptr(2)),
Expand Down Expand Up @@ -2587,6 +2603,9 @@ impl<Platform: litebox::platform::RawPointerProvider> SyscallRequest<Platform> {
Sysno::prlimit64 => sys_req!(Prlimit { pid, resource:?, new_limit:*, old_limit:* }),
Sysno::getpid => SyscallRequest::Getpid,
Sysno::getppid => SyscallRequest::Getppid,
Sysno::getpgrp => SyscallRequest::Getpgrp,
Sysno::setpgid => sys_req!(Setpgid { pid, pgid }),
Sysno::getpgid => sys_req!(Getpgid { pid }),
Sysno::getuid => SyscallRequest::Getuid,
Sysno::getgid => SyscallRequest::Getgid,
Sysno::geteuid => SyscallRequest::Geteuid,
Expand Down
17 changes: 17 additions & 0 deletions litebox_shim_linux/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ impl LinuxShimBuilder {
boot_time: self.platform.now(),
load_filter: self.load_filter,
next_thread_id: 2.into(), // start from 2, as 1 is used by the main thread
fg_pgrp: core::sync::atomic::AtomicI32::new(1),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In LiteBox, there is no concept of forground and background process. Initializing it to 1 indicates that LiteBox always runs in foreground, but I can also run it in background (cargo run -p litebox_runner_linux_userland ... &). In the latter case, LiteBox does not control the foreground process. Depending on how applications use this syscall, we may have different designs. For now, maybe we could just return some error code and print out some warnings.

litebox: self.litebox,
unix_addr_table: litebox::sync::RwLock::new(syscalls::unix::UnixAddrTable::new()),
});
Expand Down Expand Up @@ -234,6 +235,7 @@ impl LinuxShim {
wait_state: wait::WaitState::new(self.0.platform),
pid,
ppid,
pgrp: Arc::new(core::sync::atomic::AtomicI32::new(pid)),
tid: pid,
credentials: syscalls::process::Credentials {
uid,
Expand All @@ -248,6 +250,10 @@ impl LinuxShim {
signals: syscalls::signal::SignalState::new_process(),
},
};
// Default foreground process group to the initial process.
self.0
.fg_pgrp
.store(pid, core::sync::atomic::Ordering::Relaxed);
entrypoints.task.load_program(
loader::elf::ElfLoader::new(&entrypoints.task, path)?,
argv,
Expand Down Expand Up @@ -1088,6 +1094,11 @@ impl Task {
}
SyscallRequest::Getpid => Ok(self.sys_getpid().reinterpret_as_unsigned() as usize),
SyscallRequest::Getppid => Ok(self.sys_getppid().reinterpret_as_unsigned() as usize),
SyscallRequest::Getpgrp => Ok(self.sys_getpgrp().reinterpret_as_unsigned() as usize),
SyscallRequest::Setpgid { pid, pgid } => self.sys_setpgid(pid, pgid),
SyscallRequest::Getpgid { pid } => self
.sys_getpgid(pid)
.map(|v| v.reinterpret_as_unsigned() as usize),
SyscallRequest::Getuid => Ok(self.sys_getuid() as usize),
SyscallRequest::Getgid => Ok(self.sys_getgid() as usize),
SyscallRequest::Geteuid => Ok(self.sys_geteuid() as usize),
Expand Down Expand Up @@ -1161,6 +1172,8 @@ struct GlobalState {
/// Next thread ID to assign.
// TODO: better management of thread IDs
next_thread_id: core::sync::atomic::AtomicI32,
/// Foreground process group ID for the terminal.
fg_pgrp: core::sync::atomic::AtomicI32,
/// UNIX domain socket address table
unix_addr_table: litebox::sync::RwLock<Platform, syscalls::unix::UnixAddrTable>,
}
Expand All @@ -1173,6 +1186,8 @@ struct Task {
pid: i32,
/// Parent Process ID
ppid: i32,
/// Process group ID. Defaults to `pid`.
pgrp: Arc<core::sync::atomic::AtomicI32>,
/// Thread ID
tid: i32,
/// Task credentials. These are set per task but are Arc'd to save space
Expand Down Expand Up @@ -1214,6 +1229,7 @@ mod test_utils {
thread: syscalls::process::ThreadState::new_process(pid),
pid,
ppid: 0,
pgrp: Arc::new(core::sync::atomic::AtomicI32::new(pid)),
tid: pid,
credentials: Arc::new(syscalls::process::Credentials {
uid: 0,
Expand Down Expand Up @@ -1243,6 +1259,7 @@ mod test_utils {
thread: self.thread.new_thread(tid)?,
pid: self.pid,
ppid: self.ppid,
pgrp: self.pgrp.clone(),
tid,
credentials: self.credentials.clone(),
comm: self.comm.clone(),
Expand Down
20 changes: 20 additions & 0 deletions litebox_shim_linux/src/syscalls/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1275,6 +1275,24 @@ impl Task {
Ok(0)
}
IoctlArg::TCSETS(_) => Ok(0), // TODO: implement
IoctlArg::TIOCGPGRP(pgrp) => {
let fg = self
.global
.fg_pgrp
.load(core::sync::atomic::Ordering::Relaxed);
pgrp.write_at_offset(0, fg).ok_or(Errno::EFAULT)?;
Ok(0)
}
IoctlArg::TIOCSPGRP(pgrp) => {
let pgid: i32 = pgrp.read_at_offset(0).ok_or(Errno::EFAULT)?;
if pgid <= 0 {
return Err(Errno::EINVAL);
}
self.global
.fg_pgrp
.store(pgid, core::sync::atomic::Ordering::Relaxed);
Ok(0)
}
IoctlArg::TIOCGWINSZ(ws) => {
ws.write_at_offset(
0,
Expand Down Expand Up @@ -1389,6 +1407,8 @@ impl Task {
},
IoctlArg::TCGETS(..)
| IoctlArg::TCSETS(..)
| IoctlArg::TIOCGPGRP(..)
| IoctlArg::TIOCSPGRP(..)
| IoctlArg::TIOCGPTN(..)
| IoctlArg::TIOCGWINSZ(..) => match desc {
Descriptor::LiteBoxRawFd(raw_fd) => files.run_on_raw_fd(
Expand Down
134 changes: 134 additions & 0 deletions litebox_shim_linux/src/syscalls/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ impl Task {
pid: self.pid,
tid: child_tid,
ppid: self.ppid,
pgrp: self.pgrp.clone(),
credentials: self.credentials.clone(),
comm: self.comm.clone(),
fs: fs.into(),
Expand Down Expand Up @@ -1148,10 +1149,47 @@ impl Task {
self.pid
}

/// Handle syscall `getppid`.
pub(crate) fn sys_getppid(&self) -> i32 {
self.ppid
}

/// Handle syscall `getpgrp`.
pub(crate) fn sys_getpgrp(&self) -> i32 {
self.pgrp.load(core::sync::atomic::Ordering::Relaxed)
}

/// Handle syscall `getpgid`.
pub(crate) fn sys_getpgid(&self, pid: i32) -> Result<i32, Errno> {
if pid < 0 {
return Err(Errno::EINVAL);
}
if pid == 0 || pid == self.pid {
Ok(self.pgrp.load(core::sync::atomic::Ordering::Relaxed))
} else {
Err(Errno::ESRCH)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERRORS         [top](https://man7.org/linux/man-pages/man3/getpgid.3p.html#top_of_page)
       The getpgid() function shall fail if:

       EPERM  The process whose process ID is equal to pid is not in the
              same session as the calling process, and the implementation
              does not allow access to the process group ID of that
              process from the calling process.

       ESRCH  There is no process with a process ID equal to pid.

       The getpgid() function may fail if:

       EINVAL The value of the pid argument is invalid.

Add some comment here? Currently there is only process so we always return ESRCH.

}
}

/// Handle syscall `setpgid`.
#[allow(clippy::similar_names)]
pub(crate) fn sys_setpgid(&self, pid: i32, pgid: i32) -> Result<usize, Errno> {
if pid < 0 {
return Err(Errno::EINVAL);
}
let target_pid = if pid == 0 { self.pid } else { pid };
let target_pgid = if pgid == 0 { target_pid } else { pgid };
if target_pgid < 0 {
return Err(Errno::EINVAL);
}
if target_pid != self.pid {
return Err(Errno::ESRCH);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar

}
self.pgrp
.store(target_pgid, core::sync::atomic::Ordering::Relaxed);
Comment on lines +1188 to +1189
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add some check to ensure that pgid is valid. Since we only have one process, we can just return an error if pgid differs from the current one. Don't need to change pgid at all.

Ok(0)
}

/// Handle syscall `getuid`.
pub(crate) fn sys_getuid(&self) -> u32 {
self.credentials.uid
Expand Down Expand Up @@ -1606,4 +1644,100 @@ mod tests {
"prctl get_name returned unexpected comm for too long name"
);
}

#[test]
fn test_getpgrp() {
let task = crate::syscalls::tests::init_platform(None);

// getpgrp should return the pid (default behavior for new processes)
let pgrp = task.sys_getpgrp();
let pid = task.sys_getpid();
assert_eq!(
pgrp, pid,
"getpgrp should return pid for a new process that hasn't joined another process group"
);

// After setpgid, getpgrp should reflect the new value
task.sys_setpgid(0, 42).expect("setpgid failed");
assert_eq!(task.sys_getpgrp(), 42);
}

#[test]
fn test_getpgid_self() {
let task = crate::syscalls::tests::init_platform(None);
let pid = task.sys_getpid();

// getpgid(0) should return own pgrp
assert_eq!(task.sys_getpgid(0).unwrap(), pid);
// getpgid(own_pid) should return own pgrp
assert_eq!(task.sys_getpgid(pid).unwrap(), pid);
}

#[test]
fn test_getpgid_unknown() {
use crate::Errno;
let task = crate::syscalls::tests::init_platform(None);

// getpgid for a negative pid should return EINVAL
assert_eq!(task.sys_getpgid(-1), Err(Errno::EINVAL));

// getpgid for an unknown pid should return ESRCH
assert_eq!(task.sys_getpgid(99999), Err(Errno::ESRCH));
}

#[test]
fn test_setpgid() {
let task = crate::syscalls::tests::init_platform(None);
let pid = task.sys_getpid();

// setpgid(0, 0) is equivalent to setpgrp: sets pgrp to own pid
task.sys_setpgid(0, 0).expect("setpgid(0, 0) failed");
assert_eq!(task.sys_getpgrp(), pid);
assert_eq!(task.sys_getpgid(0).unwrap(), pid);

// setpgid(0, 42) sets pgrp to 42
task.sys_setpgid(0, 42).expect("setpgid(0, 42) failed");
assert_eq!(task.sys_getpgrp(), 42);
assert_eq!(task.sys_getpgid(pid).unwrap(), 42);
}

#[test]
fn test_setpgid_invalid() {
use crate::Errno;
let task = crate::syscalls::tests::init_platform(None);

// Negative pid should return EINVAL
assert_eq!(task.sys_setpgid(-1, 1), Err(Errno::EINVAL));

// Negative pgid should return EINVAL
assert_eq!(task.sys_setpgid(0, -1), Err(Errno::EINVAL));

// Unknown pid should return ESRCH
assert_eq!(task.sys_setpgid(99999, 1), Err(Errno::ESRCH));
}

#[test]
fn test_pgrp_inherited() {
let task = crate::syscalls::tests::init_platform(None);

// Set pgrp to a custom value
task.sys_setpgid(0, 42).expect("setpgid failed");
assert_eq!(task.sys_getpgrp(), 42);

// Clone and verify child inherits the pgrp
let child = task.clone_for_test().expect("clone_for_test failed");
assert_eq!(child.sys_getpgrp(), 42);
}

#[test]
fn test_pgrp_shared_between_threads() {
let task = crate::syscalls::tests::init_platform(None);
let child = task.clone_for_test().expect("clone_for_test failed");

task.sys_setpgid(0, 42).expect("setpgid failed");
assert_eq!(child.sys_getpgrp(), 42);

task.sys_setpgid(0, 7).expect("setpgid failed");
assert_eq!(child.sys_getpgrp(), 7);
}
}