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