]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/clean-ipc.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
16 #include "clean-ipc.h"
17 #include "dirent-util.h"
20 #include "format-util.h"
23 #include "string-util.h"
25 #include "user-util.h"
27 static bool match_uid_gid(uid_t subject_uid
, gid_t subject_gid
, uid_t delete_uid
, gid_t delete_gid
) {
29 if (uid_is_valid(delete_uid
) && subject_uid
== delete_uid
)
32 if (gid_is_valid(delete_gid
) && subject_gid
== delete_gid
)
38 static int clean_sysvipc_shm(uid_t delete_uid
, gid_t delete_gid
, bool rm
) {
39 _cleanup_fclose_
FILE *f
= NULL
;
43 f
= fopen("/proc/sysvipc/shm", "re");
48 return log_warning_errno(errno
, "Failed to open /proc/sysvipc/shm: %m");
52 _cleanup_free_
char *line
= NULL
;
59 r
= read_line(f
, LONG_LINE_MAX
, &line
);
61 return log_warning_errno(errno
, "Failed to read /proc/sysvipc/shm: %m");
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)
77 if (!match_uid_gid(uid
, gid
, delete_uid
, delete_gid
))
83 if (shmctl(shmid
, IPC_RMID
, NULL
) < 0) {
85 /* Ignore entries that are already deleted */
86 if (IN_SET(errno
, EIDRM
, EINVAL
))
89 ret
= log_warning_errno(errno
,
90 "Failed to remove SysV shared memory segment %i: %m",
93 log_debug("Removed SysV shared memory segment %i.", shmid
);
102 static int clean_sysvipc_sem(uid_t delete_uid
, gid_t delete_gid
, bool rm
) {
103 _cleanup_fclose_
FILE *f
= NULL
;
107 f
= fopen("/proc/sysvipc/sem", "re");
112 return log_warning_errno(errno
, "Failed to open /proc/sysvipc/sem: %m");
116 _cleanup_free_
char *line
= NULL
;
121 r
= read_line(f
, LONG_LINE_MAX
, &line
);
123 return log_warning_errno(r
, "Failed to read /proc/sysvipc/sem: %m");
132 if (sscanf(line
, "%*i %i %*o %*u " UID_FMT
" " GID_FMT
" " UID_FMT
" " GID_FMT
,
133 &semid
, &uid
, &gid
, &cuid
, &cgid
) != 5)
136 if (!match_uid_gid(uid
, gid
, delete_uid
, delete_gid
))
142 if (semctl(semid
, 0, IPC_RMID
) < 0) {
144 /* Ignore entries that are already deleted */
145 if (IN_SET(errno
, EIDRM
, EINVAL
))
148 ret
= log_warning_errno(errno
,
149 "Failed to remove SysV semaphores object %i: %m",
152 log_debug("Removed SysV semaphore %i.", semid
);
161 static int clean_sysvipc_msg(uid_t delete_uid
, gid_t delete_gid
, bool rm
) {
162 _cleanup_fclose_
FILE *f
= NULL
;
166 f
= fopen("/proc/sysvipc/msg", "re");
171 return log_warning_errno(errno
, "Failed to open /proc/sysvipc/msg: %m");
175 _cleanup_free_
char *line
= NULL
;
181 r
= read_line(f
, LONG_LINE_MAX
, &line
);
183 return log_warning_errno(r
, "Failed to read /proc/sysvipc/msg: %m");
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)
196 if (!match_uid_gid(uid
, gid
, delete_uid
, delete_gid
))
202 if (msgctl(msgid
, IPC_RMID
, NULL
) < 0) {
204 /* Ignore entries that are already deleted */
205 if (IN_SET(errno
, EIDRM
, EINVAL
))
208 ret
= log_warning_errno(errno
,
209 "Failed to remove SysV message queue %i: %m",
212 log_debug("Removed SysV message queue %i.", msgid
);
221 static int clean_posix_shm_internal(const char *dirname
, DIR *dir
, uid_t uid
, gid_t gid
, bool rm
) {
227 FOREACH_DIRENT_ALL(de
, dir
, goto fail
) {
230 if (dot_or_dot_dot(de
->d_name
))
233 if (fstatat(dirfd(dir
), de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
237 ret
= log_warning_errno(errno
, "Failed to stat() POSIX shared memory segment %s/%s: %m",
238 dirname
, de
->d_name
);
242 if (S_ISDIR(st
.st_mode
)) {
243 _cleanup_closedir_
DIR *kid
;
245 kid
= xopendirat(dirfd(dir
), de
->d_name
, O_NOFOLLOW
|O_NOATIME
);
248 ret
= log_warning_errno(errno
, "Failed to enter shared memory directory %s/%s: %m",
249 dirname
, de
->d_name
);
251 r
= clean_posix_shm_internal(de
->d_name
, kid
, uid
, gid
, rm
);
256 if (!match_uid_gid(st
.st_uid
, st
.st_gid
, uid
, gid
))
262 if (unlinkat(dirfd(dir
), de
->d_name
, AT_REMOVEDIR
) < 0) {
267 ret
= log_warning_errno(errno
, "Failed to remove POSIX shared memory directory %s/%s: %m",
268 dirname
, de
->d_name
);
270 log_debug("Removed POSIX shared memory directory %s", de
->d_name
);
276 if (!match_uid_gid(st
.st_uid
, st
.st_gid
, uid
, gid
))
282 if (unlinkat(dirfd(dir
), de
->d_name
, 0) < 0) {
287 ret
= log_warning_errno(errno
, "Failed to remove POSIX shared memory segment %s: %m", de
->d_name
);
289 log_debug("Removed POSIX shared memory segment %s", de
->d_name
);
299 return log_warning_errno(errno
, "Failed to read /dev/shm: %m");
302 static int clean_posix_shm(uid_t uid
, gid_t gid
, bool rm
) {
303 _cleanup_closedir_
DIR *dir
= NULL
;
305 dir
= opendir("/dev/shm");
310 return log_warning_errno(errno
, "Failed to open /dev/shm: %m");
313 return clean_posix_shm_internal("/dev/shm", dir
, uid
, gid
, rm
);
316 static int clean_posix_mq(uid_t uid
, gid_t gid
, bool rm
) {
317 _cleanup_closedir_
DIR *dir
= NULL
;
321 dir
= opendir("/dev/mqueue");
326 return log_warning_errno(errno
, "Failed to open /dev/mqueue: %m");
329 FOREACH_DIRENT_ALL(de
, dir
, goto fail
) {
331 char fn
[1+strlen(de
->d_name
)+1];
333 if (dot_or_dot_dot(de
->d_name
))
336 if (fstatat(dirfd(dir
), de
->d_name
, &st
, AT_SYMLINK_NOFOLLOW
) < 0) {
340 ret
= log_warning_errno(errno
,
341 "Failed to stat() MQ segment %s: %m",
346 if (!match_uid_gid(st
.st_uid
, st
.st_gid
, uid
, gid
))
353 strcpy(fn
+1, de
->d_name
);
355 if (mq_unlink(fn
) < 0) {
359 ret
= log_warning_errno(errno
,
360 "Failed to unlink POSIX message queue %s: %m",
363 log_debug("Removed POSIX message queue %s", fn
);
372 return log_warning_errno(errno
, "Failed to read /dev/mqueue: %m");
375 int clean_ipc_internal(uid_t uid
, gid_t gid
, bool rm
) {
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.
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.
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. */
401 /* Anything to do? */
402 if (!uid_is_valid(uid
) && !gid_is_valid(gid
))
405 r
= clean_sysvipc_shm(uid
, gid
, rm
);
413 r
= clean_sysvipc_sem(uid
, gid
, rm
);
421 r
= clean_sysvipc_msg(uid
, gid
, rm
);
429 r
= clean_posix_shm(uid
, gid
, rm
);
437 r
= clean_posix_mq(uid
, gid
, rm
);
448 int clean_ipc_by_uid(uid_t uid
) {
449 return clean_ipc_internal(uid
, GID_INVALID
, true);
452 int clean_ipc_by_gid(gid_t gid
) {
453 return clean_ipc_internal(UID_INVALID
, gid
, true);