| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SHILL_PROCESS_MANAGER_H_ |
| #define SHILL_PROCESS_MANAGER_H_ |
| |
| #include <sys/types.h> // for rlim_t |
| |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/callback.h> |
| #include <base/cancelable_callback.h> |
| #include <base/files/file_descriptor_watcher_posix.h> |
| #include <base/files/file_path.h> |
| #include <base/lazy_instance.h> |
| #include <base/location.h> |
| #include <base/memory/weak_ptr.h> |
| #include <brillo/minijail/minijail.h> |
| #include <brillo/process/process.h> |
| #include <brillo/process/process_reaper.h> |
| #include <libminijail.h> |
| |
| namespace shill { |
| |
| struct std_file_descriptors { |
| int* stdin_fd; |
| int* stdout_fd; |
| int* stderr_fd; |
| }; |
| |
| class EventDispatcher; |
| |
| // The ProcessManager is a singleton providing process creation and |
| // asynchronous process termination. Need to initialize it once with |
| // Init method call. |
| class ProcessManager { |
| public: |
| using ExitCallback = base::OnceCallback<void(int exit_status)>; |
| using ExitWithStdoutCallback = |
| base::OnceCallback<void(int exit_status, const std::string& stdout_str)>; |
| |
| struct MinijailOptions { |
| // Program will run as |user| and |group|. |
| std::string user; |
| std::string group; |
| // Provides the child process with capabilities, which |user| might not have |
| // on its own. |
| uint64_t capmask; |
| // Allows child process to inherit supplementary groups from uid, equivalent |
| // to using '-G' on the minijail command line. |
| bool inherit_supplementary_groups; |
| // Indicates that non-standard file descriptors should be closed so they |
| // cannot be inherited by the child process. |
| bool close_nonstd_fds; |
| // If set, the soft limit of the maximum size of the process's virtual |
| // memory (RLIMIT_AS) will be set to this value. See getrlimit(2). |
| std::optional<rlim_t> rlimit_as_soft; |
| }; |
| |
| virtual ~ProcessManager(); |
| |
| // This is a singleton -- use ProcessManager::GetInstance()->Foo(). |
| static ProcessManager* GetInstance(); |
| |
| // Register async signal handler and setup process reaper. |
| virtual void Init(EventDispatcher* dispatcher); |
| |
| // Call on shutdown to release async_signal_handler_. |
| virtual void Stop(); |
| |
| // Create and start a process for |program| with |arguments|. |environment| |
| // variables will be setup in the child process before exec the |program|. |
| // |terminate_with_parent| is used to indicate if child process should |
| // self terminate if the parent process exits. |exit_callback| will be |
| // invoked when child process exits (not terminated by us). Return -1 |
| // if failed to start the process, otherwise, return the pid of the child |
| // process. |
| virtual pid_t StartProcess( |
| const base::Location& spawn_source, |
| const base::FilePath& program, |
| const std::vector<std::string>& arguments, |
| const std::map<std::string, std::string>& environment, |
| bool terminate_with_parent, |
| ExitCallback exit_callback); |
| |
| // Similar to StartProcess(), with the following differences: |
| // - terminate_with_parent is not supported (may be non-trivial). |
| // - |minijail_options| will be applied when starting the process in minijail. |
| // See the comments for MinijailOptions above for the available options. |
| virtual pid_t StartProcessInMinijail( |
| const base::Location& spawn_source, |
| const base::FilePath& program, |
| const std::vector<std::string>& arguments, |
| const std::map<std::string, std::string>& environment, |
| const MinijailOptions& minijail_options, |
| ExitCallback exit_callback) { |
| return StartProcessInMinijailWithPipes( |
| spawn_source, program, arguments, environment, minijail_options, |
| std::move(exit_callback), |
| (struct std_file_descriptors){nullptr, nullptr, nullptr}); |
| } |
| |
| // Similar to StartProcessInMinijail(), with the additional ability to |
| // pipe the child's stdin/stdout/stderr back to us. If any of those |
| // streams is not needed, simply pass nullptr for the corresponding |
| // member in std file descriptor struct. If no pipes are needed, use |
| // StartProcessInMinijail(). |
| virtual pid_t StartProcessInMinijailWithPipes( |
| const base::Location& spawn_source, |
| const base::FilePath& program, |
| const std::vector<std::string>& arguments, |
| const std::map<std::string, std::string>& environment, |
| const MinijailOptions& minijail_options, |
| ExitCallback exit_callback, |
| struct std_file_descriptors std_fds); |
| |
| // Similar to StartProcessInMinijail, with the additional ability to return |
| // the output of stdout with the exit status together when the program exits. |
| // Note that the output of stdout will be cached inside this object during the |
| // lifetime of the process, so this function may not be suitable for the |
| // processes which will run for a long time and output a lot in stdout. |
| // Currently, this class will only keep 32KB at maximum for the stdout of a |
| // process. Any string beyond that length will be truncated. |
| virtual pid_t StartProcessInMinijailWithStdout( |
| const base::Location& spawn_source, |
| const base::FilePath& program, |
| const std::vector<std::string>& arguments, |
| const std::map<std::string, std::string>& environment, |
| const MinijailOptions& minijail_options, |
| ExitWithStdoutCallback exit_callback); |
| |
| // Stop the given |pid|. Previously registered |exit_callback| will be |
| // unregistered, since the caller is not interested in this process anymore |
| // and that callback might not be valid by the time this process terminates. |
| // This will attempt to terminate the child process by sending a SIGTERM |
| // signal first. If the process doesn't terminate within a certain time, |
| // ProcessManager will attempt to send a SIGKILL signal. It will give up |
| // with an error log If the process still doesn't terminate within a certain |
| // time. |
| virtual bool StopProcess(pid_t pid); |
| |
| // Stop the given |pid| in a synchronous manner. |
| virtual bool StopProcessAndBlock(pid_t pid); |
| |
| // Replace the current exit callback for |pid| with |new_callback|. |
| virtual bool UpdateExitCallback(pid_t pid, ExitCallback new_callback); |
| |
| protected: |
| ProcessManager(); |
| ProcessManager(const ProcessManager&) = delete; |
| ProcessManager& operator=(const ProcessManager&) = delete; |
| |
| private: |
| friend class ProcessManagerTest; |
| friend base::LazyInstanceTraitsBase<ProcessManager>; |
| |
| using TerminationTimeoutCallback = base::CancelableClosure; |
| |
| struct WatchedProcess { |
| // |exit_callback| is valid when the caller only expects the exit status. |
| // |exit_with_stdout_callback| is valid when the caller also expects the |
| // output of stdout. One and only one of these two callbacks can be valid at |
| // the same time. |
| ExitCallback exit_callback; |
| ExitWithStdoutCallback exit_with_stdout_callback; |
| |
| // The exit status if the process has already exited. |
| std::optional<int> exit_status; |
| |
| // Fields related to stdout of this watched process. They are meaningful |
| // only when |exit_with_stdout_callback| is set. |stdout_fd| and |
| // |stdout_watcher| will be set once the process is started, and be reset |
| // once the stdout pipe is closed. |
| base::ScopedFD stdout_fd; |
| std::unique_ptr<base::FileDescriptorWatcher::Controller> stdout_watcher; |
| std::string stdout_str; |
| }; |
| |
| // See the above comment for StartProcessInMinijailWithPipes(). |
| pid_t StartProcessInMinijailWithPipesInternal( |
| const base::Location& spawn_source, |
| const base::FilePath& program, |
| const std::vector<std::string>& arguments, |
| const std::map<std::string, std::string>& environment, |
| const MinijailOptions& minijail_options, |
| struct std_file_descriptors std_fds); |
| |
| // Invoked when the stdout of the process |pid| is readable. |
| void OnProcessStdoutReadable(pid_t pid); |
| |
| // Invoked when process |pid| exited. |
| void OnProcessExited(pid_t pid, const siginfo_t& info); |
| |
| // Check the WatchedProcess struct associated with process |pid|, and invoke |
| // the corresponding callback if: |
| // - |exit_status| is ready when |exit_callback| is set; |
| // - |exit_status| is ready and stdout pipe has been closed when |
| // |exit_with_stdout_callback| is set. |
| void CheckProcessExitStateAndNotify(pid_t pid); |
| |
| // Invoked when process |pid| did not terminate within a certain timeout. |
| // |kill_signal| indicates the signal used for termination. When it is set |
| // to true, SIGKILL was used to terminate the process, otherwise, SIGTERM |
| // was used. |
| void ProcessTerminationTimeoutHandler(pid_t pid, bool kill_signal); |
| |
| // Send a termination signal to process |pid|. If |kill_signal| is set to |
| // true, SIGKILL is sent, otherwise, SIGTERM is sent. After signal is sent, |
| // |pid| and timeout handler is added to |pending_termination_processes_| |
| // list, to make sure process |pid| does exit in timely manner. |
| bool TerminateProcess(pid_t pid, bool kill_signal); |
| |
| // Kill process |pid|. If |kill_signal| is true it will send SIGKILL, |
| // otherwise it will send SIGTERM. |
| // It returns true when the process was already dead or killed within |
| // the timeout. |
| // It returns false when the process failed to exit within the timeout |
| // or the system failed to send kill singal. |
| bool KillProcessWithTimeout(pid_t pid, bool kill_signal); |
| |
| // Kill process |pid| using signal |signal|. |
| // The |killed| will be set true when the process was already dead. |
| // It returns true when it sent the |signal| successfully or the |
| // process was already dead. |
| // It returns false when the system failed to send |signal|. |
| bool KillProcess(pid_t pid, int signal, bool* killed); |
| |
| // Wait for process |pid| to exit. This function will check it for at most |
| // |tries| times. The interval of waiting time grows exponentially from |
| // |sleep_ms| and it has an |upper_bound_ms| upper bound. |
| bool WaitpidWithTimeout(pid_t pid, |
| unsigned int sleep_ms, |
| unsigned int upper_bound_ms, |
| int tries); |
| |
| // Used to watch processes. |
| std::unique_ptr<brillo::AsynchronousSignalHandler> async_signal_handler_; |
| brillo::ProcessReaper process_reaper_; |
| |
| EventDispatcher* dispatcher_; |
| brillo::Minijail* minijail_; |
| |
| // Processes to watch for the caller. |
| std::map<pid_t, WatchedProcess> watched_processes_; |
| // Processes being terminated by us. Use a timer to make sure process |
| // does exit, log an error if it failed to exit within a specific timeout. |
| std::map<pid_t, std::unique_ptr<TerminationTimeoutCallback>> |
| pending_termination_processes_; |
| |
| base::WeakPtrFactory<ProcessManager> weak_factory_{this}; |
| }; |
| |
| } // namespace shill |
| |
| #endif // SHILL_PROCESS_MANAGER_H_ |