]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/clean-ipc.c
Merge pull request #11602 from vesajaaskelainen/dbus-reboot-with-parameters
[thirdparty/systemd.git] / src / shared / clean-ipc.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
66cdd0f2 2
07630cea 3#include <dirent.h>
a8fbdf54 4#include <errno.h>
07630cea 5#include <fcntl.h>
a8fbdf54 6#include <limits.h>
07630cea 7#include <mqueue.h>
a8fbdf54
TA
8#include <stdbool.h>
9#include <stdio.h>
10#include <string.h>
66cdd0f2 11#include <sys/ipc.h>
66cdd0f2 12#include <sys/msg.h>
07630cea
LP
13#include <sys/sem.h>
14#include <sys/shm.h>
66cdd0f2 15#include <sys/stat.h>
a8fbdf54 16#include <unistd.h>
66cdd0f2 17
3ffd4af2 18#include "clean-ipc.h"
cf0fbc49 19#include "dirent-util.h"
3ffd4af2 20#include "fd-util.h"
0d39fa9c 21#include "fileio.h"
f97b34a6 22#include "format-util.h"
a8fbdf54
TA
23#include "log.h"
24#include "macro.h"
07630cea 25#include "string-util.h"
66cdd0f2 26#include "strv.h"
00d9ef85 27#include "user-util.h"
66cdd0f2 28
00d9ef85
LP
29static bool match_uid_gid(uid_t subject_uid, gid_t subject_gid, uid_t delete_uid, gid_t delete_gid) {
30
31 if (uid_is_valid(delete_uid) && subject_uid == delete_uid)
32 return true;
33
34 if (gid_is_valid(delete_gid) && subject_gid == delete_gid)
35 return true;
36
37 return false;
38}
39
98e4fcec 40static int clean_sysvipc_shm(uid_t delete_uid, gid_t delete_gid, bool rm) {
66cdd0f2 41 _cleanup_fclose_ FILE *f = NULL;
66cdd0f2 42 bool first = true;
d5b3c07d 43 int ret = 0, r;
66cdd0f2
LP
44
45 f = fopen("/proc/sysvipc/shm", "re");
46 if (!f) {
47 if (errno == ENOENT)
48 return 0;
49
e1427b13 50 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
66cdd0f2
LP
51 }
52
d5b3c07d
LP
53 for (;;) {
54 _cleanup_free_ char *line = NULL;
66cdd0f2
LP
55 unsigned n_attached;
56 pid_t cpid, lpid;
57 uid_t uid, cuid;
58 gid_t gid, cgid;
59 int shmid;
60
d5b3c07d
LP
61 r = read_line(f, LONG_LINE_MAX, &line);
62 if (r < 0)
63 return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
64 if (r == 0)
65 break;
66
66cdd0f2
LP
67 if (first) {
68 first = false;
69 continue;
70 }
71
66cdd0f2
LP
72 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
73 &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
74 continue;
75
76 if (n_attached > 0)
77 continue;
78
00d9ef85 79 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
66cdd0f2
LP
80 continue;
81
98e4fcec
LP
82 if (!rm)
83 return 1;
84
66cdd0f2
LP
85 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
86
87 /* Ignore entries that are already deleted */
3742095b 88 if (IN_SET(errno, EIDRM, EINVAL))
66cdd0f2
LP
89 continue;
90
94c156cd
LP
91 ret = log_warning_errno(errno,
92 "Failed to remove SysV shared memory segment %i: %m",
93 shmid);
98e4fcec 94 } else {
8a384842 95 log_debug("Removed SysV shared memory segment %i.", shmid);
98e4fcec
LP
96 if (ret == 0)
97 ret = 1;
98 }
66cdd0f2
LP
99 }
100
101 return ret;
66cdd0f2
LP
102}
103
98e4fcec 104static int clean_sysvipc_sem(uid_t delete_uid, gid_t delete_gid, bool rm) {
66cdd0f2 105 _cleanup_fclose_ FILE *f = NULL;
66cdd0f2 106 bool first = true;
d5b3c07d 107 int ret = 0, r;
66cdd0f2
LP
108
109 f = fopen("/proc/sysvipc/sem", "re");
110 if (!f) {
111 if (errno == ENOENT)
112 return 0;
113
e1427b13 114 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
66cdd0f2
LP
115 }
116
d5b3c07d
LP
117 for (;;) {
118 _cleanup_free_ char *line = NULL;
66cdd0f2
LP
119 uid_t uid, cuid;
120 gid_t gid, cgid;
121 int semid;
122
d5b3c07d
LP
123 r = read_line(f, LONG_LINE_MAX, &line);
124 if (r < 0)
125 return log_warning_errno(r, "Failed to read /proc/sysvipc/sem: %m");
126 if (r == 0)
127 break;
128
66cdd0f2
LP
129 if (first) {
130 first = false;
131 continue;
132 }
133
66cdd0f2
LP
134 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
135 &semid, &uid, &gid, &cuid, &cgid) != 5)
136 continue;
137
00d9ef85 138 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
66cdd0f2
LP
139 continue;
140
98e4fcec
LP
141 if (!rm)
142 return 1;
143
66cdd0f2
LP
144 if (semctl(semid, 0, IPC_RMID) < 0) {
145
146 /* Ignore entries that are already deleted */
3742095b 147 if (IN_SET(errno, EIDRM, EINVAL))
66cdd0f2
LP
148 continue;
149
94c156cd
LP
150 ret = log_warning_errno(errno,
151 "Failed to remove SysV semaphores object %i: %m",
152 semid);
98e4fcec 153 } else {
8a384842 154 log_debug("Removed SysV semaphore %i.", semid);
98e4fcec
LP
155 if (ret == 0)
156 ret = 1;
157 }
66cdd0f2
LP
158 }
159
160 return ret;
66cdd0f2
LP
161}
162
98e4fcec 163static int clean_sysvipc_msg(uid_t delete_uid, gid_t delete_gid, bool rm) {
66cdd0f2 164 _cleanup_fclose_ FILE *f = NULL;
66cdd0f2 165 bool first = true;
d5b3c07d 166 int ret = 0, r;
66cdd0f2
LP
167
168 f = fopen("/proc/sysvipc/msg", "re");
169 if (!f) {
170 if (errno == ENOENT)
171 return 0;
172
e1427b13 173 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
66cdd0f2
LP
174 }
175
d5b3c07d
LP
176 for (;;) {
177 _cleanup_free_ char *line = NULL;
66cdd0f2
LP
178 uid_t uid, cuid;
179 gid_t gid, cgid;
180 pid_t cpid, lpid;
181 int msgid;
182
d5b3c07d
LP
183 r = read_line(f, LONG_LINE_MAX, &line);
184 if (r < 0)
185 return log_warning_errno(r, "Failed to read /proc/sysvipc/msg: %m");
186 if (r == 0)
187 break;
188
66cdd0f2
LP
189 if (first) {
190 first = false;
191 continue;
192 }
193
66cdd0f2
LP
194 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
195 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
196 continue;
197
00d9ef85 198 if (!match_uid_gid(uid, gid, delete_uid, delete_gid))
66cdd0f2
LP
199 continue;
200
98e4fcec
LP
201 if (!rm)
202 return 1;
203
66cdd0f2
LP
204 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
205
206 /* Ignore entries that are already deleted */
3742095b 207 if (IN_SET(errno, EIDRM, EINVAL))
66cdd0f2
LP
208 continue;
209
94c156cd
LP
210 ret = log_warning_errno(errno,
211 "Failed to remove SysV message queue %i: %m",
212 msgid);
98e4fcec 213 } else {
8a384842 214 log_debug("Removed SysV message queue %i.", msgid);
98e4fcec
LP
215 if (ret == 0)
216 ret = 1;
217 }
66cdd0f2
LP
218 }
219
220 return ret;
66cdd0f2
LP
221}
222
98e4fcec 223static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid, bool rm) {
66cdd0f2
LP
224 struct dirent *de;
225 int ret = 0, r;
226
227 assert(dir);
228
91f2048c 229 FOREACH_DIRENT_ALL(de, dir, goto fail) {
66cdd0f2
LP
230 struct stat st;
231
49bfc877 232 if (dot_or_dot_dot(de->d_name))
66cdd0f2
LP
233 continue;
234
235 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
236 if (errno == ENOENT)
237 continue;
238
3db99289 239 ret = log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
240 continue;
241 }
242
66cdd0f2
LP
243 if (S_ISDIR(st.st_mode)) {
244 _cleanup_closedir_ DIR *kid;
245
246 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
247 if (!kid) {
3db99289
LP
248 if (errno != ENOENT)
249 ret = log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
66cdd0f2 250 } else {
98e4fcec 251 r = clean_posix_shm_internal(kid, uid, gid, rm);
66cdd0f2
LP
252 if (r < 0)
253 ret = r;
254 }
255
98e4fcec
LP
256 if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
257 continue;
258
259 if (!rm)
260 return 1;
261
66cdd0f2
LP
262 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
263
264 if (errno == ENOENT)
265 continue;
266
3db99289 267 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
98e4fcec 268 } else {
8a384842 269 log_debug("Removed POSIX shared memory directory %s", de->d_name);
98e4fcec
LP
270 if (ret == 0)
271 ret = 1;
272 }
66cdd0f2
LP
273 } else {
274
98e4fcec
LP
275 if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
276 continue;
277
278 if (!rm)
279 return 1;
280
66cdd0f2
LP
281 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
282
283 if (errno == ENOENT)
284 continue;
285
3db99289 286 ret = log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
98e4fcec 287 } else {
8a384842 288 log_debug("Removed POSIX shared memory segment %s", de->d_name);
98e4fcec
LP
289 if (ret == 0)
290 ret = 1;
291 }
66cdd0f2
LP
292 }
293 }
294
295 return ret;
296
297fail:
3db99289 298 return log_warning_errno(errno, "Failed to read /dev/shm: %m");
66cdd0f2
LP
299}
300
98e4fcec 301static int clean_posix_shm(uid_t uid, gid_t gid, bool rm) {
66cdd0f2
LP
302 _cleanup_closedir_ DIR *dir = NULL;
303
304 dir = opendir("/dev/shm");
305 if (!dir) {
306 if (errno == ENOENT)
307 return 0;
308
e1427b13 309 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
66cdd0f2
LP
310 }
311
98e4fcec 312 return clean_posix_shm_internal(dir, uid, gid, rm);
66cdd0f2
LP
313}
314
98e4fcec 315static int clean_posix_mq(uid_t uid, gid_t gid, bool rm) {
66cdd0f2
LP
316 _cleanup_closedir_ DIR *dir = NULL;
317 struct dirent *de;
318 int ret = 0;
319
320 dir = opendir("/dev/mqueue");
321 if (!dir) {
322 if (errno == ENOENT)
323 return 0;
324
e1427b13 325 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
66cdd0f2
LP
326 }
327
91f2048c 328 FOREACH_DIRENT_ALL(de, dir, goto fail) {
66cdd0f2
LP
329 struct stat st;
330 char fn[1+strlen(de->d_name)+1];
331
49bfc877 332 if (dot_or_dot_dot(de->d_name))
66cdd0f2
LP
333 continue;
334
335 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
336 if (errno == ENOENT)
337 continue;
338
94c156cd
LP
339 ret = log_warning_errno(errno,
340 "Failed to stat() MQ segment %s: %m",
341 de->d_name);
66cdd0f2
LP
342 continue;
343 }
344
00d9ef85 345 if (!match_uid_gid(st.st_uid, st.st_gid, uid, gid))
66cdd0f2
LP
346 continue;
347
98e4fcec
LP
348 if (!rm)
349 return 1;
350
66cdd0f2
LP
351 fn[0] = '/';
352 strcpy(fn+1, de->d_name);
353
354 if (mq_unlink(fn) < 0) {
355 if (errno == ENOENT)
356 continue;
357
94c156cd
LP
358 ret = log_warning_errno(errno,
359 "Failed to unlink POSIX message queue %s: %m",
360 fn);
98e4fcec 361 } else {
8a384842 362 log_debug("Removed POSIX message queue %s", fn);
98e4fcec
LP
363 if (ret == 0)
364 ret = 1;
365 }
66cdd0f2
LP
366 }
367
368 return ret;
369
370fail:
e1427b13 371 return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
66cdd0f2
LP
372}
373
98e4fcec 374int clean_ipc_internal(uid_t uid, gid_t gid, bool rm) {
66cdd0f2
LP
375 int ret = 0, r;
376
98e4fcec
LP
377 /* If 'rm' is true, clean all IPC objects owned by either the specified UID or the specified GID. Return the
378 * last error encountered or == 0 if no matching IPC objects have been found or > 0 if matching IPC objects
379 * have been found and have been removed.
380 *
381 * If 'rm' is false, just search for IPC objects owned by either the specified UID or the specified GID. In
382 * this case we return < 0 on error, > 0 if we found a matching object, == 0 if we didn't.
383 *
384 * As special rule: if UID/GID is specified as root we'll silently not clean up things, and always claim that
385 * there are IPC objects for it. */
386
387 if (uid == 0) {
388 if (!rm)
389 return 1;
390
391 uid = UID_INVALID;
392 }
393 if (gid == 0) {
394 if (!rm)
395 return 1;
396
397 gid = GID_INVALID;
398 }
399
00d9ef85
LP
400 /* Anything to do? */
401 if (!uid_is_valid(uid) && !gid_is_valid(gid))
66cdd0f2
LP
402 return 0;
403
98e4fcec
LP
404 r = clean_sysvipc_shm(uid, gid, rm);
405 if (r != 0) {
406 if (!rm)
407 return r;
408 if (ret == 0)
409 ret = r;
410 }
66cdd0f2 411
98e4fcec
LP
412 r = clean_sysvipc_sem(uid, gid, rm);
413 if (r != 0) {
414 if (!rm)
415 return r;
416 if (ret == 0)
417 ret = r;
418 }
66cdd0f2 419
98e4fcec
LP
420 r = clean_sysvipc_msg(uid, gid, rm);
421 if (r != 0) {
422 if (!rm)
423 return r;
424 if (ret == 0)
425 ret = r;
426 }
66cdd0f2 427
98e4fcec
LP
428 r = clean_posix_shm(uid, gid, rm);
429 if (r != 0) {
430 if (!rm)
431 return r;
432 if (ret == 0)
433 ret = r;
434 }
66cdd0f2 435
98e4fcec
LP
436 r = clean_posix_mq(uid, gid, rm);
437 if (r != 0) {
438 if (!rm)
439 return r;
440 if (ret == 0)
441 ret = r;
442 }
66cdd0f2
LP
443
444 return ret;
445}
00d9ef85
LP
446
447int clean_ipc_by_uid(uid_t uid) {
98e4fcec 448 return clean_ipc_internal(uid, GID_INVALID, true);
00d9ef85
LP
449}
450
451int clean_ipc_by_gid(gid_t gid) {
98e4fcec 452 return clean_ipc_internal(UID_INVALID, gid, true);
00d9ef85 453}