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/>.
33 #include "clean-ipc.h"
35 static int clean_sysvipc_shm(uid_t delete_uid) {
36 _cleanup_fclose_ FILE *f = NULL;
41 f = fopen("/proc/sysvipc/shm", "re");
46 log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
50 FOREACH_LINE(line, f, goto fail) {
64 if (sscanf(line, "%*i %i %*o %*u " PID_FMT " " PID_FMT " %u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
65 &shmid, &cpid, &lpid, &n_attached, &uid, &gid, &cuid, &cgid) != 8)
71 if (uid != delete_uid)
74 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
76 /* Ignore entries that are already deleted */
77 if (errno == EIDRM || errno == EINVAL)
80 log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid);
88 log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
92 static int clean_sysvipc_sem(uid_t delete_uid) {
93 _cleanup_fclose_ FILE *f = NULL;
98 f = fopen("/proc/sysvipc/sem", "re");
103 log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
107 FOREACH_LINE(line, f, goto fail) {
119 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
120 &semid, &uid, &gid, &cuid, &cgid) != 5)
123 if (uid != delete_uid)
126 if (semctl(semid, 0, IPC_RMID) < 0) {
128 /* Ignore entries that are already deleted */
129 if (errno == EIDRM || errno == EINVAL)
132 log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid);
140 log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
144 static int clean_sysvipc_msg(uid_t delete_uid) {
145 _cleanup_fclose_ FILE *f = NULL;
150 f = fopen("/proc/sysvipc/msg", "re");
155 log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
159 FOREACH_LINE(line, f, goto fail) {
172 if (sscanf(line, "%*i %i %*o %*u %*u " PID_FMT " " PID_FMT " " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
173 &msgid, &cpid, &lpid, &uid, &gid, &cuid, &cgid) != 7)
176 if (uid != delete_uid)
179 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
181 /* Ignore entries that are already deleted */
182 if (errno == EIDRM || errno == EINVAL)
185 log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid);
193 log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
197 static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
203 FOREACH_DIRENT(de, dir, goto fail) {
206 if (STR_IN_SET(de->d_name, "..", "."))
209 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
213 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
218 if (st.st_uid != uid)
221 if (S_ISDIR(st.st_mode)) {
222 _cleanup_closedir_ DIR *kid;
224 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
226 if (errno != ENOENT) {
227 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
231 r = clean_posix_shm_internal(kid, uid);
236 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
241 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
246 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
251 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
260 log_warning_errno(errno, "Failed to read /dev/shm: %m");
264 static int clean_posix_shm(uid_t uid) {
265 _cleanup_closedir_ DIR *dir = NULL;
267 dir = opendir("/dev/shm");
272 log_warning_errno(errno, "Failed to open /dev/shm: %m");
276 return clean_posix_shm_internal(dir, uid);
279 static int clean_posix_mq(uid_t uid) {
280 _cleanup_closedir_ DIR *dir = NULL;
284 dir = opendir("/dev/mqueue");
289 log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
293 FOREACH_DIRENT(de, dir, goto fail) {
295 char fn[1+strlen(de->d_name)+1];
297 if (STR_IN_SET(de->d_name, "..", "."))
300 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
304 log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name);
309 if (st.st_uid != uid)
313 strcpy(fn+1, de->d_name);
315 if (mq_unlink(fn) < 0) {
319 log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn);
327 log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
331 int clean_ipc(uid_t uid) {
334 /* Refuse to clean IPC of the root and system users */
335 if (uid <= SYSTEM_UID_MAX)
338 r = clean_sysvipc_shm(uid);
342 r = clean_sysvipc_sem(uid);
346 r = clean_sysvipc_msg(uid);
350 r = clean_posix_shm(uid);
354 r = clean_posix_mq(uid);