Skip to content

Tracking Issue for unix_send_signal #141975

Open

Description

@tgross35

Feature gate: #![feature(unix_send_signal)]

This is a tracking issue for API to send a signal to a child process via libc kill.

Public API

// std::os::unix::process

pub trait ChildExt {
    fn send_signal(&self, signal: i32) -> Result<()>;
    fn send_process_group_signal(&self, signal: i32) -> io::Result<()>;
}

impl ChildExt for std::process::Child { /* ... */ }

See also: #156537

Steps / History

(Remember to update the S-tracking-* label when checking boxes.)

Unresolved Questions

Footnotes

  1. https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩

Activity

  1. added
    T-libs-api[DEPRECATED; DO NOT USE]
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    E-help-wantedCall for participation: Help is requested to fix this issue.
    O-unixOperating system: Unix-like
    on Jun 3, 2025
  2. added a commit that references this issue on Jun 16, 2025
  3. added 3 commits that reference this issue on Jun 17, 2025
  4. added a commit that references this issue on Jun 19, 2025
  5. added a commit that references this issue on Jun 19, 2025
  6. added 2 commits that reference this issue on Jun 20, 2025
  7. added a commit that references this issue on Jun 20, 2025
  8. added a commit that references this issue on Jul 3, 2025
  9. 5 remaining items

  10. jmillikin commented on May 14, 2026

    @jmillikin
    Contributor

    FYI: A new function send_process_group_signal was added to this feature in PR #156539

    trait ChildExt {
        // ...
    
        // like `send_signal` but wraps `killpg()` instead of `kill()`
        fn send_process_group_signal(&self, signal: i32) -> io::Result<()>;
    }
  11. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
  12. tgross35 commented on Sep 29, 2026

    @tgross35
    MemberAuthor

    Is there some reason our preexisting internal implementation took &mut?

    Perhaps to avoid racing with this

    // If we've already waited on this process then the pid can be recycled and
    // used for another process, and we probably shouldn't be sending signals to
    // random processes, so return Ok because the process has exited already.
    if self.status.is_some() {
    return Ok(());
    }
    . But wait already takes &mut self so that should be fine.

  13. tgross35 commented on Sep 29, 2026

    @tgross35
    MemberAuthor

    I don't think this is ready for stabilization yet, but nominating for @rust-lang/libs-ping for the two main unresolved questions:

    1. Should this take &self or &mut self? Tracking Issue for unix_kill_process_group #156537 has the same question.
    2. Should we provide a subset of common signals in some form?

    @rustbot label +I-libs-nominated

    For &T vs. &mut T: kill and wait take &mut T even though they don't strictly have to. I'm not really sure why but perhaps some implementation may want to update some internal data or have a weak guard against racing? Taking &mut self is probably the easiest answer, since otherwise you could work around the existing kill mutability requirement with .send_signal(SIGKILL).

    For the second question, I think we should expose a subset of signals to make this more usable without needing libc. The constants supported by the kill utility could make a reasonable list https://pubs.opengroup.org/onlinepubs/9799919799/utilities/kill.html: SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGKILL, SIGALRM, and SIGTERM`. I don't have too much of a preference for what this looks like.

    // Option A
    pub mod signal {
        pub const INTERRUPT: i32 = libc::SIGINT;
        pub const ABORT: i32 = libc::SIGABRT;
    }
    
    // Option B
    pub const SIGNAL_INTERRUPT: i32 = libc::SIGINT;
    pub const SIGNAL_ABORT: i32 = libc::SIGABRT;
    
    // Option C
    pub enum Signal {
        Interrupt = libc::SIGINT,
        Abort = libc::SIGABRT,
        Any(i32),
    }
  14. added
    I-libs-nominatedNominated for discussion during a libs team meeting.
    and removed
    I-libs-nominatedNominated for discussion during a libs team meeting.
    on Sep 30, 2026
  15. joshtriplett commented on Oct 1, 2026

    @joshtriplett
    Member

    Speaking for myself and not for the rest of libs:

    I don't think we should block on bikeshedding a set of signal constants and figuring out which ones we can offer portably.

    When we do offer those constants, I think we should take our cues from rustix and not rename them except to 1) strip off the common SIG prefix in favor of an enum and 2) maybe expand the equivalents of creat like ABRT to ABORT or ALRM to ALARM. I don't think, for instance, TERM should be expanded to TERMINATE.

    For the first question: I don't think we're providing much of a useful guard by requiring &mut self, and I think it'd be fine to allow just &self. If you're concerned about workarounds, note that child.id() only requires &self, and the PID is all you need to send a signal.

  16. thomcc commented on Oct 2, 2026

    @thomcc
    Member

    I think I would do this as a struct that wraps the i32. Something like

    // Option D
    #[repr(transparent)]
    #[derive(Copy, Clone, PartialEq, ...)]
    pub struct Signal(i32);
    
    impl Signal {
        pub const ABORT: Self = Self::from_raw(libc::SIGABRT);
        pub const INTERRUPT: Self = Self::from_raw(libc::SIGINT);
        // ...
    
    
        pub const fn from_raw(code: i32) -> Self { Self(code) }
        pub const fn into_raw(self) -> i32 { self.0 }
        // ...
    }

    I prefer this because it makes it a bit harder to use the raw signal value, which can be different on different OSes (or architectures within an OS), at least for some signals.

    It also gives us a place to put methods or trait impls. I could imagine a Debug impl that tells you the name of the signal (at least for the ones we give as constants) would be useful, for example.

    It's also more obvious in API signatures.

    A downside with this (and with option C) is that we already use i32 for signals elsewhere (for example https://doc.rust-lang.org/nightly/std/os/unix/process/trait.ExitStatusExt.html#tymethod.stopped_signal).


    // Option C

    It's neither here nor there (since personally I don't prefer the enum approach for this case), but according to https://goals.rust-lang.org/2026/open-enums.html we do have a project goal around open enums, so I'd imagine we'd want to use that if we wanted that. Admittedly, blocking library features we want because of language features we may get someday is... less than ideal. But I think if we went the enum approach and didn't wait for open enums, it would be a bit odd/unfortunate if/when we did get them.

  17. joboet commented on Oct 4, 2026

    @joboet
    Member

    For &T vs. &mut T: kill and wait take &mut T even though they don't strictly have to. I'm not really sure why but perhaps some implementation may want to update some internal data or have a weak guard against racing? Taking &mut self is probably the easiest answer, since otherwise you could work around the existing kill mutability requirement with .send_signal(SIGKILL).

    wait has to take a mutable reference since it store sthe exit status of the child in the handle, so that it's safe to call multiple times. send_signal doesn't have any such constraints, and as such I think a shared reference makes more sense. I don't really see why kill takes a mutable reference – perhaps Windows has some special requirements?

  18. ChrisDenton commented on Oct 6, 2026

    @ChrisDenton
    Member

    I don't really see why kill takes a mutable reference – perhaps Windows has some special requirements?

    It's nothing to do with Windows.

  19. joshtriplett commented on Oct 6, 2026

    @joshtriplett
    Member

    We talked about this in today's @rust-lang/libs meeting.

    We agreed that we want this operation to be &self, not &mut self. Nothing it does needs &mut self and we didn't feel there was semantic value in forcing exclusive ownership.

    We debated at length whether we wanted to use i32 or use struct Signal. We didn't fully settle that question, but the trend of the meeting was leaning towards i32. We have other APIs ( ExitStatusExt::signal and ExitStatusExt::stopped_signal that use i32 for signals, and we didn't see much value in an opaque type because we aren't offering APIs where there'd be much potential for confusion with other i32s. Also, our handling of UIDs and GIDs use non-opaque integers as well.

    Finally, we talked about what to do with constants for signal numbers. We agreed that we want the constants namespaced (e.g. pub mod sig { ... }). (If we used an opaque type, they'd be associated constants, but if we use i32 they should just be pub const.) We didn't finish bikeshedding the naming, but we were roughly leaning in favor of "people using these APIs know the names, don't gratuitously change them". So, for instance, sig::FPE rather than sig::FLOATING_POINT_EXCEPTION. (We debated some degree of expanding some of the more obfuscated ones, but also noted that we could offer more than one name for the same constant, e.g. sig::ALRM and sig::ALARM)

    So, tentatively: let's proceed with i32, and a mod sig containing constants for the most useful/portable signals.

  20. joshtriplett commented on Oct 6, 2026

    @joshtriplett
    Member

    Tentative list of the most useful/portable signals:

    • ABRT and ABORT
    • ALRM and ALARM
    • BUS
    • CHLD and CLD and CHILD
    • CONT
    • FPE
    • HUP
    • ILL
    • INT
    • KILL
    • PIPE
    • QUIT
    • SEGV
    • STOP
    • TSTP
    • SYS
    • TERM
    • TRAP
    • TTIN
    • TTOU
    • URG
    • USR1
    • USR2
    • WINCH

    Not all of these will be useful with send_signal, but some will also be useful with ExitStatusExt or similar.

    AFAICT all of these should exist on every Rust target. I've omitted some that wouldn't be. (Notably, PROF, VTALRM, and XCPU, which might not exist on RTEMS. I would ideally like to include those too, but let's get the definitely-available-everywhere subset added first.)

  21. removed
    I-libs-nominatedNominated for discussion during a libs team meeting.
    on Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCO-unixOperating system: Unix-likeT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions