1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include "clean-ipc.h"
32 #include "dirent-util.h"
35 #include "formats-util.h"
36 #include "string-util.h"
40 static int clean_sysvipc_shm(uid_t delete_uid) {
41 _cleanup_fclose_ FILE *f = NULL;
46 f = fopen("/proc/sysvipc/shm", "re");
51 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
54 FOREACH_LINE(line, f, goto fail) {
68 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
69 &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
75 if (uid != delete_uid)
78 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
80 /* Ignore entries that are already deleted */
81 if (errno == EIDRM || errno == EINVAL)
84 ret = log_warning_errno(errno,
85 "Failed to remove SysV shared memory segment %i: %m",
93 return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
96 static int clean_sysvipc_sem(uid_t delete_uid) {
97 _cleanup_fclose_ FILE *f = NULL;
102 f = fopen("/proc/sysvipc/sem", "re");
107 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
110 FOREACH_LINE(line, f, goto fail) {
122 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
123 &semid, &uid, &gid, &cuid, &cgid) != 5)
126 if (uid != delete_uid)
129 if (semctl(semid, 0, IPC_RMID) < 0) {
131 /* Ignore entries that are already deleted */
132 if (errno == EIDRM || errno == EINVAL)
135 ret = log_warning_errno(errno,
136 "Failed to remove SysV semaphores object %i: %m",
144 return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
147 static int clean_sysvipc_msg(uid_t delete_uid) {
148 _cleanup_fclose_ FILE *f = NULL;
153 f = fopen("/proc/sysvipc/msg", "re");
158 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
161 FOREACH_LINE(line, f, goto fail) {
174 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
175 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
178 if (uid != delete_uid)
181 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
183 /* Ignore entries that are already deleted */
184 if (errno == EIDRM || errno == EINVAL)
187 ret = log_warning_errno(errno,
188 "Failed to remove SysV message queue %i: %m",
196 return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
199 static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
205 FOREACH_DIRENT(de, dir, goto fail) {
208 if (STR_IN_SET(de->d_name, "..", "."))
211 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
215 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
220 if (st.st_uid != uid)
223 if (S_ISDIR(st.st_mode)) {
224 _cleanup_closedir_ DIR *kid;
226 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
228 if (errno != ENOENT) {
229 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
233 r = clean_posix_shm_internal(kid, uid);
238 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
243 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
248 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
253 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
262 log_warning_errno(errno, "Failed to read /dev/shm: %m");
266 static int clean_posix_shm(uid_t uid) {
267 _cleanup_closedir_ DIR *dir = NULL;
269 dir = opendir("/dev/shm");
274 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
277 return clean_posix_shm_internal(dir, uid);
280 /// UNNEEDED by elogind
282 static int clean_posix_mq(uid_t uid) {
283 _cleanup_closedir_ DIR *dir = NULL;
287 dir = opendir("/dev/mqueue");
292 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
295 FOREACH_DIRENT(de, dir, goto fail) {
297 char fn[1+strlen(de->d_name)+1];
299 if (STR_IN_SET(de->d_name, "..", "."))
302 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
306 ret = log_warning_errno(errno,
307 "Failed to stat() MQ segment %s: %m",
312 if (st.st_uid != uid)
316 strcpy(fn+1, de->d_name);
318 if (mq_unlink(fn) < 0) {
322 ret = log_warning_errno(errno,
323 "Failed to unlink POSIX message queue %s: %m",
331 return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
335 int clean_ipc(uid_t uid) {
338 /* Refuse to clean IPC of the root and system users */
339 if (uid <= SYSTEM_UID_MAX)
342 r = clean_sysvipc_shm(uid);
346 r = clean_sysvipc_sem(uid);
350 r = clean_sysvipc_msg(uid);
354 r = clean_posix_shm(uid);
358 /// elogind does not use mq_open anywhere
360 r = clean_posix_mq(uid);