Apache Mesos
killtree.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_POSIX_KILLTREE_HPP__
14 #define __STOUT_OS_POSIX_KILLTREE_HPP__
15 
16 #include <stdlib.h>
17 #include <unistd.h>
18 
19 #include <list>
20 #include <queue>
21 #include <set>
22 
23 #include <stout/check.hpp>
24 #include <stout/os.hpp>
25 
26 #include <stout/os/pstree.hpp>
27 
28 
29 namespace os {
30 
31 // Forward declarations from os.hpp.
32 inline std::set<pid_t> children(pid_t, const std::list<Process>&, bool);
34 inline Option<Process> process(pid_t, const std::list<Process>&);
37  const std::set<pid_t>&,
38  const std::list<Process>&);
39 
40 
41 // Sends a signal to a process tree rooted at the specified pid.
42 // If groups is true, this also sends the signal to all encountered
43 // process groups.
44 // If sessions is true, this also sends the signal to all encountered
45 // process sessions.
46 // Note that processes of the group and session of the parent of the
47 // root process is not included unless they are part of the root
48 // process tree.
49 // Note that if the process 'pid' has exited we'll signal the process
50 // tree(s) rooted at pids in the group or session led by the process
51 // if groups = true or sessions = true, respectively.
52 // Returns the process trees that were successfully or unsuccessfully
53 // signaled. Note that the process trees can be stringified.
54 // TODO(benh): Allow excluding the root pid from stopping, killing,
55 // and continuing so as to provide a means for expressing "kill all of
56 // my children". This is non-trivial because of the current
57 // implementation.
59  pid_t pid,
60  int signal,
61  bool groups = false,
62  bool sessions = false)
63 {
65 
66  if (processes.isError()) {
67  return Error(processes.error());
68  }
69 
70  Result<Process> process = os::process(pid, processes.get());
71 
72  std::queue<pid_t> queue;
73 
74  // If the root process has already terminated we'll add in any pids
75  // that are in the process group originally led by pid or in the
76  // session originally led by pid, if instructed.
77  if (process.isNone()) {
78  foreach (const Process& _process, processes.get()) {
79  if (groups && _process.group == pid) {
80  queue.push(_process.pid);
81  } else if (sessions &&
82  _process.session.isSome() &&
83  _process.session.get() == pid) {
84  queue.push(_process.pid);
85  }
86  }
87 
88  // Root process is not running and no processes found in the
89  // process group or session so nothing we can do.
90  if (queue.empty()) {
91  return std::list<ProcessTree>();
92  }
93  } else {
94  // Start the traversal from pid as the root.
95  queue.push(pid);
96  }
97 
98  struct {
99  std::set<pid_t> pids;
100  std::set<pid_t> groups;
101  std::set<pid_t> sessions;
102  std::list<Process> processes;
103  } visited;
104 
105  // If we are following groups and/or sessions then we try and make
106  // the group and session of the parent process "already visited" so
107  // that we don't kill "up the tree". This can only be done if the
108  // process is present.
109  if (process.isSome() && (groups || sessions)) {
110  Option<Process> parent = os::process(process->parent, processes.get());
111 
112  if (parent.isSome()) {
113  if (groups) {
114  visited.groups.insert(parent->group);
115  }
116  if (sessions && parent->session.isSome()) {
117  visited.sessions.insert(parent->session.get());
118  }
119  }
120  }
121 
122  while (!queue.empty()) {
123  pid_t pid = queue.front();
124  queue.pop();
125 
126  if (visited.pids.count(pid) != 0) {
127  continue;
128  }
129 
130  // Make sure this process still exists.
131  process = os::process(pid);
132 
133  if (process.isError()) {
134  return Error(process.error());
135  } else if (process.isNone()) {
136  continue;
137  }
138 
139  // Stop the process to keep it from forking while we are killing
140  // it since a forked child might get re-parented by init and
141  // become impossible to find.
142  kill(pid, SIGSTOP);
143 
144  visited.pids.insert(pid);
145  visited.processes.push_back(process.get());
146 
147  // Now refresh the process list knowing that the current process
148  // can't fork any more children (since it's stopped).
149  processes = os::processes();
150 
151  if (processes.isError()) {
152  return Error(processes.error());
153  }
154 
155  // Enqueue the children for visiting.
156  foreach (pid_t child, os::children(pid, processes.get(), false)) {
157  queue.push(child);
158  }
159 
160  // Now "visit" the group and/or session of the current process.
161  if (groups) {
162  pid_t group = process->group;
163  if (visited.groups.count(group) == 0) {
164  foreach (const Process& process, processes.get()) {
165  if (process.group == group) {
166  queue.push(process.pid);
167  }
168  }
169  visited.groups.insert(group);
170  }
171  }
172 
173  // If we do not have a session for the process, it's likely
174  // because the process is a zombie on OS X. This implies it has
175  // not been reaped and thus is located somewhere in the tree we
176  // are trying to kill. Therefore, we should discover it from our
177  // tree traversal, or through its group (which is always present).
178  if (sessions && process->session.isSome()) {
179  pid_t session = process->session.get();
180  if (visited.sessions.count(session) == 0) {
181  foreach (const Process& process, processes.get()) {
182  if (process.session.isSome() && process.session.get() == session) {
183  queue.push(process.pid);
184  }
185  }
186  visited.sessions.insert(session);
187  }
188  }
189  }
190 
191  // Now that all processes are stopped, we send the signal.
192  foreach (pid_t pid, visited.pids) {
193  kill(pid, signal);
194  }
195 
196  // There is a concern that even though some process is stopped,
197  // sending a signal to any of its children may cause a SIGCLD to
198  // be delivered to it which wakes it up (or any other signal maybe
199  // delivered). However, from the Open Group standards on "Signal
200  // Concepts":
201  //
202  // "While a process is stopped, any additional signals that are
203  // sent to the process shall not be delivered until the process
204  // is continued, except SIGKILL which always terminates the
205  // receiving process."
206  //
207  // In practice, this is not what has been witnessed. Rather, a
208  // process that has been stopped will respond to SIGTERM, SIGINT,
209  // etc. That being said, we still continue the process below in the
210  // event that it doesn't terminate from the sending signal but it
211  // also doesn't get continued (as per the specifications above).
212 
213  // Try and continue the processes in case the signal is
214  // non-terminating but doesn't continue the process.
215  foreach (pid_t pid, visited.pids) {
216  kill(pid, SIGCONT);
217  }
218 
219  // Return the process trees representing the visited pids.
220  return pstrees(visited.pids, visited.processes);
221 }
222 
223 } // namespace os {
224 
225 #endif // __STOUT_OS_POSIX_KILLTREE_HPP__
Definition: errorbase.hpp:36
Definition: option.hpp:29
T & get()&
Definition: try.hpp:80
Definition: check.hpp:33
const pid_t group
Definition: process.hpp:55
Try< std::list< ProcessTree > > pstrees(const std::set< pid_t > &, const std::list< Process > &)
Definition: pstree.hpp:84
Try< std::list< Process > > processes()
Definition: os.hpp:184
Definition: posix_signalhandler.hpp:23
const Option< pid_t > session
Definition: process.hpp:56
Definition: check.hpp:30
bool isSome() const
Definition: option.hpp:116
DWORD pid_t
Definition: windows.hpp:181
Definition: process.hpp:32
const pid_t pid
Definition: process.hpp:53
Try< std::list< ProcessTree > > killtree(pid_t pid, int signal, bool groups=false, bool sessions=false)
Definition: killtree.hpp:58
const T & get() const &
Definition: option.hpp:119
static Try error(const E &e)
Definition: try.hpp:43
int kill(pid_t pid, int sig)
Definition: kill.hpp:21
Result< Process > process(pid_t pid)
Definition: freebsd.hpp:30
Definition: grp.hpp:26
bool isError() const
Definition: try.hpp:78
std::set< pid_t > children(pid_t, const std::list< Process > &, bool)
Definition: os.hpp:217
Definition: executor.hpp:48
Try< std::set< pid_t > > pids()
Definition: freebsd.hpp:62