]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/clean-ipc.c
Merge pull request #1989 from keszybz/filetriggers-v2
[thirdparty/systemd.git] / src / shared / clean-ipc.c
CommitLineData
66cdd0f2
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
07630cea
LP
22#include <dirent.h>
23#include <fcntl.h>
24#include <mqueue.h>
66cdd0f2 25#include <sys/ipc.h>
66cdd0f2 26#include <sys/msg.h>
07630cea
LP
27#include <sys/sem.h>
28#include <sys/shm.h>
66cdd0f2 29#include <sys/stat.h>
66cdd0f2 30
3ffd4af2 31#include "clean-ipc.h"
cf0fbc49 32#include "dirent-util.h"
3ffd4af2 33#include "fd-util.h"
0d39fa9c 34#include "fileio.h"
6482f626 35#include "formats-util.h"
07630cea 36#include "string-util.h"
66cdd0f2 37#include "strv.h"
07630cea 38#include "util.h"
66cdd0f2
LP
39
40static int clean_sysvipc_shm(uid_t delete_uid) {
41 _cleanup_fclose_ FILE *f = NULL;
42 char line[LINE_MAX];
43 bool first = true;
44 int ret = 0;
45
46 f = fopen("/proc/sysvipc/shm", "re");
47 if (!f) {
48 if (errno == ENOENT)
49 return 0;
50
e1427b13 51 return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m");
66cdd0f2
LP
52 }
53
54 FOREACH_LINE(line, f, goto fail) {
55 unsigned n_attached;
56 pid_t cpid, lpid;
57 uid_t uid, cuid;
58 gid_t gid, cgid;
59 int shmid;
60
61 if (first) {
62 first = false;
63 continue;
64 }
65
66 truncate_nl(line);
67
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)
70 continue;
71
72 if (n_attached > 0)
73 continue;
74
75 if (uid != delete_uid)
76 continue;
77
78 if (shmctl(shmid, IPC_RMID, NULL) < 0) {
79
80 /* Ignore entries that are already deleted */
81 if (errno == EIDRM || errno == EINVAL)
82 continue;
83
94c156cd
LP
84 ret = log_warning_errno(errno,
85 "Failed to remove SysV shared memory segment %i: %m",
86 shmid);
66cdd0f2
LP
87 }
88 }
89
90 return ret;
91
92fail:
e1427b13 93 return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m");
66cdd0f2
LP
94}
95
96static int clean_sysvipc_sem(uid_t delete_uid) {
97 _cleanup_fclose_ FILE *f = NULL;
98 char line[LINE_MAX];
99 bool first = true;
100 int ret = 0;
101
102 f = fopen("/proc/sysvipc/sem", "re");
103 if (!f) {
104 if (errno == ENOENT)
105 return 0;
106
e1427b13 107 return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m");
66cdd0f2
LP
108 }
109
110 FOREACH_LINE(line, f, goto fail) {
111 uid_t uid, cuid;
112 gid_t gid, cgid;
113 int semid;
114
115 if (first) {
116 first = false;
117 continue;
118 }
119
120 truncate_nl(line);
121
122 if (sscanf(line, "%*i %i %*o %*u " UID_FMT " " GID_FMT " " UID_FMT " " GID_FMT,
123 &semid, &uid, &gid, &cuid, &cgid) != 5)
124 continue;
125
126 if (uid != delete_uid)
127 continue;
128
129 if (semctl(semid, 0, IPC_RMID) < 0) {
130
131 /* Ignore entries that are already deleted */
132 if (errno == EIDRM || errno == EINVAL)
133 continue;
134
94c156cd
LP
135 ret = log_warning_errno(errno,
136 "Failed to remove SysV semaphores object %i: %m",
137 semid);
66cdd0f2
LP
138 }
139 }
140
141 return ret;
142
143fail:
e1427b13 144 return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m");
66cdd0f2
LP
145}
146
147static int clean_sysvipc_msg(uid_t delete_uid) {
148 _cleanup_fclose_ FILE *f = NULL;
149 char line[LINE_MAX];
150 bool first = true;
151 int ret = 0;
152
153 f = fopen("/proc/sysvipc/msg", "re");
154 if (!f) {
155 if (errno == ENOENT)
156 return 0;
157
e1427b13 158 return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m");
66cdd0f2
LP
159 }
160
161 FOREACH_LINE(line, f, goto fail) {
162 uid_t uid, cuid;
163 gid_t gid, cgid;
164 pid_t cpid, lpid;
165 int msgid;
166
167 if (first) {
168 first = false;
169 continue;
170 }
171
172 truncate_nl(line);
173
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)
176 continue;
177
178 if (uid != delete_uid)
179 continue;
180
181 if (msgctl(msgid, IPC_RMID, NULL) < 0) {
182
183 /* Ignore entries that are already deleted */
184 if (errno == EIDRM || errno == EINVAL)
185 continue;
186
94c156cd
LP
187 ret = log_warning_errno(errno,
188 "Failed to remove SysV message queue %i: %m",
189 msgid);
66cdd0f2
LP
190 }
191 }
192
193 return ret;
194
195fail:
e1427b13 196 return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m");
66cdd0f2
LP
197}
198
199static int clean_posix_shm_internal(DIR *dir, uid_t uid) {
200 struct dirent *de;
201 int ret = 0, r;
202
203 assert(dir);
204
205 FOREACH_DIRENT(de, dir, goto fail) {
206 struct stat st;
207
208 if (STR_IN_SET(de->d_name, "..", "."))
209 continue;
210
211 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
212 if (errno == ENOENT)
213 continue;
214
56f64d95 215 log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
216 ret = -errno;
217 continue;
218 }
219
220 if (st.st_uid != uid)
221 continue;
222
223 if (S_ISDIR(st.st_mode)) {
224 _cleanup_closedir_ DIR *kid;
225
226 kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME);
227 if (!kid) {
228 if (errno != ENOENT) {
56f64d95 229 log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
230 ret = -errno;
231 }
232 } else {
233 r = clean_posix_shm_internal(kid, uid);
234 if (r < 0)
235 ret = r;
236 }
237
238 if (unlinkat(dirfd(dir), de->d_name, AT_REMOVEDIR) < 0) {
239
240 if (errno == ENOENT)
241 continue;
242
56f64d95 243 log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name);
66cdd0f2
LP
244 ret = -errno;
245 }
246 } else {
247
248 if (unlinkat(dirfd(dir), de->d_name, 0) < 0) {
249
250 if (errno == ENOENT)
251 continue;
252
56f64d95 253 log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name);
66cdd0f2
LP
254 ret = -errno;
255 }
256 }
257 }
258
259 return ret;
260
261fail:
56f64d95 262 log_warning_errno(errno, "Failed to read /dev/shm: %m");
66cdd0f2
LP
263 return -errno;
264}
265
266static int clean_posix_shm(uid_t uid) {
267 _cleanup_closedir_ DIR *dir = NULL;
268
269 dir = opendir("/dev/shm");
270 if (!dir) {
271 if (errno == ENOENT)
272 return 0;
273
e1427b13 274 return log_warning_errno(errno, "Failed to open /dev/shm: %m");
66cdd0f2
LP
275 }
276
277 return clean_posix_shm_internal(dir, uid);
278}
279
280static int clean_posix_mq(uid_t uid) {
281 _cleanup_closedir_ DIR *dir = NULL;
282 struct dirent *de;
283 int ret = 0;
284
285 dir = opendir("/dev/mqueue");
286 if (!dir) {
287 if (errno == ENOENT)
288 return 0;
289
e1427b13 290 return log_warning_errno(errno, "Failed to open /dev/mqueue: %m");
66cdd0f2
LP
291 }
292
293 FOREACH_DIRENT(de, dir, goto fail) {
294 struct stat st;
295 char fn[1+strlen(de->d_name)+1];
296
297 if (STR_IN_SET(de->d_name, "..", "."))
298 continue;
299
300 if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
301 if (errno == ENOENT)
302 continue;
303
94c156cd
LP
304 ret = log_warning_errno(errno,
305 "Failed to stat() MQ segment %s: %m",
306 de->d_name);
66cdd0f2
LP
307 continue;
308 }
309
310 if (st.st_uid != uid)
311 continue;
312
313 fn[0] = '/';
314 strcpy(fn+1, de->d_name);
315
316 if (mq_unlink(fn) < 0) {
317 if (errno == ENOENT)
318 continue;
319
94c156cd
LP
320 ret = log_warning_errno(errno,
321 "Failed to unlink POSIX message queue %s: %m",
322 fn);
66cdd0f2
LP
323 }
324 }
325
326 return ret;
327
328fail:
e1427b13 329 return log_warning_errno(errno, "Failed to read /dev/mqueue: %m");
66cdd0f2
LP
330}
331
332int clean_ipc(uid_t uid) {
333 int ret = 0, r;
334
f7dc3ab9
LP
335 /* Refuse to clean IPC of the root and system users */
336 if (uid <= SYSTEM_UID_MAX)
66cdd0f2
LP
337 return 0;
338
339 r = clean_sysvipc_shm(uid);
340 if (r < 0)
341 ret = r;
342
343 r = clean_sysvipc_sem(uid);
344 if (r < 0)
345 ret = r;
346
347 r = clean_sysvipc_msg(uid);
348 if (r < 0)
349 ret = r;
350
351 r = clean_posix_shm(uid);
352 if (r < 0)
353 ret = r;
354
355 r = clean_posix_mq(uid);
356 if (r < 0)
357 ret = r;
358
359 return ret;
360}