Skip to main content

yash_env/system/concurrency/
delegates.rs

1// This file is part of yash, an extended POSIX shell.
2// Copyright (C) 2026 WATANABE Yuki
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Trait implementations for `Concurrent<S>` that delegate to the inner system `S`
18
19use super::super::resource::{LimitPair, Resource};
20use super::super::{
21    Chdir, ChildProcessStarter, Clock, Close, CpuTimes, Dir, Dup, Exec, Exit, Fcntl, FdFlag, Fork,
22    Fstat, GetCwd, GetPid, GetPw, GetRlimit, GetSigaction, GetUid, Gid, IsExecutableFile, Isatty,
23    Mode, OfdAccess, Open, OpenFlag, Pipe, Result, Seek, SendSignal, SetPgid, SetRlimit, ShellPath,
24    Sigaction, Sigmask, SigmaskOp, Signals, Sysconf, TcGetPgrp, TcSetPgrp, Times, Uid, Umask, Wait,
25    signal,
26};
27use super::Concurrent;
28use crate::io::Fd;
29use crate::job::{Pid, ProcessState};
30use crate::path::PathBuf;
31use crate::semantics::ExitStatus;
32use enumset::EnumSet;
33use std::convert::Infallible;
34use std::ffi::{CStr, CString};
35use std::io::SeekFrom;
36use std::ops::RangeInclusive;
37use std::time::Instant;
38use unix_str::UnixString;
39
40impl<S> Fstat for Concurrent<S>
41where
42    S: Fstat + Sigmask,
43{
44    type Stat = S::Stat;
45
46    #[inline]
47    fn fstat(&self, fd: Fd) -> Result<Self::Stat> {
48        self.inner.fstat(fd)
49    }
50    #[inline]
51    fn fstatat(&self, dir_fd: Fd, path: &CStr, follow_symlinks: bool) -> Result<Self::Stat> {
52        self.inner.fstatat(dir_fd, path, follow_symlinks)
53    }
54    #[inline]
55    fn is_directory(&self, path: &CStr) -> bool {
56        self.inner.is_directory(path)
57    }
58    #[inline]
59    fn fd_is_pipe(&self, fd: Fd) -> bool {
60        self.inner.fd_is_pipe(fd)
61    }
62}
63
64impl<S> IsExecutableFile for Concurrent<S>
65where
66    S: IsExecutableFile + Sigmask,
67{
68    #[inline]
69    fn is_executable_file(&self, path: &CStr) -> bool {
70        self.inner.is_executable_file(path)
71    }
72}
73
74impl<S> Pipe for Concurrent<S>
75where
76    S: Pipe + Sigmask,
77{
78    #[inline]
79    fn pipe(&self) -> Result<(Fd, Fd)> {
80        self.inner.pipe()
81    }
82}
83
84impl<S> Dup for Concurrent<S>
85where
86    S: Dup + Sigmask,
87{
88    #[inline]
89    fn dup(&self, from: Fd, to_min: Fd, flags: EnumSet<FdFlag>) -> Result<Fd> {
90        self.inner.dup(from, to_min, flags)
91    }
92
93    #[inline]
94    fn dup2(&self, from: Fd, to: Fd) -> Result<Fd> {
95        self.inner.dup2(from, to)
96    }
97}
98
99/// This implementation does not (yet) support non-blocking open operations.
100impl<S> Open for Concurrent<S>
101where
102    S: Open + Sigmask,
103{
104    #[inline]
105    fn open(
106        &self,
107        path: &CStr,
108        access: OfdAccess,
109        flags: EnumSet<OpenFlag>,
110        mode: Mode,
111    ) -> impl Future<Output = Result<Fd>> + use<S> {
112        self.inner.open(path, access, flags, mode)
113    }
114
115    #[inline]
116    fn open_tmpfile(&self, parent_dir: &unix_path::Path) -> Result<Fd> {
117        self.inner.open_tmpfile(parent_dir)
118    }
119
120    #[inline]
121    fn fdopendir(&self, fd: Fd) -> Result<impl Dir + use<S>> {
122        self.inner.fdopendir(fd)
123    }
124
125    #[inline]
126    fn opendir(&self, path: &CStr) -> Result<impl Dir + use<S>> {
127        self.inner.opendir(path)
128    }
129}
130
131impl<S> Close for Concurrent<S>
132where
133    S: Close + Sigmask,
134{
135    #[inline]
136    fn close(&self, fd: Fd) -> Result<()> {
137        self.inner.close(fd)
138    }
139}
140
141impl<S> Fcntl for Concurrent<S>
142where
143    S: Fcntl + Sigmask,
144{
145    #[inline]
146    fn ofd_access(&self, fd: Fd) -> Result<OfdAccess> {
147        self.inner.ofd_access(fd)
148    }
149
150    #[inline]
151    fn get_and_set_nonblocking(&self, fd: Fd, nonblocking: bool) -> Result<bool> {
152        self.inner.get_and_set_nonblocking(fd, nonblocking)
153    }
154
155    #[inline]
156    fn fcntl_getfd(&self, fd: Fd) -> Result<EnumSet<FdFlag>> {
157        self.inner.fcntl_getfd(fd)
158    }
159
160    #[inline]
161    fn fcntl_setfd(&self, fd: Fd, flags: EnumSet<FdFlag>) -> Result<()> {
162        self.inner.fcntl_setfd(fd, flags)
163    }
164}
165
166impl<S> Seek for Concurrent<S>
167where
168    S: Seek + Sigmask,
169{
170    #[inline]
171    fn lseek(&self, fd: Fd, position: SeekFrom) -> Result<u64> {
172        self.inner.lseek(fd, position)
173    }
174}
175
176impl<S> Umask for Concurrent<S>
177where
178    S: Sigmask + Umask,
179{
180    #[inline]
181    fn umask(&self, new_mask: Mode) -> Mode {
182        self.inner.umask(new_mask)
183    }
184}
185
186impl<S> GetCwd for Concurrent<S>
187where
188    S: GetCwd + Sigmask,
189{
190    #[inline]
191    fn getcwd(&self) -> Result<PathBuf> {
192        self.inner.getcwd()
193    }
194}
195
196impl<S> Chdir for Concurrent<S>
197where
198    S: Chdir + Sigmask,
199{
200    #[inline]
201    fn chdir(&self, path: &CStr) -> Result<()> {
202        self.inner.chdir(path)
203    }
204}
205
206impl<S> Clock for Concurrent<S>
207where
208    S: Clock + Sigmask,
209{
210    #[inline]
211    fn now(&self) -> Instant {
212        self.inner.now()
213    }
214}
215
216impl<S> Times for Concurrent<S>
217where
218    S: Sigmask + Times,
219{
220    #[inline]
221    fn times(&self) -> Result<CpuTimes> {
222        self.inner.times()
223    }
224}
225
226impl<S> GetPid for Concurrent<S>
227where
228    S: GetPid + Sigmask,
229{
230    #[inline]
231    fn getpid(&self) -> Pid {
232        self.inner.getpid()
233    }
234    #[inline]
235    fn getppid(&self) -> Pid {
236        self.inner.getppid()
237    }
238    #[inline]
239    fn getpgrp(&self) -> Pid {
240        self.inner.getpgrp()
241    }
242    #[inline]
243    fn getsid(&self, pid: Pid) -> Result<Pid> {
244        self.inner.getsid(pid)
245    }
246}
247
248impl<S> SetPgid for Concurrent<S>
249where
250    S: SetPgid + Sigmask,
251{
252    #[inline]
253    fn setpgid(&self, pid: Pid, pgid: Pid) -> Result<()> {
254        self.inner.setpgid(pid, pgid)
255    }
256}
257
258impl<S> Signals for Concurrent<S>
259where
260    S: Sigmask,
261{
262    const SIGABRT: signal::Number = S::SIGABRT;
263    const SIGALRM: signal::Number = S::SIGALRM;
264    const SIGBUS: signal::Number = S::SIGBUS;
265    const SIGCHLD: signal::Number = S::SIGCHLD;
266    const SIGCLD: Option<signal::Number> = S::SIGCLD;
267    const SIGCONT: signal::Number = S::SIGCONT;
268    const SIGEMT: Option<signal::Number> = S::SIGEMT;
269    const SIGFPE: signal::Number = S::SIGFPE;
270    const SIGHUP: signal::Number = S::SIGHUP;
271    const SIGILL: signal::Number = S::SIGILL;
272    const SIGINFO: Option<signal::Number> = S::SIGINFO;
273    const SIGINT: signal::Number = S::SIGINT;
274    const SIGIO: Option<signal::Number> = S::SIGIO;
275    const SIGIOT: signal::Number = S::SIGIOT;
276    const SIGKILL: signal::Number = S::SIGKILL;
277    const SIGLOST: Option<signal::Number> = S::SIGLOST;
278    const SIGPIPE: signal::Number = S::SIGPIPE;
279    const SIGPOLL: Option<signal::Number> = S::SIGPOLL;
280    const SIGPROF: signal::Number = S::SIGPROF;
281    const SIGPWR: Option<signal::Number> = S::SIGPWR;
282    const SIGQUIT: signal::Number = S::SIGQUIT;
283    const SIGSEGV: signal::Number = S::SIGSEGV;
284    const SIGSTKFLT: Option<signal::Number> = S::SIGSTKFLT;
285    const SIGSTOP: signal::Number = S::SIGSTOP;
286    const SIGSYS: signal::Number = S::SIGSYS;
287    const SIGTERM: signal::Number = S::SIGTERM;
288    const SIGTHR: Option<signal::Number> = S::SIGTHR;
289    const SIGTRAP: signal::Number = S::SIGTRAP;
290    const SIGTSTP: signal::Number = S::SIGTSTP;
291    const SIGTTIN: signal::Number = S::SIGTTIN;
292    const SIGTTOU: signal::Number = S::SIGTTOU;
293    const SIGURG: signal::Number = S::SIGURG;
294    const SIGUSR1: signal::Number = S::SIGUSR1;
295    const SIGUSR2: signal::Number = S::SIGUSR2;
296    const SIGVTALRM: signal::Number = S::SIGVTALRM;
297    const SIGWINCH: signal::Number = S::SIGWINCH;
298    const SIGXCPU: signal::Number = S::SIGXCPU;
299    const SIGXFSZ: signal::Number = S::SIGXFSZ;
300
301    #[inline]
302    fn sigrt_range(&self) -> Option<RangeInclusive<signal::Number>> {
303        self.inner.sigrt_range()
304    }
305
306    const NAMED_SIGNALS: &'static [(&'static str, Option<signal::Number>)] = S::NAMED_SIGNALS;
307
308    #[inline]
309    fn iter_sigrt(&self) -> impl DoubleEndedIterator<Item = signal::Number> + use<S> {
310        self.inner.iter_sigrt()
311    }
312    #[inline]
313    fn to_signal_number<N: Into<signal::RawNumber>>(&self, number: N) -> Option<signal::Number> {
314        self.inner.to_signal_number(number)
315    }
316    #[inline]
317    fn sig2str<N: Into<signal::RawNumber>>(
318        &self,
319        signal: N,
320    ) -> Option<std::borrow::Cow<'static, str>> {
321        self.inner.sig2str(signal)
322    }
323    #[inline]
324    fn str2sig(&self, name: &str) -> Option<signal::Number> {
325        self.inner.str2sig(name)
326    }
327    #[inline]
328    fn validate_signal(&self, number: signal::RawNumber) -> Option<(signal::Name, signal::Number)> {
329        self.inner.validate_signal(number)
330    }
331    #[inline]
332    fn signal_name_from_number(&self, number: signal::Number) -> signal::Name {
333        self.inner.signal_name_from_number(number)
334    }
335    #[inline]
336    fn signal_number_from_name(&self, name: signal::Name) -> Option<signal::Number> {
337        self.inner.signal_number_from_name(name)
338    }
339}
340
341/// Exposes the inner system's `sigmask` method.
342///
343/// This implementation of `Sigmask` simply delegates to the inner system's
344/// `sigmask` method, which bypasses the internal state of `Concurrent` and may
345/// prevent the [`peek`](super::Select::peek) and
346/// [`select`](super::Select::select) methods from responding to received
347/// signals without race conditions. To ensure that the signal mask is
348/// configured in a way that allows `Concurrent` to respond to signals
349/// correctly, direct calls to `sigmask` should be avoided, and, if necessary,
350/// only used to temporarily change the signal mask for specific operations
351/// while ensuring that the original mask is restored afterward before a next
352/// call to `peek`, `select`, or `set_disposition`.
353impl<S> Sigmask for Concurrent<S>
354where
355    S: Sigmask,
356{
357    type Sigset = S::Sigset;
358
359    #[inline]
360    fn sigmask(
361        &self,
362        op_and_signals: Option<(SigmaskOp, &Self::Sigset)>,
363        old_mask: Option<&mut Self::Sigset>,
364    ) -> impl Future<Output = Result<()>> + use<S> {
365        self.inner.sigmask(op_and_signals, old_mask)
366    }
367}
368
369impl<S> GetSigaction for Concurrent<S>
370where
371    S: GetSigaction + Sigmask,
372{
373    #[inline]
374    fn get_sigaction(&self, signal: signal::Number) -> Result<signal::Disposition> {
375        self.inner.get_sigaction(signal)
376    }
377}
378
379/// Exposes the inner system's `sigaction` method.
380///
381/// This implementation of `Sigaction` simply delegates to the inner system's
382/// `sigaction` method, which bypasses the internal state of `Concurrent` and
383/// may prevent the [`peek`](super::Select::peek) and
384/// [`select`](super::Select::select) methods from responding to received
385/// signals without race conditions. To ensure that signal dispositions are
386/// configured in a way that allows `Concurrent` to respond to signals
387/// correctly, direct calls to `sigaction` should be avoided, and, if necessary,
388/// only used to temporarily change the signal disposition for specific
389/// operations while ensuring that the original disposition is restored
390/// afterward before a next call to `peek`, `select`, or `set_disposition`.
391///
392/// The standard way to set a signal disposition to `Concurrent` is to use the
393/// `set_disposition` method provided by the [`SignalSystem`] trait, which
394/// ensures that the signal disposition and the signal mask are updated
395/// consistently.
396///
397/// [`SignalSystem`]: crate::trap::SignalSystem
398impl<S> Sigaction for Concurrent<S>
399where
400    S: Sigaction + Sigmask,
401{
402    #[inline]
403    fn sigaction(
404        &self,
405        signal: signal::Number,
406        disposition: signal::Disposition,
407    ) -> Result<signal::Disposition> {
408        self.inner.sigaction(signal, disposition)
409    }
410}
411
412// CaughtSignals is not implemented for Concurrent<S> because Concurrent needs to
413// control the signal dispositions and the signal mask itself to ensure that the
414// select method can respond to received signals without race conditions. Instead,
415// Concurrent<S> implements the SignalSystem trait, which provides the necessary
416// methods for configuring signal dispositions and masks in a controlled manner.
417//
418// Note: Sigmask, GetSigaction, and Sigaction are implemented above as delegating
419// implementations. However, direct calls to sigmask() and sigaction() bypass
420// Concurrent's internal state and may prevent peek() and select() from responding
421// to received signals without race conditions. To ensure correct behavior, use the
422// set_disposition method from the SignalSystem trait instead. If direct sigmask or
423// sigaction calls are necessary, temporarily change the mask/disposition and restore
424// it afterward before calling peek(), select(), or set_disposition().
425
426impl<S> SendSignal for Concurrent<S>
427where
428    S: SendSignal + Sigmask,
429{
430    #[inline]
431    fn kill(
432        &self,
433        pid: Pid,
434        signal: Option<signal::Number>,
435    ) -> impl Future<Output = Result<()>> + use<S> {
436        self.inner.kill(pid, signal)
437    }
438    #[inline]
439    fn raise(&self, signal: signal::Number) -> impl Future<Output = Result<()>> + use<S> {
440        self.inner.raise(signal)
441    }
442}
443
444impl<S> Isatty for Concurrent<S>
445where
446    S: Isatty + Sigmask,
447{
448    #[inline]
449    fn isatty(&self, fd: Fd) -> bool {
450        self.inner.isatty(fd)
451    }
452}
453
454impl<S> TcGetPgrp for Concurrent<S>
455where
456    S: Sigmask + TcGetPgrp,
457{
458    #[inline]
459    fn tcgetpgrp(&self, fd: Fd) -> Result<Pid> {
460        self.inner.tcgetpgrp(fd)
461    }
462}
463
464impl<S> TcSetPgrp for Concurrent<S>
465where
466    S: Sigmask + TcSetPgrp,
467{
468    #[inline]
469    fn tcsetpgrp(&self, fd: Fd, pgid: Pid) -> impl Future<Output = Result<()>> + use<S> {
470        self.inner.tcsetpgrp(fd, pgid)
471    }
472}
473
474impl<S> Concurrent<S>
475where
476    S: Fork + Sigmask,
477{
478    /// Creates a new child process.
479    ///
480    /// Returns the `ChildProcessStarter<S>` returned by the inner system's
481    /// [`Fork::new_child_process`] method. This method is an inherent method of
482    /// `Concurrent<S>` instead of an implementation of the `Fork` trait because
483    /// the return type does not match with that of the inner system `S`.
484    #[deprecated(
485        since = "0.14.0",
486        note = "use the `Fork::run_in_child_process` method instead"
487    )]
488    #[inline]
489    pub fn new_child_process(&self) -> Result<ChildProcessStarter<S>> {
490        #[allow(deprecated, reason = "the caller and callee are both deprecated")]
491        self.inner.new_child_process()
492    }
493}
494
495impl<S> Wait for Concurrent<S>
496where
497    S: Sigmask + Wait,
498{
499    #[inline]
500    fn wait(&self, target: Pid) -> Result<Option<(Pid, ProcessState)>> {
501        self.inner.wait(target)
502    }
503}
504
505impl<S> Exec for Concurrent<S>
506where
507    S: Exec + Sigmask,
508{
509    #[inline]
510    fn execve(
511        &self,
512        path: &CStr,
513        args: &[CString],
514        envs: &[CString],
515    ) -> impl Future<Output = Result<Infallible>> + use<S> {
516        self.inner.execve(path, args, envs)
517    }
518}
519
520impl<S> Exit for Concurrent<S>
521where
522    S: Exit + Sigmask,
523{
524    #[inline]
525    fn exit(&self, exit_status: ExitStatus) -> impl Future<Output = Infallible> + use<S> {
526        self.inner.exit(exit_status)
527    }
528}
529
530impl<S> GetUid for Concurrent<S>
531where
532    S: GetUid + Sigmask,
533{
534    #[inline]
535    fn getuid(&self) -> Uid {
536        self.inner.getuid()
537    }
538    #[inline]
539    fn geteuid(&self) -> Uid {
540        self.inner.geteuid()
541    }
542    #[inline]
543    fn getgid(&self) -> Gid {
544        self.inner.getgid()
545    }
546    #[inline]
547    fn getegid(&self) -> Gid {
548        self.inner.getegid()
549    }
550}
551
552impl<S> GetPw for Concurrent<S>
553where
554    S: GetPw + Sigmask,
555{
556    #[inline]
557    fn getpwnam_dir(&self, name: &CStr) -> Result<Option<PathBuf>> {
558        self.inner.getpwnam_dir(name)
559    }
560}
561
562impl<S> Sysconf for Concurrent<S>
563where
564    S: Sysconf + Sigmask,
565{
566    #[inline]
567    fn confstr_path(&self) -> Result<UnixString> {
568        self.inner.confstr_path()
569    }
570}
571
572impl<S> ShellPath for Concurrent<S>
573where
574    S: ShellPath + Sigmask,
575{
576    #[inline]
577    fn shell_path(&self) -> CString {
578        self.inner.shell_path()
579    }
580}
581
582impl<S> GetRlimit for Concurrent<S>
583where
584    S: GetRlimit + Sigmask,
585{
586    #[inline]
587    fn getrlimit(&self, resource: Resource) -> Result<LimitPair> {
588        self.inner.getrlimit(resource)
589    }
590}
591
592impl<S> SetRlimit for Concurrent<S>
593where
594    S: SetRlimit + Sigmask,
595{
596    #[inline]
597    fn setrlimit(&self, resource: Resource, limits: LimitPair) -> Result<()> {
598        self.inner.setrlimit(resource, limits)
599    }
600}