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