use std::ffi::OsStr;

use nix::sys::signal::Signal;

use crate::unshare::{ffi_util::ToCString, Command, Executable};

impl Command {
    /// Allow child process to daemonize. By default we run equivalent of
    /// `set_parent_death_signal(SIGKILL)`. See the `set_parent_death_signal`
    /// for better explanation.
    pub fn allow_daemonize(&mut self) -> &mut Command {
        self.config.death_sig = None;
        self
    }

    /// Set a signal that is sent to a process when it's parent is dead.
    /// This is by default set to `SIGKILL`. And you should keep it that way
    /// unless you know what you are doing.
    ///
    /// Particularly you should consider the following choices:
    ///
    /// 1. Instead of setting ``PDEATHSIG`` to some other signal, send signal
    ///    yourself and wait until child gracefully finishes.
    ///
    /// 2. Instead of daemonizing use ``systemd``/``upstart``/whatever system
    ///    init script to run your service
    ///
    /// Another issue with this option is that it works only with immediate
    /// child. To better control all descendant processes you may need the
    /// following:
    ///
    /// 1. The `prctl(PR_SET_CHILD_SUBREAPER..)` in parent which allows to
    ///    "catch" descendant processes.
    ///
    /// 2. The pid namespaces
    ///
    /// The former is out of scope of this library. The latter works by
    /// ``cmd.unshare(Namespace::Pid)``, but you may need to setup mount points
    /// and other important things (which are out of scope too).
    ///
    /// To reset this behavior use ``allow_daemonize()``.
    pub fn set_parent_death_signal(&mut self, sig: Signal) -> &mut Command {
        self.config.death_sig = Some(sig);
        self
    }

    /// Keep signal mask intact after executing child, keeps also ignored
    /// signals
    ///
    /// By default signal mask is empty and all signals are reset to the
    /// `SIG_DFL` value right before `execve()` syscall.
    ///
    /// This is only useful if started process is aware of the issue and sets
    /// sigmasks to some reasonable value. When used wisely it may avoid some
    /// race conditions when signal is sent after child is cloned but before
    /// child have been able to establish it's state.
    pub fn keep_sigmask(&mut self) -> &mut Command {
        self.config.restore_sigmask = false;
        self
    }

    /// Set the argument zero for the process
    ///
    /// By default argument zero is same as path to the program to run. You
    /// may set it to a short name of the command or to something else to
    /// pretend there is a symlink to a program (for example to run `gzip` as
    /// `gunzip`).
    pub fn arg0<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command {
        if let Executable::Program((_, ref mut args)) = self.exe {
            args[0] = arg.to_cstring();
        }
        self
    }

    /// Makes child process a group leader
    ///
    /// If child process is being launched as a foreground job,
    /// the child process group needs to be put into the foreground on
    /// the controlling terminal using `tcsetpgrp`. To request status
    /// information from stopped child process you should call `waitpid` with
    /// `WUNTRACED` flag. And then check status with `WIFSTOPPED` macro.
    /// After giving child process group access to the controlling terminal
    /// you should send the SIGCONT signal to the child process group.
    pub fn make_group_leader(&mut self, make_group_leader: bool) -> &mut Command {
        self.config.make_group_leader = make_group_leader;
        self
    }

    /// Deny reading the timestamp counter (x86 only)
    pub fn deny_tsc(&mut self, deny: bool) -> &mut Command {
        self.config.deny_tsc = deny;
        self
    }

    /// Makes process keeps capabilities (only CAP_SYS_PTRACE is dropped atm).
    pub fn keep(&mut self, keep: bool) -> &mut Command {
        self.config.keep = keep;
        self
    }

    /// STOPs process before exec, so e.g. a ptracer can attach.
    pub fn stop(&mut self, stop: bool) -> &mut Command {
        self.config.stop = stop;
        self
    }
}
