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