]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <dlezcano at fr.ibm.com> | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | #define _GNU_SOURCE | |
24 | #include <stdio.h> | |
25 | #undef _GNU_SOURCE | |
26 | #include <stdlib.h> | |
27 | #include <errno.h> | |
28 | #include <mntent.h> | |
29 | #include <unistd.h> | |
30 | #include <string.h> | |
31 | #include <fcntl.h> | |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/param.h> | |
35 | #include <sys/inotify.h> | |
36 | #include <netinet/in.h> | |
37 | #include <net/if.h> | |
38 | ||
39 | #include "error.h" | |
40 | #include "config.h" | |
41 | ||
42 | #include <lxc/log.h> | |
43 | #include <lxc/cgroup.h> | |
44 | #include <lxc/start.h> | |
45 | ||
46 | lxc_log_define(lxc_cgroup, lxc); | |
47 | ||
48 | #define MTAB "/proc/mounts" | |
49 | ||
50 | static char nsgroup_path[MAXPATHLEN]; | |
51 | ||
52 | enum { | |
53 | CGROUP_NS_CGROUP = 1, | |
54 | CGROUP_CLONE_CHILDREN, | |
55 | }; | |
56 | ||
57 | static int get_cgroup_mount(const char *mtab, char *mnt) | |
58 | { | |
59 | struct mntent *mntent; | |
60 | FILE *file = NULL; | |
61 | int err = -1; | |
62 | ||
63 | file = setmntent(mtab, "r"); | |
64 | if (!file) { | |
65 | SYSERROR("failed to open %s", mtab); | |
66 | return -1; | |
67 | } | |
68 | ||
69 | while ((mntent = getmntent(file))) { | |
70 | ||
71 | /* there is a cgroup mounted named "lxc" */ | |
72 | if (!strcmp(mntent->mnt_fsname, "lxc") && | |
73 | !strcmp(mntent->mnt_type, "cgroup")) { | |
74 | strcpy(mnt, mntent->mnt_dir); | |
75 | err = 0; | |
76 | break; | |
77 | } | |
78 | ||
79 | /* fallback to the first non-lxc cgroup found */ | |
80 | if (!strcmp(mntent->mnt_type, "cgroup") && err) { | |
81 | strcpy(mnt, mntent->mnt_dir); | |
82 | err = 0; | |
83 | } | |
84 | }; | |
85 | ||
86 | DEBUG("using cgroup mounted at '%s'", mnt); | |
87 | ||
88 | fclose(file); | |
89 | ||
90 | return err; | |
91 | } | |
92 | ||
93 | static int get_cgroup_flags(const char *mtab, int *flags) | |
94 | { | |
95 | struct mntent *mntent; | |
96 | FILE *file = NULL; | |
97 | int err = -1; | |
98 | ||
99 | file = setmntent(mtab, "r"); | |
100 | if (!file) { | |
101 | SYSERROR("failed to open %s", mtab); | |
102 | return -1; | |
103 | } | |
104 | ||
105 | *flags = 0; | |
106 | ||
107 | while ((mntent = getmntent(file))) { | |
108 | ||
109 | /* there is a cgroup mounted named "lxc" */ | |
110 | if (!strcmp(mntent->mnt_fsname, "lxc") && | |
111 | !strcmp(mntent->mnt_type, "cgroup")) { | |
112 | ||
113 | if (hasmntopt(mntent, "ns")) | |
114 | *flags |= CGROUP_NS_CGROUP; | |
115 | ||
116 | if (hasmntopt(mntent, "clone_children")) | |
117 | *flags |= CGROUP_CLONE_CHILDREN; | |
118 | ||
119 | err = 0; | |
120 | break; | |
121 | } | |
122 | ||
123 | /* fallback to the first non-lxc cgroup found */ | |
124 | if (!strcmp(mntent->mnt_type, "cgroup") && err) { | |
125 | ||
126 | if (hasmntopt(mntent, "ns")) | |
127 | *flags |= CGROUP_NS_CGROUP; | |
128 | ||
129 | if (hasmntopt(mntent, "clone_children")) | |
130 | *flags |= CGROUP_CLONE_CHILDREN; | |
131 | ||
132 | err = 0; | |
133 | } | |
134 | }; | |
135 | ||
136 | DEBUG("cgroup flags is 0x%x", *flags); | |
137 | ||
138 | fclose(file); | |
139 | ||
140 | return err; | |
141 | } | |
142 | ||
143 | static int cgroup_rename_nsgroup(const char *mnt, const char *name, pid_t pid) | |
144 | { | |
145 | char oldname[MAXPATHLEN]; | |
146 | ||
147 | snprintf(oldname, MAXPATHLEN, "%s/%d", mnt, pid); | |
148 | ||
149 | if (rename(oldname, name)) { | |
150 | SYSERROR("failed to rename cgroup %s->%s", oldname, name); | |
151 | return -1; | |
152 | } | |
153 | ||
154 | DEBUG("'%s' renamed to '%s'", oldname, name); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
159 | static int cgroup_enable_clone_children(const char *path) | |
160 | { | |
161 | FILE *f; | |
162 | int ret = 0; | |
163 | ||
164 | f = fopen(path, "w"); | |
165 | if (!f) { | |
166 | SYSERROR("failed to open '%s'", path); | |
167 | return -1; | |
168 | } | |
169 | ||
170 | if (fprintf(f, "1") < 1) { | |
171 | ERROR("failed to write flag to '%s'", path); | |
172 | ret = -1; | |
173 | } | |
174 | ||
175 | fclose(f); | |
176 | ||
177 | return ret; | |
178 | } | |
179 | ||
180 | static int cgroup_attach(const char *path, pid_t pid) | |
181 | { | |
182 | FILE *f; | |
183 | char tasks[MAXPATHLEN]; | |
184 | int ret = 0; | |
185 | ||
186 | snprintf(tasks, MAXPATHLEN, "%s/tasks", path); | |
187 | ||
188 | f = fopen(tasks, "w"); | |
189 | if (!f) { | |
190 | SYSERROR("failed to open '%s'", tasks); | |
191 | return -1; | |
192 | } | |
193 | ||
194 | if (fprintf(f, "%d", pid) <= 0) { | |
195 | SYSERROR("failed to write pid '%d' to '%s'", pid, tasks); | |
196 | ret = -1; | |
197 | } | |
198 | ||
199 | fclose(f); | |
200 | ||
201 | return ret; | |
202 | } | |
203 | ||
204 | int lxc_cgroup_create(const char *name, pid_t pid) | |
205 | { | |
206 | char cgmnt[MAXPATHLEN]; | |
207 | char cgname[MAXPATHLEN]; | |
208 | char clonechild[MAXPATHLEN]; | |
209 | int flags; | |
210 | ||
211 | if (get_cgroup_mount(MTAB, cgmnt)) { | |
212 | ERROR("cgroup is not mounted"); | |
213 | return -1; | |
214 | } | |
215 | ||
216 | snprintf(cgname, MAXPATHLEN, "%s/%s", cgmnt, name); | |
217 | ||
218 | /* | |
219 | * There is a previous cgroup, assume it is empty, | |
220 | * otherwise that fails | |
221 | */ | |
222 | if (!access(cgname, F_OK) && rmdir(cgname)) { | |
223 | SYSERROR("failed to remove previous cgroup '%s'", cgname); | |
224 | return -1; | |
225 | } | |
226 | ||
227 | if (get_cgroup_flags(MTAB, &flags)) { | |
228 | SYSERROR("failed to get cgroup flags"); | |
229 | return -1; | |
230 | } | |
231 | ||
232 | /* We have the deprecated ns_cgroup subsystem */ | |
233 | if (flags & CGROUP_NS_CGROUP) { | |
234 | WARN("using deprecated ns_cgroup"); | |
235 | return cgroup_rename_nsgroup(cgmnt, cgname, pid); | |
236 | } | |
237 | ||
238 | /* we check if the kernel has clone_children, at this point if there | |
239 | * no clone_children neither ns_cgroup, that means the cgroup is mounted | |
240 | * without the ns_cgroup and it has not the compatibility flag | |
241 | */ | |
242 | if (access(clonechild, F_OK)) { | |
243 | ERROR("no ns_cgroup option specified"); | |
244 | return -1; | |
245 | } | |
246 | ||
247 | snprintf(clonechild, MAXPATHLEN, "%s/cgroup.clone_children", cgmnt); | |
248 | ||
249 | /* we enable the clone_children flag of the cgroup */ | |
250 | if (cgroup_enable_clone_children(clonechild)) { | |
251 | SYSERROR("failed to enable 'clone_children flag"); | |
252 | return -1; | |
253 | } | |
254 | ||
255 | /* Let's create the cgroup */ | |
256 | if (mkdir(cgname, 0700)) { | |
257 | SYSERROR("failed to create '%s' directory", cgname); | |
258 | return -1; | |
259 | } | |
260 | ||
261 | /* Let's add the pid to the 'tasks' file */ | |
262 | if (cgroup_attach(cgname, pid)) { | |
263 | SYSERROR("failed to attach pid '%d' to '%s'", pid, cgname); | |
264 | rmdir(cgname); | |
265 | return -1; | |
266 | } | |
267 | ||
268 | return 0; | |
269 | } | |
270 | ||
271 | int lxc_cgroup_destroy(const char *name) | |
272 | { | |
273 | char cgmnt[MAXPATHLEN]; | |
274 | char cgname[MAXPATHLEN]; | |
275 | ||
276 | if (get_cgroup_mount(MTAB, cgmnt)) { | |
277 | ERROR("cgroup is not mounted"); | |
278 | return -1; | |
279 | } | |
280 | ||
281 | snprintf(cgname, MAXPATHLEN, "%s/%s", cgmnt, name); | |
282 | if (rmdir(cgname)) { | |
283 | SYSERROR("failed to remove cgroup '%s'", cgname); | |
284 | return -1; | |
285 | } | |
286 | ||
287 | DEBUG("'%s' unlinked", cgname); | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | int lxc_cgroup_path_get(char **path, const char *name) | |
293 | { | |
294 | char cgroup[MAXPATHLEN]; | |
295 | ||
296 | *path = &nsgroup_path[0]; | |
297 | ||
298 | /* | |
299 | * report nsgroup_path string if already set | |
300 | */ | |
301 | if (**path != 0) | |
302 | return 0; | |
303 | ||
304 | if (get_cgroup_mount(MTAB, cgroup)) { | |
305 | ERROR("cgroup is not mounted"); | |
306 | return -1; | |
307 | } | |
308 | ||
309 | snprintf(nsgroup_path, MAXPATHLEN, "%s/%s", cgroup, name); | |
310 | return 0; | |
311 | } | |
312 | ||
313 | int lxc_cgroup_set(const char *name, const char *subsystem, const char *value) | |
314 | { | |
315 | int fd, ret; | |
316 | char *nsgroup; | |
317 | char path[MAXPATHLEN]; | |
318 | ||
319 | ret = lxc_cgroup_path_get(&nsgroup, name); | |
320 | if (ret) | |
321 | return -1; | |
322 | ||
323 | snprintf(path, MAXPATHLEN, "%s/%s", nsgroup, subsystem); | |
324 | ||
325 | fd = open(path, O_WRONLY); | |
326 | if (fd < 0) { | |
327 | ERROR("open %s : %s", path, strerror(errno)); | |
328 | return -1; | |
329 | } | |
330 | ||
331 | ret = write(fd, value, strlen(value)); | |
332 | if (ret < 0) { | |
333 | ERROR("write %s : %s", path, strerror(errno)); | |
334 | goto out; | |
335 | } | |
336 | ||
337 | ret = 0; | |
338 | out: | |
339 | close(fd); | |
340 | return ret; | |
341 | } | |
342 | ||
343 | int lxc_cgroup_get(const char *name, const char *subsystem, | |
344 | char *value, size_t len) | |
345 | { | |
346 | int fd, ret = -1; | |
347 | char *nsgroup; | |
348 | char path[MAXPATHLEN]; | |
349 | ||
350 | ret = lxc_cgroup_path_get(&nsgroup, name); | |
351 | if (ret) | |
352 | return -1; | |
353 | ||
354 | snprintf(path, MAXPATHLEN, "%s/%s", nsgroup, subsystem); | |
355 | ||
356 | fd = open(path, O_RDONLY); | |
357 | if (fd < 0) { | |
358 | ERROR("open %s : %s", path, strerror(errno)); | |
359 | return -1; | |
360 | } | |
361 | ||
362 | ret = read(fd, value, len); | |
363 | if (ret < 0) | |
364 | ERROR("read %s : %s", path, strerror(errno)); | |
365 | ||
366 | close(fd); | |
367 | return ret; | |
368 | } | |
369 | ||
370 | int lxc_cgroup_nrtasks(const char *name) | |
371 | { | |
372 | char *nsgroup; | |
373 | char path[MAXPATHLEN]; | |
374 | int pid, ret, count = 0; | |
375 | FILE *file; | |
376 | ||
377 | ret = lxc_cgroup_path_get(&nsgroup, name); | |
378 | if (ret) | |
379 | return -1; | |
380 | ||
381 | snprintf(path, MAXPATHLEN, "%s/tasks", nsgroup); | |
382 | ||
383 | file = fopen(path, "r"); | |
384 | if (!file) { | |
385 | SYSERROR("fopen '%s' failed", path); | |
386 | return -1; | |
387 | } | |
388 | ||
389 | while (fscanf(file, "%d", &pid) != EOF) | |
390 | count++; | |
391 | ||
392 | fclose(file); | |
393 | ||
394 | return count; | |
395 | } |