1 /* BEGIN_COMMON_COPYRIGHT_HEADER
2 * (c)LGPL2+
3 *
4 * LXQt - a lightweight, Qt based, desktop toolset
5 * https://lxqt.org
6 *
7 * Copyright: 2021~ LXQt team
8 * Authors:
9 * Palo Kisa <palo.kisa@gmail.com>
10 *
11 * This program or library is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General
22 * Public License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301 USA
25 *
26 * END_COMMON_COPYRIGHT_HEADER */
27
28 #include "procreaper.h"
29 #include "log.h"
30 #if defined(Q_OS_LINUX)
31 #include <sys/prctl.h>
32 #include <proc/readproc.h>
33 #elif defined(Q_OS_FREEBSD)
34 #include <sys/sysctl.h>
35 #include <sys/procctl.h>
36 #include <libutil.h>
37 #include <sys/user.h>
38 #include <signal.h>
39 #endif
40 #include <unistd.h>
41 #include <cstring>
42 #include <cerrno>
43 #include <sys/wait.h>
44
45 /*-
46 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
47 *
48 * Copyright (c) 2007 Robert N. M. Watson
49 * Copyright (c) 2009 Ulf Lilleengen
50 * All rights reserved.
51 *
52 * Redistribution and use in source and binary forms, with or without
53 * modification, are permitted provided that the following conditions
54 * are met:
55 * 1. Redistributions of source code must retain the above copyright
56 * notice, this list of conditions and the following disclaimer.
57 * 2. Redistributions in binary form must reproduce the above copyright
58 * notice, this list of conditions and the following disclaimer in the
59 * documentation and/or other materials provided with the distribution.
60 *
61 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
62 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
63 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
64 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
65 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
66 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
67 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
68 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
69 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
70 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
71 * SUCH DAMAGE.
72 *
73 * $FreeBSD$
74 */
75
76 /*
77 * XXX DPORTS: This is NOT okay but given the time I have it should work and be
78 * enough for now
79 */
80
81 static int
kinfo_proc_compare(const void * a,const void * b)82 kinfo_proc_compare(const void *a, const void *b)
83 {
84 int i;
85
86 i = ((const struct kinfo_proc *)a)->kp_pid -
87 ((const struct kinfo_proc *)b)->kp_pid;
88 if (i != 0)
89 return (i);
90 i = ((const struct kinfo_proc *)a)->kp_lwp.kl_tid -
91 ((const struct kinfo_proc *)b)->kp_lwp.kl_tid;
92 return (i);
93 }
94
95 static void
kinfo_proc_sort(struct kinfo_proc * kipp,int count)96 kinfo_proc_sort(struct kinfo_proc *kipp, int count)
97 {
98
99 qsort(kipp, count, sizeof(*kipp), kinfo_proc_compare);
100 }
101
102 struct kinfo_proc *
kinfo_getallproc(int * cntp)103 kinfo_getallproc(int *cntp)
104 {
105 struct kinfo_proc *kipp;
106 size_t len;
107 int mib[3];
108
109 mib[0] = CTL_KERN;
110 mib[1] = KERN_PROC;
111 mib[2] = KERN_PROC_ALL;
112
113 len = 0;
114 if (::sysctl(mib, nitems(mib), NULL, &len, NULL, 0) < 0)
115 return (NULL);
116
117 kipp = (struct kinfo_proc *)malloc(len);
118 if (kipp == NULL)
119 return (NULL);
120
121 if (::sysctl(mib, nitems(mib), kipp, &len, NULL, 0) < 0)
122 goto bad;
123 if (len % sizeof(*kipp) != 0)
124 goto bad;
125 *cntp = len / sizeof(*kipp);
126 kinfo_proc_sort(kipp, len / sizeof(*kipp));
127 return (kipp);
128 bad:
129 *cntp = 0;
130 free(kipp);
131 return (NULL);
132 }
133
ProcReaper()134 ProcReaper::ProcReaper()
135 : mShouldRun{true}
136 {
137 #if defined(Q_OS_LINUX)
138 int result = prctl(PR_SET_CHILD_SUBREAPER, 1);
139 if (result != 0)
140 qCWarning(SESSION) << "Unable to to set PR_SET_CHILD_SUBREAPER, " << result << " - " << strerror(errno);
141 #elif defined(Q_OS_FREEBSD)
142 int result = procctl(P_PID, ::getpid(), PROC_REAP_ACQUIRE, nullptr);
143 if (result != 0)
144 qCWarning(SESSION) << "Unable to to set PROC_REAP_ACQUIRE, " << result << " - " << strerror(errno);
145 #endif
146 }
147
~ProcReaper()148 ProcReaper::~ProcReaper()
149 {
150 stop({});
151 }
152
run()153 void ProcReaper::run()
154 {
155 pid_t pid = 0;
156 while (true)
157 {
158 if (pid <= 0)
159 {
160 QMutexLocker guard{&mMutex};
161 mWait.wait(&mMutex, 1000); // 1 second
162 }
163
164 int status;
165 pid = ::waitpid(-1, &status, WNOHANG);
166 if (pid < 0)
167 {
168 if (ECHILD != errno)
169 qCDebug(SESSION) << "waitpid failed " << strerror(errno);
170 } else if (pid > 0)
171 {
172 if (WIFEXITED(status))
173 qCDebug(SESSION) << "Child process " << pid << " exited with status " << WEXITSTATUS(status);
174 else if (WIFSIGNALED(status))
175 qCDebug(SESSION) << "Child process " << pid << " terminated on signal " << WTERMSIG(status);
176 else
177 qCDebug(SESSION) << "Child process " << pid << " ended";
178 }
179 {
180 QMutexLocker guard{&mMutex};
181 if (!mShouldRun && pid <= 0)
182 break;
183 }
184 }
185 }
186
stop(const std::set<int64_t> & excludedPids)187 void ProcReaper::stop(const std::set<int64_t> & excludedPids)
188 {
189 {
190 QMutexLocker guard{&mMutex};
191 if (!mShouldRun)
192 return;
193 }
194 // send term to all children
195 const pid_t my_pid = ::getpid();
196 std::vector<pid_t> children;
197 #if defined(Q_OS_LINUX)
198 PROCTAB * proc_dir = ::openproc(PROC_FILLSTAT);
199 while (proc_t * proc = ::readproc(proc_dir, nullptr))
200 {
201 if (proc->ppid == my_pid)
202 {
203 children.push_back(proc->tgid);
204 }
205 ::freeproc(proc);
206 }
207 ::closeproc(proc_dir);
208 #elif defined(Q_OS_FREEBSD)
209 int cnt = 0;
210 if (kinfo_proc *proc_info = kinfo_getallproc(&cnt))
211 {
212 for (int i = 0; i < cnt; ++i)
213 {
214 if (proc_info[i].kp_ppid == my_pid)
215 {
216 children.push_back(proc_info[i].kp_pid);
217 }
218 }
219 free(proc_info);
220 }
221 #endif
222 for (auto const & child : children)
223 {
224 if (excludedPids.count(child) == 0)
225 {
226 qCDebug(SESSION) << "Seding TERM to child " << child;
227 ::kill(child, SIGTERM);
228 }
229 }
230 mWait.wakeAll();
231 {
232 QMutexLocker guard{&mMutex};
233 mShouldRun = false;
234 }
235 QThread::wait(5000); // 5 seconds
236 }
237