Merge lp:~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208251712 into lp:ubuntu/quantal/lxc
- Quantal (12.10)
- quantal-201208251712
- Merge into quantal
Status: | Needs review |
---|---|
Proposed branch: | lp:~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208251712 |
Merge into: | lp:ubuntu/quantal/lxc |
Diff against target: |
1933 lines (+1700/-188) (has conflicts) 6 files modified
.pc/0201-fix-mkdir-race/src/lxc/cgroup.c (+669/-0) .pc/0202-make-api-start-reliable/src/lxc/lxccontainer.c (+905/-0) debian/changelog (+44/-17) debian/local/lxc-wait (+0/-171) debian/patches/0201-fix-mkdir-race (+24/-0) debian/patches/0202-make-api-start-reliable (+58/-0) Conflict adding file .pc/0201-fix-mkdir-race. Moved existing file to .pc/0201-fix-mkdir-race.moved. Conflict adding file .pc/0202-make-api-start-reliable. Moved existing file to .pc/0202-make-api-start-reliable.moved. Text conflict in debian/changelog Conflict adding file debian/patches/0201-fix-mkdir-race. Moved existing file to debian/patches/0201-fix-mkdir-race.moved. Conflict adding file debian/patches/0202-make-api-start-reliable. Moved existing file to debian/patches/0202-make-api-start-reliable.moved. |
To merge this branch: | bzr merge lp:~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208251712 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+121314@code.launchpad.net |
Commit message
Description of the change
The package importer has detected a possible inconsistency between the package history in the archive and the history in bzr. As the archive is authoritative the importer has made lp:ubuntu/quantal/lxc reflect what is in the archive and the old bzr branch has been pushed to lp:~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208251712. This merge proposal was created so that an Ubuntu developer can review the situations and perform a merge/upload if necessary. There are three typical cases where this can happen.
1. Where someone pushes a change to bzr and someone else uploads the package without that change. This is the reason that this check is done by the importer. If this appears to be the case then a merge/upload should be done if the changes that were in bzr are still desirable.
2. The importer incorrectly detected the above situation when someone made a change in bzr and then uploaded it.
3. The importer incorrectly detected the above situation when someone just uploaded a package and didn't touch bzr.
If this case doesn't appear to be the first situation then set the status of the merge proposal to "Rejected" and help avoid the problem in future by filing a bug at https:/
(this is an automatically generated message)
Unmerged revisions
- 146. By Stéphane Graber
-
Get rid of debian/
local/lxc- wait - 145. By Stéphane Graber
-
releasing version 0.8.0~rc1-4ubuntu29
- 144. By Stéphane Graber
-
Fix get_ips() timeout and add import time warning
- 143. By Serge Hallyn
-
move fn up before use.
- 142. By Serge Hallyn
-
add needed fn prototype before use.
- 141. By Serge Hallyn
-
0202-make-
api-start- reliable: have daemonized start through the api
wait until the container is RUNNING before returning true. If a 5
second timeout is hit before the container is RUNNING, return false. - 140. By Serge Hallyn
-
rename 201-fix-mkdir-race to 0201-fix-mkdir-race
- 139. By Serge Hallyn
-
add missing directories for new patch.
reset pocket to unreleased for push to parent - 138. By Serge Hallyn
-
set version and release pocket for ppa build.
- 137. By Serge Hallyn
-
201-fix-mkdir-race: don't raise error if mkdir fails with EEXIST.
Preview Diff
1 | === added directory '.pc/0201-fix-mkdir-race' |
2 | === renamed directory '.pc/0201-fix-mkdir-race' => '.pc/0201-fix-mkdir-race.moved' |
3 | === added file '.pc/0201-fix-mkdir-race/.timestamp' |
4 | === added directory '.pc/0201-fix-mkdir-race/src' |
5 | === added directory '.pc/0201-fix-mkdir-race/src/lxc' |
6 | === added file '.pc/0201-fix-mkdir-race/src/lxc/cgroup.c' |
7 | --- .pc/0201-fix-mkdir-race/src/lxc/cgroup.c 1970-01-01 00:00:00 +0000 |
8 | +++ .pc/0201-fix-mkdir-race/src/lxc/cgroup.c 2012-08-25 17:20:25 +0000 |
9 | @@ -0,0 +1,669 @@ |
10 | +/* |
11 | + * lxc: linux Container library |
12 | + * |
13 | + * (C) Copyright IBM Corp. 2007, 2008 |
14 | + * |
15 | + * Authors: |
16 | + * Daniel Lezcano <dlezcano at fr.ibm.com> |
17 | + * |
18 | + * This library is free software; you can redistribute it and/or |
19 | + * modify it under the terms of the GNU Lesser General Public |
20 | + * License as published by the Free Software Foundation; either |
21 | + * version 2.1 of the License, or (at your option) any later version. |
22 | + * |
23 | + * This library is distributed in the hope that it will be useful, |
24 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
26 | + * Lesser General Public License for more details. |
27 | + * |
28 | + * You should have received a copy of the GNU Lesser General Public |
29 | + * License along with this library; if not, write to the Free Software |
30 | + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
31 | + */ |
32 | +#define _GNU_SOURCE |
33 | +#include <stdio.h> |
34 | +#undef _GNU_SOURCE |
35 | +#include <stdlib.h> |
36 | +#include <errno.h> |
37 | +#include <mntent.h> |
38 | +#include <unistd.h> |
39 | +#include <string.h> |
40 | +#include <dirent.h> |
41 | +#include <fcntl.h> |
42 | +#include <sys/types.h> |
43 | +#include <sys/stat.h> |
44 | +#include <sys/param.h> |
45 | +#include <sys/inotify.h> |
46 | +#include <netinet/in.h> |
47 | +#include <net/if.h> |
48 | + |
49 | +#include "error.h" |
50 | +#include "config.h" |
51 | + |
52 | +#include <lxc/log.h> |
53 | +#include <lxc/cgroup.h> |
54 | +#include <lxc/start.h> |
55 | + |
56 | +lxc_log_define(lxc_cgroup, lxc); |
57 | + |
58 | +#define MTAB "/proc/mounts" |
59 | + |
60 | +enum { |
61 | + CGROUP_NS_CGROUP = 1, |
62 | + CGROUP_CLONE_CHILDREN, |
63 | +}; |
64 | + |
65 | +static char *hasmntopt_multiple(struct mntent *mntent, const char *options) |
66 | +{ |
67 | + const char *ptr = options; |
68 | + const char *ptr2 = strchr(options, ','); |
69 | + char *result; |
70 | + |
71 | + while (ptr2 != NULL) { |
72 | + char *option = strndup(ptr, ptr2 - ptr); |
73 | + if (!option) { |
74 | + SYSERROR("Temporary memory allocation error"); |
75 | + return NULL; |
76 | + } |
77 | + |
78 | + result = hasmntopt(mntent, option); |
79 | + free(option); |
80 | + |
81 | + if (!result) { |
82 | + return NULL; |
83 | + } |
84 | + |
85 | + ptr = ptr2 + 1; |
86 | + ptr2 = strchr(ptr, ','); |
87 | + } |
88 | + |
89 | + /* for multiple mount options, the return value is basically NULL |
90 | + * or non-NULL, so this should suffice for our purposes */ |
91 | + return hasmntopt(mntent, ptr); |
92 | +} |
93 | + |
94 | +/* |
95 | + * get_init_cgroup: get the cgroup init is in. |
96 | + * dsg: preallocated buffer to put the output in |
97 | + * subsystem: the exact cgroup subsystem to look up |
98 | + * mntent: a mntent (from getmntent) whose mntopts contains the |
99 | + * subsystem to look up. |
100 | + * |
101 | + * subsystem and mntent can both be NULL, in which case we return |
102 | + * the first entry in /proc/1/cgroup. |
103 | + * |
104 | + * Returns a pointer to the answer, which may be "". |
105 | + */ |
106 | +static char *get_init_cgroup(const char *subsystem, struct mntent *mntent, |
107 | + char *dsg) |
108 | +{ |
109 | + FILE *f; |
110 | + char *c, *c2; |
111 | + char line[MAXPATHLEN]; |
112 | + |
113 | + *dsg = '\0'; |
114 | + f = fopen("/proc/1/cgroup", "r"); |
115 | + if (!f) |
116 | + return dsg; |
117 | + |
118 | + while (fgets(line, MAXPATHLEN, f)) { |
119 | + c = index(line, ':'); |
120 | + if (!c) |
121 | + continue; |
122 | + c++; |
123 | + c2 = index(c, ':'); |
124 | + if (!c2) |
125 | + continue; |
126 | + *c2 = '\0'; |
127 | + c2++; |
128 | + if (!subsystem && !mntent) |
129 | + goto good; |
130 | + if (subsystem && strcmp(c, subsystem) != 0) |
131 | + continue; |
132 | + if (mntent && !hasmntopt(mntent, c)) |
133 | + continue; |
134 | +good: |
135 | + DEBUG("get_init_cgroup: found init cgroup for subsys %s at %s\n", |
136 | + subsystem, c2); |
137 | + strncpy(dsg, c2, MAXPATHLEN); |
138 | + c = &dsg[strlen(dsg)-1]; |
139 | + if (*c == '\n') |
140 | + *c = '\0'; |
141 | + goto found; |
142 | + } |
143 | + |
144 | +found: |
145 | + fclose(f); |
146 | + return dsg; |
147 | +} |
148 | + |
149 | +static int get_cgroup_mount(const char *subsystem, char *mnt) |
150 | +{ |
151 | + struct mntent *mntent; |
152 | + char initcgroup[MAXPATHLEN]; |
153 | + FILE *file = NULL; |
154 | + |
155 | + file = setmntent(MTAB, "r"); |
156 | + if (!file) { |
157 | + SYSERROR("failed to open %s", MTAB); |
158 | + return -1; |
159 | + } |
160 | + |
161 | + while ((mntent = getmntent(file))) { |
162 | + |
163 | + if (strcmp(mntent->mnt_type, "cgroup")) |
164 | + continue; |
165 | + if (!subsystem || hasmntopt_multiple(mntent, subsystem)) { |
166 | + int ret; |
167 | + ret = snprintf(mnt, MAXPATHLEN, "%s%s/lxc", |
168 | + mntent->mnt_dir, |
169 | + get_init_cgroup(subsystem, NULL, |
170 | + initcgroup)); |
171 | + if (ret < 0 || ret >= MAXPATHLEN) |
172 | + goto fail; |
173 | + fclose(file); |
174 | + DEBUG("using cgroup mounted at '%s'", mnt); |
175 | + return 0; |
176 | + } |
177 | + }; |
178 | + |
179 | +fail: |
180 | + DEBUG("Failed to find cgroup for %s\n", |
181 | + subsystem ? subsystem : "(NULL)"); |
182 | + |
183 | + fclose(file); |
184 | + |
185 | + return -1; |
186 | +} |
187 | + |
188 | +int lxc_ns_is_mounted(void) |
189 | +{ |
190 | + static char buf[MAXPATHLEN]; |
191 | + |
192 | + return (get_cgroup_mount("ns", buf) == 0); |
193 | +} |
194 | + |
195 | +static int get_cgroup_flags(struct mntent *mntent) |
196 | +{ |
197 | + int flags = 0; |
198 | + |
199 | + |
200 | + if (hasmntopt(mntent, "ns")) |
201 | + flags |= CGROUP_NS_CGROUP; |
202 | + |
203 | + if (hasmntopt(mntent, "clone_children")) |
204 | + flags |= CGROUP_CLONE_CHILDREN; |
205 | + |
206 | + DEBUG("cgroup %s has flags 0x%x", mntent->mnt_dir, flags); |
207 | + return flags; |
208 | +} |
209 | + |
210 | +static int cgroup_rename_nsgroup(const char *mnt, const char *name, pid_t pid) |
211 | +{ |
212 | + char oldname[MAXPATHLEN]; |
213 | + int ret; |
214 | + |
215 | + ret = snprintf(oldname, MAXPATHLEN, "%s/%d", mnt, pid); |
216 | + if (ret < 0 || ret >= MAXPATHLEN) { |
217 | + ERROR("Name too long"); |
218 | + return -1; |
219 | + } |
220 | + |
221 | + if (rename(oldname, name)) { |
222 | + SYSERROR("failed to rename cgroup %s->%s", oldname, name); |
223 | + return -1; |
224 | + } |
225 | + |
226 | + DEBUG("'%s' renamed to '%s'", oldname, name); |
227 | + |
228 | + return 0; |
229 | +} |
230 | + |
231 | +static int cgroup_enable_clone_children(const char *path) |
232 | +{ |
233 | + FILE *f; |
234 | + int ret = 0; |
235 | + |
236 | + f = fopen(path, "w"); |
237 | + if (!f) { |
238 | + SYSERROR("failed to open '%s'", path); |
239 | + return -1; |
240 | + } |
241 | + |
242 | + if (fprintf(f, "1") < 1) { |
243 | + ERROR("failed to write flag to '%s'", path); |
244 | + ret = -1; |
245 | + } |
246 | + |
247 | + fclose(f); |
248 | + |
249 | + return ret; |
250 | +} |
251 | + |
252 | +int lxc_cgroup_attach(const char *path, pid_t pid) |
253 | +{ |
254 | + FILE *f; |
255 | + char tasks[MAXPATHLEN]; |
256 | + int ret = 0; |
257 | + int rc; |
258 | + |
259 | + rc = snprintf(tasks, MAXPATHLEN, "%s/tasks", path); |
260 | + if (rc < 0 || rc >= MAXPATHLEN) { |
261 | + ERROR("pathname too long"); |
262 | + return -1; |
263 | + } |
264 | + |
265 | + f = fopen(tasks, "w"); |
266 | + if (!f) { |
267 | + SYSERROR("failed to open '%s'", tasks); |
268 | + return -1; |
269 | + } |
270 | + |
271 | + if (fprintf(f, "%d", pid) <= 0) { |
272 | + SYSERROR("failed to write pid '%d' to '%s'", pid, tasks); |
273 | + ret = -1; |
274 | + } |
275 | + |
276 | + fclose(f); |
277 | + |
278 | + return ret; |
279 | +} |
280 | + |
281 | +/* |
282 | + * rename cgname, which is under cgparent, to a new name starting |
283 | + * with 'cgparent/dead'. That way cgname can be reused. Return |
284 | + * 0 on success, -1 on failure. |
285 | + */ |
286 | +int try_to_move_cgname(char *cgparent, char *cgname) |
287 | +{ |
288 | + char *newdir; |
289 | + |
290 | + /* tempnam problems don't matter here - cgroupfs will prevent |
291 | + * duplicates if we race, and we'll just fail at that (unlikely) |
292 | + * point |
293 | + */ |
294 | + |
295 | + newdir = tempnam(cgparent, "dead"); |
296 | + if (!newdir) |
297 | + return -1; |
298 | + if (rename(cgname, newdir)) |
299 | + return -1; |
300 | + WARN("non-empty cgroup %s renamed to %s, please manually inspect it\n", |
301 | + cgname, newdir); |
302 | + |
303 | + return 0; |
304 | +} |
305 | + |
306 | +/* |
307 | + * create a cgroup for the container in a particular subsystem. |
308 | + */ |
309 | +static int lxc_one_cgroup_create(const char *name, |
310 | + struct mntent *mntent, pid_t pid) |
311 | +{ |
312 | + char cginit[MAXPATHLEN], cgname[MAXPATHLEN], cgparent[MAXPATHLEN]; |
313 | + char clonechild[MAXPATHLEN]; |
314 | + char initcgroup[MAXPATHLEN]; |
315 | + int flags, ret; |
316 | + |
317 | + /* cgparent is the parent dir, /sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc */ |
318 | + /* (remember get_init_cgroup() returns a path starting with '/') */ |
319 | + /* cgname is the full name, /sys/fs/cgroup/</cgroup>/<init-cgroup>/lxc/name */ |
320 | + ret = snprintf(cginit, MAXPATHLEN, "%s%s", mntent->mnt_dir, |
321 | + get_init_cgroup(NULL, mntent, initcgroup)); |
322 | + if (ret < 0 || ret >= MAXPATHLEN) { |
323 | + SYSERROR("Failed creating pathname for init's cgroup (%d)\n", ret); |
324 | + return -1; |
325 | + } |
326 | + |
327 | + ret = snprintf(cgparent, MAXPATHLEN, "%s/lxc", cginit); |
328 | + if (ret < 0 || ret >= MAXPATHLEN) { |
329 | + SYSERROR("Failed creating pathname for cgroup parent (%d)\n", ret); |
330 | + return -1; |
331 | + } |
332 | + ret = snprintf(cgname, MAXPATHLEN, "%s/%s", cgparent, name); |
333 | + if (ret < 0 || ret >= MAXPATHLEN) { |
334 | + SYSERROR("Failed creating pathname for cgroup (%d)\n", ret); |
335 | + return -1; |
336 | + } |
337 | + |
338 | + flags = get_cgroup_flags(mntent); |
339 | + |
340 | + /* Do we have the deprecated ns_cgroup subsystem? */ |
341 | + if (flags & CGROUP_NS_CGROUP) { |
342 | + WARN("using deprecated ns_cgroup"); |
343 | + return cgroup_rename_nsgroup(cgparent, cgname, pid); |
344 | + } |
345 | + |
346 | + ret = snprintf(clonechild, MAXPATHLEN, "%s/cgroup.clone_children", |
347 | + cginit); |
348 | + if (ret < 0 || ret >= MAXPATHLEN) { |
349 | + SYSERROR("Failed creating pathname for clone_children (%d)\n", ret); |
350 | + return -1; |
351 | + } |
352 | + |
353 | + /* we check if the kernel has clone_children, at this point if there |
354 | + * no clone_children neither ns_cgroup, that means the cgroup is mounted |
355 | + * without the ns_cgroup and it has not the compatibility flag |
356 | + */ |
357 | + if (access(clonechild, F_OK)) { |
358 | + ERROR("no ns_cgroup option specified"); |
359 | + return -1; |
360 | + } |
361 | + |
362 | + /* enable the clone_children flag of the cgroup */ |
363 | + if (cgroup_enable_clone_children(clonechild)) { |
364 | + SYSERROR("failed to enable 'clone_children flag"); |
365 | + return -1; |
366 | + } |
367 | + |
368 | + /* if /sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc does not exist, create it */ |
369 | + if (access(cgparent, F_OK) && mkdir(cgparent, 0755)) { |
370 | + SYSERROR("failed to create '%s' directory", cgparent); |
371 | + return -1; |
372 | + } |
373 | + |
374 | + /* |
375 | + * There is a previous cgroup. Try to delete it. If that fails |
376 | + * (i.e. it is not empty) try to move it out of the way. |
377 | + */ |
378 | + if (!access(cgname, F_OK) && rmdir(cgname)) { |
379 | + if (try_to_move_cgname(cgparent, cgname)) { |
380 | + SYSERROR("failed to remove previous cgroup '%s'", cgname); |
381 | + return -1; |
382 | + } |
383 | + } |
384 | + |
385 | + /* Let's create the cgroup */ |
386 | + if (mkdir(cgname, 0755)) { |
387 | + SYSERROR("failed to create '%s' directory", cgname); |
388 | + return -1; |
389 | + } |
390 | + |
391 | + /* Let's add the pid to the 'tasks' file */ |
392 | + if (lxc_cgroup_attach(cgname, pid)) { |
393 | + SYSERROR("failed to attach pid '%d' to '%s'", pid, cgname); |
394 | + rmdir(cgname); |
395 | + return -1; |
396 | + } |
397 | + |
398 | + INFO("created cgroup '%s'", cgname); |
399 | + |
400 | + return 0; |
401 | +} |
402 | + |
403 | +/* |
404 | + * for each mounted cgroup, create a cgroup for the container |
405 | + */ |
406 | +int lxc_cgroup_create(const char *name, pid_t pid) |
407 | +{ |
408 | + struct mntent *mntent; |
409 | + FILE *file = NULL; |
410 | + int err = -1; |
411 | + int found = 0; |
412 | + |
413 | + file = setmntent(MTAB, "r"); |
414 | + if (!file) { |
415 | + SYSERROR("failed to open %s", MTAB); |
416 | + return -1; |
417 | + } |
418 | + |
419 | + while ((mntent = getmntent(file))) { |
420 | + |
421 | + DEBUG("checking '%s' (%s)", mntent->mnt_dir, mntent->mnt_type); |
422 | + |
423 | + if (!strcmp(mntent->mnt_type, "cgroup")) { |
424 | + |
425 | + INFO("[%d] found cgroup mounted at '%s',opts='%s'", |
426 | + ++found, mntent->mnt_dir, mntent->mnt_opts); |
427 | + |
428 | + err = lxc_one_cgroup_create(name, mntent, pid); |
429 | + if (err) |
430 | + goto out; |
431 | + } |
432 | + }; |
433 | + |
434 | + if (!found) |
435 | + ERROR("No cgroup mounted on the system"); |
436 | + |
437 | +out: |
438 | + endmntent(file); |
439 | + return err; |
440 | +} |
441 | + |
442 | +int recursive_rmdir(char *dirname) |
443 | +{ |
444 | + struct dirent dirent, *direntp; |
445 | + DIR *dir; |
446 | + int ret; |
447 | + char pathname[MAXPATHLEN]; |
448 | + |
449 | + dir = opendir(dirname); |
450 | + if (!dir) { |
451 | + WARN("failed to open directory: %m"); |
452 | + return -1; |
453 | + } |
454 | + |
455 | + while (!readdir_r(dir, &dirent, &direntp)) { |
456 | + struct stat mystat; |
457 | + int rc; |
458 | + |
459 | + if (!direntp) |
460 | + break; |
461 | + |
462 | + if (!strcmp(direntp->d_name, ".") || |
463 | + !strcmp(direntp->d_name, "..")) |
464 | + continue; |
465 | + |
466 | + rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); |
467 | + if (rc < 0 || rc >= MAXPATHLEN) { |
468 | + ERROR("pathname too long"); |
469 | + continue; |
470 | + } |
471 | + ret = stat(pathname, &mystat); |
472 | + if (ret) |
473 | + continue; |
474 | + if (S_ISDIR(mystat.st_mode)) |
475 | + recursive_rmdir(pathname); |
476 | + } |
477 | + |
478 | + ret = rmdir(dirname); |
479 | + |
480 | + if (closedir(dir)) |
481 | + ERROR("failed to close directory"); |
482 | + return ret; |
483 | + |
484 | + |
485 | +} |
486 | + |
487 | +int lxc_one_cgroup_destroy(struct mntent *mntent, const char *name) |
488 | +{ |
489 | + char cgname[MAXPATHLEN], initcgroup[MAXPATHLEN]; |
490 | + char *cgmnt = mntent->mnt_dir; |
491 | + int rc; |
492 | + |
493 | + rc = snprintf(cgname, MAXPATHLEN, "%s%s/lxc/%s", cgmnt, |
494 | + get_init_cgroup(NULL, mntent, initcgroup), name); |
495 | + if (rc < 0 || rc >= MAXPATHLEN) { |
496 | + ERROR("name too long"); |
497 | + return -1; |
498 | + } |
499 | + DEBUG("destroying %s\n", cgname); |
500 | + if (recursive_rmdir(cgname)) { |
501 | + SYSERROR("failed to remove cgroup '%s'", cgname); |
502 | + return -1; |
503 | + } |
504 | + |
505 | + DEBUG("'%s' unlinked", cgname); |
506 | + |
507 | + return 0; |
508 | +} |
509 | + |
510 | +/* |
511 | + * for each mounted cgroup, destroy the cgroup for the container |
512 | + */ |
513 | +int lxc_cgroup_destroy(const char *name) |
514 | +{ |
515 | + struct mntent *mntent; |
516 | + FILE *file = NULL; |
517 | + int ret, err = -1; |
518 | + |
519 | + file = setmntent(MTAB, "r"); |
520 | + if (!file) { |
521 | + SYSERROR("failed to open %s", MTAB); |
522 | + return -1; |
523 | + } |
524 | + |
525 | + while ((mntent = getmntent(file))) { |
526 | + if (!strcmp(mntent->mnt_type, "cgroup")) { |
527 | + ret = lxc_one_cgroup_destroy(mntent, name); |
528 | + if (ret) { |
529 | + fclose(file); |
530 | + return ret; |
531 | + } |
532 | + err = 0; |
533 | + } |
534 | + } |
535 | + |
536 | + fclose(file); |
537 | + |
538 | + return err; |
539 | +} |
540 | +/* |
541 | + * lxc_cgroup_path_get: put into *path the pathname for |
542 | + * %subsystem and cgroup %name. If %subsystem is NULL, then |
543 | + * the first mounted cgroup will be used (for nr_tasks) |
544 | + */ |
545 | +int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name) |
546 | +{ |
547 | + static char buf[MAXPATHLEN]; |
548 | + static char retbuf[MAXPATHLEN]; |
549 | + int rc; |
550 | + |
551 | + /* what lxc_cgroup_set calls subsystem is actually the filename, i.e. |
552 | + 'devices.allow'. So for our purposee we trim it */ |
553 | + if (subsystem) { |
554 | + rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem); |
555 | + if (rc < 0 || rc >= MAXPATHLEN) { |
556 | + ERROR("subsystem name too long"); |
557 | + return -1; |
558 | + } |
559 | + char *s = index(retbuf, '.'); |
560 | + if (s) |
561 | + *s = '\0'; |
562 | + DEBUG("%s: called for subsys %s name %s\n", __func__, retbuf, name); |
563 | + } |
564 | + if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) { |
565 | + ERROR("cgroup is not mounted"); |
566 | + return -1; |
567 | + } |
568 | + |
569 | + rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, name); |
570 | + if (rc < 0 || rc >= MAXPATHLEN) { |
571 | + ERROR("name too long"); |
572 | + return -1; |
573 | + } |
574 | + |
575 | + DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem); |
576 | + |
577 | + *path = retbuf; |
578 | + return 0; |
579 | +} |
580 | + |
581 | +int lxc_cgroup_set(const char *name, const char *filename, const char *value) |
582 | +{ |
583 | + int fd, ret; |
584 | + char *dirpath; |
585 | + char path[MAXPATHLEN]; |
586 | + int rc; |
587 | + |
588 | + ret = lxc_cgroup_path_get(&dirpath, filename, name); |
589 | + if (ret) |
590 | + return -1; |
591 | + |
592 | + rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); |
593 | + if (rc < 0 || rc >= MAXPATHLEN) { |
594 | + ERROR("pathname too long"); |
595 | + return -1; |
596 | + } |
597 | + |
598 | + fd = open(path, O_WRONLY); |
599 | + if (fd < 0) { |
600 | + ERROR("open %s : %s", path, strerror(errno)); |
601 | + return -1; |
602 | + } |
603 | + |
604 | + ret = write(fd, value, strlen(value)); |
605 | + if (ret < 0) { |
606 | + ERROR("write %s : %s", path, strerror(errno)); |
607 | + goto out; |
608 | + } |
609 | + |
610 | + ret = 0; |
611 | +out: |
612 | + close(fd); |
613 | + return ret; |
614 | +} |
615 | + |
616 | +int lxc_cgroup_get(const char *name, const char *filename, |
617 | + char *value, size_t len) |
618 | +{ |
619 | + int fd, ret = -1; |
620 | + char *dirpath; |
621 | + char path[MAXPATHLEN]; |
622 | + int rc; |
623 | + |
624 | + ret = lxc_cgroup_path_get(&dirpath, filename, name); |
625 | + if (ret) |
626 | + return -1; |
627 | + |
628 | + rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); |
629 | + if (rc < 0 || rc >= MAXPATHLEN) { |
630 | + ERROR("pathname too long"); |
631 | + return -1; |
632 | + } |
633 | + |
634 | + fd = open(path, O_RDONLY); |
635 | + if (fd < 0) { |
636 | + ERROR("open %s : %s", path, strerror(errno)); |
637 | + return -1; |
638 | + } |
639 | + |
640 | + ret = read(fd, value, len); |
641 | + if (ret < 0) |
642 | + ERROR("read %s : %s", path, strerror(errno)); |
643 | + |
644 | + close(fd); |
645 | + return ret; |
646 | +} |
647 | + |
648 | +int lxc_cgroup_nrtasks(const char *name) |
649 | +{ |
650 | + char *dpath; |
651 | + char path[MAXPATHLEN]; |
652 | + int pid, ret, count = 0; |
653 | + FILE *file; |
654 | + int rc; |
655 | + |
656 | + ret = lxc_cgroup_path_get(&dpath, NULL, name); |
657 | + if (ret) |
658 | + return -1; |
659 | + |
660 | + rc = snprintf(path, MAXPATHLEN, "%s/tasks", dpath); |
661 | + if (rc < 0 || rc >= MAXPATHLEN) { |
662 | + ERROR("pathname too long"); |
663 | + return -1; |
664 | + } |
665 | + |
666 | + file = fopen(path, "r"); |
667 | + if (!file) { |
668 | + SYSERROR("fopen '%s' failed", path); |
669 | + return -1; |
670 | + } |
671 | + |
672 | + while (fscanf(file, "%d", &pid) != EOF) |
673 | + count++; |
674 | + |
675 | + fclose(file); |
676 | + |
677 | + return count; |
678 | +} |
679 | |
680 | === added directory '.pc/0202-make-api-start-reliable' |
681 | === renamed directory '.pc/0202-make-api-start-reliable' => '.pc/0202-make-api-start-reliable.moved' |
682 | === added file '.pc/0202-make-api-start-reliable/.timestamp' |
683 | === added directory '.pc/0202-make-api-start-reliable/src' |
684 | === added directory '.pc/0202-make-api-start-reliable/src/lxc' |
685 | === added file '.pc/0202-make-api-start-reliable/src/lxc/lxccontainer.c' |
686 | --- .pc/0202-make-api-start-reliable/src/lxc/lxccontainer.c 1970-01-01 00:00:00 +0000 |
687 | +++ .pc/0202-make-api-start-reliable/src/lxc/lxccontainer.c 2012-08-25 17:20:25 +0000 |
688 | @@ -0,0 +1,905 @@ |
689 | +/* liblxcapi |
690 | + * |
691 | + * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>. |
692 | + * Copyright © 2012 Canonical Ltd. |
693 | + * |
694 | + * This program is free software; you can redistribute it and/or modify |
695 | + * it under the terms of the GNU General Public License version 2, as |
696 | + * published by the Free Software Foundation. |
697 | + * |
698 | + * This program is distributed in the hope that it will be useful, |
699 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
700 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
701 | + * GNU General Public License for more details. |
702 | + * |
703 | + * You should have received a copy of the GNU General Public License along |
704 | + * with this program; if not, write to the Free Software Foundation, Inc., |
705 | + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
706 | + */ |
707 | + |
708 | +#include "lxc.h" |
709 | +#include "state.h" |
710 | +#include "lxccontainer.h" |
711 | +#include "conf.h" |
712 | +#include "config.h" |
713 | +#include "confile.h" |
714 | +#include "cgroup.h" |
715 | +#include "commands.h" |
716 | +#include "log.h" |
717 | +#include <unistd.h> |
718 | +#include <sys/types.h> |
719 | +#include <sys/wait.h> |
720 | +#include <errno.h> |
721 | + |
722 | +lxc_log_define(lxc_container, lxc); |
723 | + |
724 | +/* LOCKING |
725 | + * c->privlock protects the struct lxc_container from multiple threads. |
726 | + * c->slock protects the on-disk container data |
727 | + * NOTHING mutexes two independent programs with their own struct |
728 | + * lxc_container for the same c->name, between API calls. For instance, |
729 | + * c->config_read(); c->start(); Between those calls, data on disk |
730 | + * could change (which shouldn't bother the caller unless for instance |
731 | + * the rootfs get moved). c->config_read(); update; c->config_write(); |
732 | + * Two such updaters could race. The callers should therefore check their |
733 | + * results. Trying to prevent that would necessarily expose us to deadlocks |
734 | + * due to hung callers. So I prefer to keep the locks only within our own |
735 | + * functions, not across functions. |
736 | + * |
737 | + * If you're going to fork while holding a lxccontainer, increment |
738 | + * c->numthreads (under privlock) before forking. When deleting, |
739 | + * decrement numthreads under privlock, then if it hits 0 you can delete. |
740 | + * Do not ever use a lxccontainer whose numthreads you did not bump. |
741 | + */ |
742 | + |
743 | +static void lxc_container_free(struct lxc_container *c) |
744 | +{ |
745 | + if (!c) |
746 | + return; |
747 | + |
748 | + if (c->configfile) { |
749 | + free(c->configfile); |
750 | + c->configfile = NULL; |
751 | + } |
752 | + if (c->error_string) { |
753 | + free(c->error_string); |
754 | + c->error_string = NULL; |
755 | + } |
756 | + if (c->privlock) { |
757 | + sem_destroy(c->privlock); |
758 | + free(c->privlock); |
759 | + c->privlock = NULL; |
760 | + } |
761 | + if (c->name) { |
762 | + free(c->name); |
763 | + c->name = NULL; |
764 | + } |
765 | + /* |
766 | + * XXX TODO |
767 | + * note, c->lxc_conf is going to have to be freed, but the fn |
768 | + * to do that hasn't been written yet near as I can tell |
769 | + */ |
770 | + free(c); |
771 | +} |
772 | + |
773 | +int lxc_container_get(struct lxc_container *c) |
774 | +{ |
775 | + if (!c) |
776 | + return 0; |
777 | + |
778 | + if (lxclock(c->privlock, 0)) |
779 | + return 0; |
780 | + if (c->numthreads < 1) { |
781 | + // bail without trying to unlock, bc the privlock is now probably |
782 | + // in freed memory |
783 | + return 0; |
784 | + } |
785 | + c->numthreads++; |
786 | + lxcunlock(c->privlock); |
787 | + return 1; |
788 | +} |
789 | + |
790 | +int lxc_container_put(struct lxc_container *c) |
791 | +{ |
792 | + if (!c) |
793 | + return -1; |
794 | + if (lxclock(c->privlock, 0)) |
795 | + return -1; |
796 | + if (--c->numthreads < 1) { |
797 | + lxcunlock(c->privlock); |
798 | + lxc_container_free(c); |
799 | + return 1; |
800 | + } |
801 | + lxcunlock(c->privlock); |
802 | + return 0; |
803 | +} |
804 | + |
805 | +static bool file_exists(char *f) |
806 | +{ |
807 | + struct stat statbuf; |
808 | + |
809 | + return stat(f, &statbuf) == 0; |
810 | +} |
811 | + |
812 | +static bool lxcapi_is_defined(struct lxc_container *c) |
813 | +{ |
814 | + struct stat statbuf; |
815 | + bool ret = false; |
816 | + int statret; |
817 | + |
818 | + if (!c) |
819 | + return false; |
820 | + |
821 | + if (lxclock(c->privlock, 0)) |
822 | + return false; |
823 | + if (!c->configfile) |
824 | + goto out; |
825 | + statret = stat(c->configfile, &statbuf); |
826 | + if (statret != 0) |
827 | + goto out; |
828 | + ret = true; |
829 | + |
830 | +out: |
831 | + lxcunlock(c->privlock); |
832 | + return ret; |
833 | +} |
834 | + |
835 | +static const char *lxcapi_state(struct lxc_container *c) |
836 | +{ |
837 | + const char *ret; |
838 | + lxc_state_t s; |
839 | + |
840 | + if (!c) |
841 | + return NULL; |
842 | + if (lxclock(c->slock, 0)) |
843 | + return NULL; |
844 | + s = lxc_getstate(c->name); |
845 | + ret = lxc_state2str(s); |
846 | + lxcunlock(c->slock); |
847 | + |
848 | + return ret; |
849 | +} |
850 | + |
851 | +static bool lxcapi_is_running(struct lxc_container *c) |
852 | +{ |
853 | + const char *s; |
854 | + |
855 | + if (!c) |
856 | + return false; |
857 | + s = lxcapi_state(c); |
858 | + if (!s || strcmp(s, "STOPPED") == 0) |
859 | + return false; |
860 | + return true; |
861 | +} |
862 | + |
863 | +static bool lxcapi_freeze(struct lxc_container *c) |
864 | +{ |
865 | + int ret; |
866 | + if (!c) |
867 | + return false; |
868 | + |
869 | + if (lxclock(c->slock, 0)) |
870 | + return false; |
871 | + ret = lxc_freeze(c->name); |
872 | + lxcunlock(c->slock); |
873 | + if (ret) |
874 | + return false; |
875 | + return true; |
876 | +} |
877 | + |
878 | +static bool lxcapi_unfreeze(struct lxc_container *c) |
879 | +{ |
880 | + int ret; |
881 | + if (!c) |
882 | + return false; |
883 | + |
884 | + if (lxclock(c->slock, 0)) |
885 | + return false; |
886 | + ret = lxc_unfreeze(c->name); |
887 | + lxcunlock(c->slock); |
888 | + if (ret) |
889 | + return false; |
890 | + return true; |
891 | +} |
892 | + |
893 | +static pid_t lxcapi_init_pid(struct lxc_container *c) |
894 | +{ |
895 | + pid_t ret; |
896 | + if (!c) |
897 | + return -1; |
898 | + |
899 | + if (lxclock(c->slock, 0)) |
900 | + return -1; |
901 | + ret = get_init_pid(c->name); |
902 | + lxcunlock(c->slock); |
903 | + return ret; |
904 | +} |
905 | + |
906 | +static bool lxcapi_load_config(struct lxc_container *c, char *alt_file) |
907 | +{ |
908 | + bool ret = false; |
909 | + char *fname; |
910 | + if (!c) |
911 | + return false; |
912 | + |
913 | + fname = c->configfile; |
914 | + if (alt_file) |
915 | + fname = alt_file; |
916 | + if (!fname) |
917 | + return false; |
918 | + if (lxclock(c->slock, 0)) |
919 | + return false; |
920 | + if (!c->lxc_conf) |
921 | + c->lxc_conf = lxc_conf_init(); |
922 | + if (c->lxc_conf && !lxc_config_read(fname, c->lxc_conf)) |
923 | + ret = true; |
924 | + lxcunlock(c->slock); |
925 | + return ret; |
926 | +} |
927 | + |
928 | +static void lxcapi_want_daemonize(struct lxc_container *c) |
929 | +{ |
930 | + if (!c) |
931 | + return; |
932 | + c->daemonize = 1; |
933 | +} |
934 | + |
935 | +/* |
936 | + * I can't decide if it'd be more convenient for callers if we accept '...', |
937 | + * or a null-terminated array (i.e. execl vs execv) |
938 | + */ |
939 | +static bool lxcapi_start(struct lxc_container *c, int useinit, char ** argv) |
940 | +{ |
941 | + int ret; |
942 | + struct lxc_conf *conf; |
943 | + int daemonize = 0; |
944 | + char *default_args[] = { |
945 | + "/sbin/init", |
946 | + '\0', |
947 | + }; |
948 | + |
949 | + /* container exists */ |
950 | + if (!c) |
951 | + return false; |
952 | + /* container has been setup */ |
953 | + if (!c->lxc_conf) |
954 | + return false; |
955 | + |
956 | + /* is this app meant to be run through lxcinit, as in lxc-execute? */ |
957 | + if (useinit && !argv) |
958 | + return false; |
959 | + |
960 | + if (lxclock(c->privlock, 0)) |
961 | + return false; |
962 | + conf = c->lxc_conf; |
963 | + daemonize = c->daemonize; |
964 | + lxcunlock(c->privlock); |
965 | + |
966 | + if (useinit) { |
967 | + ret = lxc_execute(c->name, argv, 1, conf); |
968 | + return ret == 0 ? true : false; |
969 | + } |
970 | + |
971 | + if (!argv) |
972 | + argv = default_args; |
973 | + |
974 | + /* |
975 | + * say, I'm not sure - what locks do we want here? Any? |
976 | + * Is liblxc's locking enough here to protect the on disk |
977 | + * container? We don't want to exclude things like lxc_info |
978 | + * while container is running... |
979 | + */ |
980 | + if (daemonize) { |
981 | + if (!lxc_container_get(c)) |
982 | + return false; |
983 | + pid_t pid = fork(); |
984 | + if (pid < 0) { |
985 | + lxc_container_put(c); |
986 | + return false; |
987 | + } |
988 | + if (pid != 0) |
989 | + return true; |
990 | + /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ |
991 | + chdir("/"); |
992 | + close(0); |
993 | + close(1); |
994 | + close(2); |
995 | + open("/dev/null", O_RDONLY); |
996 | + open("/dev/null", O_RDWR); |
997 | + open("/dev/null", O_RDWR); |
998 | + setsid(); |
999 | + } |
1000 | + |
1001 | + if (putenv("container=lxc")) { |
1002 | + fprintf(stderr, "failed to set environment variable"); |
1003 | + if (daemonize) { |
1004 | + lxc_container_put(c); |
1005 | + exit(1); |
1006 | + } else { |
1007 | + return false; |
1008 | + } |
1009 | + } |
1010 | + |
1011 | +reboot: |
1012 | + conf->reboot = 0; |
1013 | + ret = lxc_start(c->name, argv, conf); |
1014 | + |
1015 | + if (conf->reboot) { |
1016 | + INFO("container requested reboot"); |
1017 | + conf->reboot = 0; |
1018 | + if (conf->maincmd_fd) |
1019 | + close(conf->maincmd_fd); |
1020 | + conf->maincmd_fd = 0; |
1021 | + goto reboot; |
1022 | + } |
1023 | + |
1024 | + if (daemonize) { |
1025 | + lxc_container_put(c); |
1026 | + exit (ret == 0 ? true : false); |
1027 | + } else { |
1028 | + return (ret == 0 ? true : false); |
1029 | + } |
1030 | +} |
1031 | + |
1032 | +/* |
1033 | + * note there MUST be an ending NULL |
1034 | + */ |
1035 | +static bool lxcapi_startl(struct lxc_container *c, int useinit, ...) |
1036 | +{ |
1037 | + va_list ap; |
1038 | + char **inargs = NULL, **temp; |
1039 | + int n_inargs = 0; |
1040 | + bool bret = false; |
1041 | + |
1042 | + /* container exists */ |
1043 | + if (!c) |
1044 | + return false; |
1045 | + |
1046 | + /* build array of arguments if any */ |
1047 | + va_start(ap, useinit); |
1048 | + while (1) { |
1049 | + char *arg; |
1050 | + arg = va_arg(ap, char *); |
1051 | + if (!arg) |
1052 | + break; |
1053 | + n_inargs++; |
1054 | + temp = realloc(inargs, n_inargs * sizeof(*inargs)); |
1055 | + if (!temp) |
1056 | + goto out; |
1057 | + inargs = temp; |
1058 | + inargs[n_inargs - 1] = strdup(arg); // not sure if it's safe not to copy |
1059 | + } |
1060 | + va_end(ap); |
1061 | + |
1062 | + /* add trailing NULL */ |
1063 | + if (n_inargs) { |
1064 | + n_inargs++; |
1065 | + temp = realloc(inargs, n_inargs * sizeof(*inargs)); |
1066 | + if (!temp) |
1067 | + goto out; |
1068 | + inargs = temp; |
1069 | + inargs[n_inargs - 1] = NULL; |
1070 | + } |
1071 | + |
1072 | + bret = lxcapi_start(c, useinit, inargs); |
1073 | + |
1074 | +out: |
1075 | + if (inargs) { |
1076 | + int i; |
1077 | + for (i = 0; i < n_inargs; i++) { |
1078 | + if (inargs[i]) |
1079 | + free(inargs[i]); |
1080 | + } |
1081 | + free(inargs); |
1082 | + } |
1083 | + |
1084 | + return bret; |
1085 | +} |
1086 | + |
1087 | +static bool lxcapi_stop(struct lxc_container *c) |
1088 | +{ |
1089 | + int ret; |
1090 | + |
1091 | + if (!c) |
1092 | + return false; |
1093 | + |
1094 | + ret = lxc_stop(c->name); |
1095 | + |
1096 | + return ret == 0; |
1097 | +} |
1098 | + |
1099 | +static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) |
1100 | +{ |
1101 | + int ret; |
1102 | + |
1103 | + if (!c) |
1104 | + return false; |
1105 | + |
1106 | + ret = lxc_wait(c->name, state, timeout); |
1107 | + return ret == 0; |
1108 | +} |
1109 | + |
1110 | +static bool valid_template(char *t) |
1111 | +{ |
1112 | + struct stat statbuf; |
1113 | + int statret; |
1114 | + |
1115 | + statret = stat(t, &statbuf); |
1116 | + if (statret == 0) |
1117 | + return true; |
1118 | + return false; |
1119 | +} |
1120 | + |
1121 | +/* |
1122 | + * create the standard expected container dir |
1123 | + */ |
1124 | +static bool create_container_dir(struct lxc_container *c) |
1125 | +{ |
1126 | + char *s; |
1127 | + int len, ret; |
1128 | + |
1129 | + len = strlen(LXCPATH) + strlen(c->name) + 2; |
1130 | + s = malloc(len); |
1131 | + if (!s) |
1132 | + return false; |
1133 | + ret = snprintf(s, len, "%s/%s", LXCPATH, c->name); |
1134 | + if (ret < 0 || ret >= len) { |
1135 | + free(s); |
1136 | + return false; |
1137 | + } |
1138 | + ret = mkdir(s, 0755); |
1139 | + if (ret) { |
1140 | + if (errno == EEXIST) |
1141 | + ret = 0; |
1142 | + else |
1143 | + SYSERROR("failed to create container path for %s\n", c->name); |
1144 | + } |
1145 | + free(s); |
1146 | + return ret == 0; |
1147 | +} |
1148 | + |
1149 | +/* |
1150 | + * backing stores not (yet) supported |
1151 | + * for ->create, argv contains the arguments to pass to the template, |
1152 | + * terminated by NULL. If no arguments, you can just pass NULL. |
1153 | + */ |
1154 | +static bool lxcapi_create(struct lxc_container *c, char *t, char **argv) |
1155 | +{ |
1156 | + bool bret = false; |
1157 | + pid_t pid; |
1158 | + int ret, status; |
1159 | + char *tpath = NULL; |
1160 | + int len, nargs = 0; |
1161 | + char **newargv; |
1162 | + |
1163 | + if (!c) |
1164 | + return false; |
1165 | + |
1166 | + len = strlen(LXCTEMPLATEDIR) + strlen(t) + strlen("/lxc-") + 1; |
1167 | + tpath = malloc(len); |
1168 | + if (!tpath) |
1169 | + return false; |
1170 | + ret = snprintf(tpath, len, "%s/lxc-%s", LXCTEMPLATEDIR, t); |
1171 | + if (ret < 0 || ret >= len) |
1172 | + goto out; |
1173 | + if (!valid_template(tpath)) { |
1174 | + ERROR("bad template: %s\n", t); |
1175 | + goto out; |
1176 | + } |
1177 | + |
1178 | + if (!create_container_dir(c)) |
1179 | + goto out; |
1180 | + |
1181 | + if (!c->save_config(c, NULL)) { |
1182 | + ERROR("failed to save starting configuration for %s\n", c->name); |
1183 | + goto out; |
1184 | + } |
1185 | + |
1186 | + /* we're going to fork. but since we'll wait for our child, we |
1187 | + don't need to lxc_container_get */ |
1188 | + |
1189 | + if (lxclock(c->slock, 0)) { |
1190 | + ERROR("failed to grab global container lock for %s\n", c->name); |
1191 | + goto out; |
1192 | + } |
1193 | + |
1194 | + pid = fork(); |
1195 | + if (pid < 0) { |
1196 | + SYSERROR("failed to fork task for container creation template\n"); |
1197 | + goto out_unlock; |
1198 | + } |
1199 | + |
1200 | + if (pid == 0) { // child |
1201 | + char *patharg, *namearg; |
1202 | + int i; |
1203 | + |
1204 | + close(0); |
1205 | + close(1); |
1206 | + close(2); |
1207 | + open("/dev/null", O_RDONLY); |
1208 | + open("/dev/null", O_RDWR); |
1209 | + open("/dev/null", O_RDWR); |
1210 | + |
1211 | + /* |
1212 | + * create our new array, pre-pend the template name and |
1213 | + * base args |
1214 | + */ |
1215 | + if (argv) |
1216 | + for (; argv[nargs]; nargs++) ; |
1217 | + nargs += 3; // template, path and name args |
1218 | + newargv = malloc(nargs * sizeof(*newargv)); |
1219 | + if (!newargv) |
1220 | + exit(1); |
1221 | + newargv[0] = t; |
1222 | + |
1223 | + len = strlen(LXCPATH) + strlen(c->name) + strlen("--path=") + 2; |
1224 | + patharg = malloc(len); |
1225 | + if (!patharg) |
1226 | + exit(1); |
1227 | + ret = snprintf(patharg, len, "--path=%s/%s", LXCPATH, c->name); |
1228 | + if (ret < 0 || ret >= len) |
1229 | + exit(1); |
1230 | + newargv[1] = patharg; |
1231 | + len = strlen("--name=") + strlen(c->name) + 1; |
1232 | + namearg = malloc(len); |
1233 | + if (!namearg) |
1234 | + exit(1); |
1235 | + ret = snprintf(namearg, len, "--name=%s", c->name); |
1236 | + if (ret < 0 || ret >= len) |
1237 | + exit(1); |
1238 | + newargv[2] = namearg; |
1239 | + |
1240 | + /* add passed-in args */ |
1241 | + if (argv) |
1242 | + for (i = 3; i < nargs; i++) |
1243 | + newargv[i] = argv[i-3]; |
1244 | + |
1245 | + /* add trailing NULL */ |
1246 | + nargs++; |
1247 | + newargv = realloc(newargv, nargs * sizeof(*newargv)); |
1248 | + if (!newargv) |
1249 | + exit(1); |
1250 | + newargv[nargs - 1] = NULL; |
1251 | + |
1252 | + /* execute */ |
1253 | + ret = execv(tpath, newargv); |
1254 | + SYSERROR("failed to execute template %s", tpath); |
1255 | + exit(1); |
1256 | + } |
1257 | + |
1258 | +again: |
1259 | + ret = waitpid(pid, &status, 0); |
1260 | + if (ret == -1) { |
1261 | + if (errno == -EINTR) |
1262 | + goto again; |
1263 | + SYSERROR("waitpid failed"); |
1264 | + goto out_unlock; |
1265 | + } |
1266 | + if (ret != pid) |
1267 | + goto again; |
1268 | + if (!WIFEXITED(status)) { // did not exit normally |
1269 | + // we could set an error code and string inside the |
1270 | + // container_struct here if we like |
1271 | + ERROR("container creation template exited abnormally\n"); |
1272 | + goto out_unlock; |
1273 | + } |
1274 | + |
1275 | + if (WEXITSTATUS(status) != 0) |
1276 | + ERROR("container creation template for %s exited with %d\n", |
1277 | + c->name, WEXITSTATUS(status)); |
1278 | + else |
1279 | + bret = true; |
1280 | + |
1281 | +out_unlock: |
1282 | + lxcunlock(c->slock); |
1283 | +out: |
1284 | + if (tpath) |
1285 | + free(tpath); |
1286 | + return bret; |
1287 | +} |
1288 | + |
1289 | +static bool lxcapi_shutdown(struct lxc_container *c, int timeout) |
1290 | +{ |
1291 | + bool retv; |
1292 | + pid_t pid; |
1293 | + |
1294 | + if (!c) |
1295 | + return false; |
1296 | + |
1297 | + if (!timeout) |
1298 | + timeout = -1; |
1299 | + if (!c->is_running(c)) |
1300 | + return true; |
1301 | + pid = c->init_pid(c); |
1302 | + if (pid <= 0) |
1303 | + return true; |
1304 | + kill(pid, SIGPWR); |
1305 | + retv = c->wait(c, "STOPPED", timeout); |
1306 | + if (timeout > 0) { |
1307 | + c->stop(c); |
1308 | + retv = c->wait(c, "STOPPED", 0); // 0 means don't wait |
1309 | + } |
1310 | + return retv; |
1311 | +} |
1312 | + |
1313 | +static bool lxcapi_createl(struct lxc_container *c, char *t, ...) |
1314 | +{ |
1315 | + bool bret = false; |
1316 | + char **args = NULL, **temp; |
1317 | + va_list ap; |
1318 | + int nargs = 0; |
1319 | + |
1320 | + if (!c) |
1321 | + return false; |
1322 | + |
1323 | + /* |
1324 | + * since we're going to wait for create to finish, I don't think we |
1325 | + * need to get a copy of the arguments. |
1326 | + */ |
1327 | + va_start(ap, t); |
1328 | + while (1) { |
1329 | + char *arg; |
1330 | + arg = va_arg(ap, char *); |
1331 | + if (!arg) |
1332 | + break; |
1333 | + nargs++; |
1334 | + temp = realloc(args, nargs * sizeof(*args)); |
1335 | + if (!temp) |
1336 | + goto out; |
1337 | + args = temp; |
1338 | + args[nargs - 1] = arg; |
1339 | + } |
1340 | + va_end(ap); |
1341 | + |
1342 | + bret = c->create(c, t, args); |
1343 | + |
1344 | +out: |
1345 | + if (args) |
1346 | + free(args); |
1347 | + return bret; |
1348 | +} |
1349 | + |
1350 | +static bool lxcapi_clear_config_item(struct lxc_container *c, char *key) |
1351 | +{ |
1352 | + int ret; |
1353 | + |
1354 | + if (!c || !c->lxc_conf) |
1355 | + return false; |
1356 | + if (lxclock(c->privlock, 0)) { |
1357 | + return false; |
1358 | + } |
1359 | + ret = lxc_clear_config_item(c->lxc_conf, key); |
1360 | + lxcunlock(c->privlock); |
1361 | + return ret == 0; |
1362 | +} |
1363 | + |
1364 | +static int lxcapi_get_config_item(struct lxc_container *c, char *key, char *retv, int inlen) |
1365 | +{ |
1366 | + int ret; |
1367 | + |
1368 | + if (!c || !c->lxc_conf) |
1369 | + return -1; |
1370 | + if (lxclock(c->privlock, 0)) { |
1371 | + return -1; |
1372 | + } |
1373 | + ret = lxc_get_config_item(c->lxc_conf, key, retv, inlen); |
1374 | + lxcunlock(c->privlock); |
1375 | + return ret; |
1376 | +} |
1377 | + |
1378 | +static int lxcapi_get_keys(struct lxc_container *c, char *key, char *retv, int inlen) |
1379 | +{ |
1380 | + if (!key) |
1381 | + return lxc_listconfigs(retv, inlen); |
1382 | + /* |
1383 | + * Support 'lxc.network.<idx>', i.e. 'lxc.network.0' |
1384 | + * This is an intelligent result to show which keys are valid given |
1385 | + * the type of nic it is |
1386 | + */ |
1387 | + if (!c || !c->lxc_conf) |
1388 | + return -1; |
1389 | + if (lxclock(c->privlock, 0)) |
1390 | + return -1; |
1391 | + int ret = -1; |
1392 | + if (strncmp(key, "lxc.network.", 12) == 0) |
1393 | + ret = lxc_list_nicconfigs(c->lxc_conf, key, retv, inlen); |
1394 | + lxcunlock(c->privlock); |
1395 | + return ret; |
1396 | +} |
1397 | + |
1398 | + |
1399 | +/* default config file - should probably come through autoconf */ |
1400 | +#define LXC_DEFAULT_CONFIG "/etc/lxc/lxc.conf" |
1401 | +static bool lxcapi_save_config(struct lxc_container *c, char *alt_file) |
1402 | +{ |
1403 | + if (!alt_file) |
1404 | + alt_file = c->configfile; |
1405 | + if (!alt_file) |
1406 | + return false; // should we write to stdout if no file is specified? |
1407 | + if (!c->lxc_conf) |
1408 | + if (!c->load_config(c, LXC_DEFAULT_CONFIG)) { |
1409 | + ERROR("Error loading default configuration file %s while saving %s\n", LXC_DEFAULT_CONFIG, c->name); |
1410 | + return false; |
1411 | + } |
1412 | + |
1413 | + FILE *fout = fopen(alt_file, "w"); |
1414 | + if (!fout) |
1415 | + return false; |
1416 | + if (lxclock(c->privlock, 0)) { |
1417 | + fclose(fout); |
1418 | + return false; |
1419 | + } |
1420 | + write_config(fout, c->lxc_conf); |
1421 | + fclose(fout); |
1422 | + lxcunlock(c->privlock); |
1423 | + return true; |
1424 | +} |
1425 | + |
1426 | +static bool lxcapi_destroy(struct lxc_container *c) |
1427 | +{ |
1428 | + pid_t pid; |
1429 | + int ret, status; |
1430 | + |
1431 | + if (!c) |
1432 | + return false; |
1433 | + |
1434 | + pid = fork(); |
1435 | + if (pid < 0) |
1436 | + return false; |
1437 | + if (pid == 0) { // child |
1438 | + ret = execlp("lxc-destroy", "lxc-destroy", "-n", c->name, NULL); |
1439 | + perror("execl"); |
1440 | + exit(1); |
1441 | + } |
1442 | + |
1443 | +again: |
1444 | + ret = waitpid(pid, &status, 0); |
1445 | + if (ret == -1) { |
1446 | + if (errno == -EINTR) |
1447 | + goto again; |
1448 | + perror("waitpid"); |
1449 | + return false; |
1450 | + } |
1451 | + if (ret != pid) |
1452 | + goto again; |
1453 | + if (!WIFEXITED(status)) { // did not exit normally |
1454 | + // we could set an error code and string inside the |
1455 | + // container_struct here if we like |
1456 | + return false; |
1457 | + } |
1458 | + |
1459 | + return WEXITSTATUS(status) == 0; |
1460 | +} |
1461 | + |
1462 | +static bool lxcapi_set_config_item(struct lxc_container *c, char *key, char *v) |
1463 | +{ |
1464 | + int ret; |
1465 | + bool b = false; |
1466 | + struct lxc_config_t *config; |
1467 | + |
1468 | + if (!c) |
1469 | + return false; |
1470 | + |
1471 | + if (lxclock(c->privlock, 0)) |
1472 | + return false; |
1473 | + |
1474 | + if (!c->lxc_conf) |
1475 | + c->lxc_conf = lxc_conf_init(); |
1476 | + if (!c->lxc_conf) |
1477 | + goto err; |
1478 | + config = lxc_getconfig(key); |
1479 | + if (!config) |
1480 | + goto err; |
1481 | + ret = config->cb(key, v, c->lxc_conf); |
1482 | + if (!ret) |
1483 | + b = true; |
1484 | + |
1485 | +err: |
1486 | + lxcunlock(c->privlock); |
1487 | + return b; |
1488 | +} |
1489 | + |
1490 | +static char *lxcapi_config_file_name(struct lxc_container *c) |
1491 | +{ |
1492 | + if (!c || !c->configfile) |
1493 | + return NULL; |
1494 | + return strdup(c->configfile); |
1495 | +} |
1496 | + |
1497 | +struct lxc_container *lxc_container_new(char *name) |
1498 | +{ |
1499 | + struct lxc_container *c; |
1500 | + int ret, len; |
1501 | + |
1502 | + c = malloc(sizeof(*c)); |
1503 | + if (!c) { |
1504 | + fprintf(stderr, "failed to malloc lxc_container\n"); |
1505 | + return NULL; |
1506 | + } |
1507 | + memset(c, 0, sizeof(*c)); |
1508 | + |
1509 | + c->name = malloc(strlen(name)+1); |
1510 | + if (!c->name) { |
1511 | + fprintf(stderr, "Error allocating lxc_container name\n"); |
1512 | + goto err; |
1513 | + } |
1514 | + strcpy(c->name, name); |
1515 | + |
1516 | + c->numthreads = 1; |
1517 | + c->slock = lxc_newlock(name); |
1518 | + if (!c->slock) { |
1519 | + fprintf(stderr, "failed to create lock\n"); |
1520 | + goto err; |
1521 | + } |
1522 | + |
1523 | + c->privlock = lxc_newlock(NULL); |
1524 | + if (!c->privlock) { |
1525 | + fprintf(stderr, "failed to alloc privlock\n"); |
1526 | + goto err; |
1527 | + } |
1528 | + |
1529 | + len = strlen(LXCDIR)+strlen(c->name)+strlen("/config")+2; |
1530 | + c->configfile = malloc(len); |
1531 | + if (!c->configfile) { |
1532 | + fprintf(stderr, "Error allocating config file pathname\n"); |
1533 | + goto err; |
1534 | + } |
1535 | + ret = snprintf(c->configfile, len, "%s/%s/config", LXCDIR, c->name); |
1536 | + if (ret < 0 || ret >= len) { |
1537 | + fprintf(stderr, "Error printing out config file name\n"); |
1538 | + goto err; |
1539 | + } |
1540 | + |
1541 | + if (file_exists(c->configfile)) |
1542 | + lxcapi_load_config(c, NULL); |
1543 | + |
1544 | + // assign the member functions |
1545 | + c->is_defined = lxcapi_is_defined; |
1546 | + c->state = lxcapi_state; |
1547 | + c->is_running = lxcapi_is_running; |
1548 | + c->freeze = lxcapi_freeze; |
1549 | + c->unfreeze = lxcapi_unfreeze; |
1550 | + c->init_pid = lxcapi_init_pid; |
1551 | + c->load_config = lxcapi_load_config; |
1552 | + c->want_daemonize = lxcapi_want_daemonize; |
1553 | + c->start = lxcapi_start; |
1554 | + c->startl = lxcapi_startl; |
1555 | + c->stop = lxcapi_stop; |
1556 | + c->config_file_name = lxcapi_config_file_name; |
1557 | + c->wait = lxcapi_wait; |
1558 | + c->set_config_item = lxcapi_set_config_item; |
1559 | + c->destroy = lxcapi_destroy; |
1560 | + c->save_config = lxcapi_save_config; |
1561 | + c->get_keys = lxcapi_get_keys; |
1562 | + c->create = lxcapi_create; |
1563 | + c->createl = lxcapi_createl; |
1564 | + c->shutdown = lxcapi_shutdown; |
1565 | + c->clear_config_item = lxcapi_clear_config_item; |
1566 | + c->get_config_item = lxcapi_get_config_item; |
1567 | + |
1568 | + /* we'll allow the caller to update these later */ |
1569 | + if (lxc_log_init("/var/log/lxccontainer.log", "trace", "lxc_container", 0)) { |
1570 | + fprintf(stderr, "failed to open log\n"); |
1571 | + goto err; |
1572 | + } |
1573 | + |
1574 | + /* |
1575 | + * default configuration file is $LXCDIR/$NAME/config |
1576 | + */ |
1577 | + |
1578 | + return c; |
1579 | + |
1580 | +err: |
1581 | + lxc_container_free(c); |
1582 | + return NULL; |
1583 | +} |
1584 | + |
1585 | +int lxc_get_wait_states(char **states) |
1586 | +{ |
1587 | + int i; |
1588 | + |
1589 | + if (states) |
1590 | + for (i=0; i<MAX_STATE; i++) |
1591 | + states[i] = lxc_state2str(i); |
1592 | + return MAX_STATE; |
1593 | +} |
1594 | |
1595 | === modified file 'debian/changelog' |
1596 | --- debian/changelog 2012-08-25 12:44:17 +0000 |
1597 | +++ debian/changelog 2012-08-25 17:20:25 +0000 |
1598 | @@ -1,20 +1,47 @@ |
1599 | -lxc (0.8.0~rc1-4ubuntu29) quantal; urgency=low |
1600 | - |
1601 | - [ Serge Hallyn ] |
1602 | - * fix lxcapi_start to not return true when it container failed to start. |
1603 | - * 0201-fix-mkdir-race: don't raise error if mkdir fails with EEXIST. |
1604 | - * 0202-make-api-start-reliable: have daemonized start through the api |
1605 | - wait until the container is RUNNING before returning true. If a 5 |
1606 | - second timeout is hit before the container is RUNNING, return false. |
1607 | - |
1608 | - [ Stéphane Graber ] |
1609 | - * python-lxc: in get_ips() if timeout is 1 don't wait one second before |
1610 | - returning. |
1611 | - * python-lxc: Add import time warning that the API isn't yet stable and |
1612 | - so may change at any point in the future. |
1613 | - |
1614 | - -- Stéphane Graber <stgraber@ubuntu.com> Sat, 25 Aug 2012 12:44:17 -0400 |
1615 | - |
1616 | +<<<<<<< TREE |
1617 | +lxc (0.8.0~rc1-4ubuntu29) quantal; urgency=low |
1618 | + |
1619 | + [ Serge Hallyn ] |
1620 | + * fix lxcapi_start to not return true when it container failed to start. |
1621 | + * 0201-fix-mkdir-race: don't raise error if mkdir fails with EEXIST. |
1622 | + * 0202-make-api-start-reliable: have daemonized start through the api |
1623 | + wait until the container is RUNNING before returning true. If a 5 |
1624 | + second timeout is hit before the container is RUNNING, return false. |
1625 | + |
1626 | + [ Stéphane Graber ] |
1627 | + * python-lxc: in get_ips() if timeout is 1 don't wait one second before |
1628 | + returning. |
1629 | + * python-lxc: Add import time warning that the API isn't yet stable and |
1630 | + so may change at any point in the future. |
1631 | + |
1632 | + -- Stéphane Graber <stgraber@ubuntu.com> Sat, 25 Aug 2012 12:44:17 -0400 |
1633 | + |
1634 | +======= |
1635 | +lxc (0.8.0~rc1-4ubuntu30) UNRELEASED; urgency=low |
1636 | + |
1637 | + * Remove debian/local/lxc-wait in favor of upstream's implementation. |
1638 | + This actually fixes (LP: #1020179) |
1639 | + |
1640 | + -- Stéphane Graber <stgraber@ubuntu.com> Sat, 25 Aug 2012 13:01:02 -0400 |
1641 | + |
1642 | +lxc (0.8.0~rc1-4ubuntu29) quantal; urgency=low |
1643 | + |
1644 | + [ Serge Hallyn ] |
1645 | + * fix lxcapi_start to not return true when it container failed to start. |
1646 | + * 0201-fix-mkdir-race: don't raise error if mkdir fails with EEXIST. |
1647 | + * 0202-make-api-start-reliable: have daemonized start through the api |
1648 | + wait until the container is RUNNING before returning true. If a 5 |
1649 | + second timeout is hit before the container is RUNNING, return false. |
1650 | + |
1651 | + [ Stéphane Graber ] |
1652 | + * python-lxc: in get_ips() if timeout is 1 don't wait one second before |
1653 | + returning. |
1654 | + * python-lxc: Add import time warning that the API isn't yet stable and |
1655 | + so may change at any point in the future. |
1656 | + |
1657 | + -- Stéphane Graber <stgraber@ubuntu.com> Sat, 25 Aug 2012 12:44:17 -0400 |
1658 | + |
1659 | +>>>>>>> MERGE-SOURCE |
1660 | lxc (0.8.0~rc1-4ubuntu28) quantal; urgency=low |
1661 | |
1662 | [ Stéphane Graber ] |
1663 | |
1664 | === removed file 'debian/local/lxc-wait' |
1665 | --- debian/local/lxc-wait 2012-03-21 08:20:06 +0000 |
1666 | +++ debian/local/lxc-wait 1970-01-01 00:00:00 +0000 |
1667 | @@ -1,171 +0,0 @@ |
1668 | -#!/bin/bash |
1669 | - |
1670 | -# (C) Copyright Canonical 2011,2012 |
1671 | - |
1672 | -# This library is free software; you can redistribute it and/or |
1673 | -# modify it under the terms of the GNU Lesser General Public |
1674 | -# License as published by the Free Software Foundation; either |
1675 | -# version 2.1 of the License, or (at your option) any later version. |
1676 | - |
1677 | -# This library is distributed in the hope that it will be useful, |
1678 | -# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1679 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1680 | -# Lesser General Public License for more details. |
1681 | - |
1682 | -# You should have received a copy of the GNU Lesser General Public |
1683 | -# License along with this library; if not, write to the Free Software |
1684 | -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1685 | - |
1686 | -usage() { |
1687 | - echo "Usage: lxc-wait --name=NAME --state=STATE" |
1688 | -} |
1689 | - |
1690 | -help() { |
1691 | - usage |
1692 | - echo |
1693 | - echo "lxc-wait waits for NAME container state to reach STATE" |
1694 | - echo |
1695 | - echo "Options :" |
1696 | - echo " -n, --name=NAME NAME for name of the container" |
1697 | - echo " -s, --state=STATE ORed states to wait for" |
1698 | - echo " STOPPED, STARTING, RUNNING, STOPPING," |
1699 | - echo " ABORTING, FREEZING, FROZEN" |
1700 | -} |
1701 | - |
1702 | -set -e |
1703 | - |
1704 | -valid_states=("STOPPED" "STARTING" "RUNNING" "STOPPING" |
1705 | -"ABORTING" "FREEZING" "FROZEN" "THAWED" "MAX_STATE") |
1706 | - |
1707 | -container_exists() { |
1708 | - local c=$1 |
1709 | - local list=`lxc-ls -1` |
1710 | - local name |
1711 | - |
1712 | - for name in $list; do |
1713 | - if [ "$c" = "$name" ]; then |
1714 | - echo "yes" |
1715 | - fi |
1716 | - echo "no" |
1717 | - done |
1718 | -} |
1719 | - |
1720 | -verify_state() { |
1721 | - local s="$1" |
1722 | - local i |
1723 | - |
1724 | - for((i=0;i<${#valid_states[@]};i++)); do |
1725 | - if [ "$s" = "${valid_states[$i]}" ]; then |
1726 | - echo "ok"; return; |
1727 | - fi |
1728 | - done |
1729 | - echo "bad"; return; |
1730 | -} |
1731 | - |
1732 | -badstate="" |
1733 | -verify_states() { |
1734 | - local states=$1 |
1735 | - local s |
1736 | - local v |
1737 | - |
1738 | - for s in $states; do |
1739 | - echo "verifying $s" |
1740 | - v=$(verify_state "$s") |
1741 | - if [ $v = "bad" ]; then |
1742 | - echo "bad"; |
1743 | - badstate="$s" |
1744 | - return; |
1745 | - fi |
1746 | - done |
1747 | - echo "ok"; |
1748 | -} |
1749 | - |
1750 | -state_is_in() { |
1751 | - local state=$1 |
1752 | - local states=$2 |
1753 | - local s |
1754 | - |
1755 | - for s in $states; do |
1756 | - if [ "$state" = "$s" ]; then |
1757 | - echo "yes"; return; |
1758 | - fi |
1759 | - done |
1760 | - echo "no" |
1761 | -} |
1762 | - |
1763 | -shortoptions='hn:s:' |
1764 | -longoptions='help,name:,states:' |
1765 | - |
1766 | -getopt=$(getopt -o $shortoptions --longoptions $longoptions -- "$@") |
1767 | -if [ $? != 0 ]; then |
1768 | - usage |
1769 | - exit 1; |
1770 | -fi |
1771 | - |
1772 | -eval set -- "$getopt" |
1773 | - |
1774 | -states="RUNNING" |
1775 | - |
1776 | -while true; do |
1777 | - case "$1" in |
1778 | - -h|--help) |
1779 | - help |
1780 | - exit 1 |
1781 | - ;; |
1782 | - -n|--name) |
1783 | - shift |
1784 | - lxc_name=$1 |
1785 | - shift |
1786 | - ;; |
1787 | - -s|--states) |
1788 | - shift |
1789 | - states=$1 |
1790 | - shift |
1791 | - ;; |
1792 | - --) |
1793 | - shift |
1794 | - break;; |
1795 | - *) |
1796 | - echo $1 |
1797 | - usage |
1798 | - exit 1 |
1799 | - ;; |
1800 | - esac |
1801 | -done |
1802 | - |
1803 | -if [ -z "$lxc_name" ]; then |
1804 | - echo "no container name specified" |
1805 | - usage |
1806 | - exit 1 |
1807 | -fi |
1808 | - |
1809 | -if [ "$(id -u)" != "0" ]; then |
1810 | - echo "This command has to be run as root" |
1811 | - exit 1 |
1812 | -fi |
1813 | - |
1814 | -type lxc-info > /dev/null || { echo "lxc-info not found."; exit 1; } |
1815 | - |
1816 | -v=$(container_exists $lxc_name) |
1817 | -if [ "$v" = "no" ]; then |
1818 | - echo "Container $lxc_name does not exist" |
1819 | - exit 1 |
1820 | -fi |
1821 | -states2="`echo $states | sed -e 's/|/ /g'`" |
1822 | -v=$(verify_states "${states2}") |
1823 | -if [ "$v" = "bad" ]; then |
1824 | - echo "invalid state $badstate in provided set" |
1825 | - exit 1 |
1826 | -fi |
1827 | - |
1828 | - |
1829 | -while [ 1 ]; do |
1830 | - o=`lxc-info -s -n $lxc_name` |
1831 | - s=`echo $o | awk '{ print $2 }'` |
1832 | - if [ $(state_is_in "$s" "${states2}") = "yes" ]; then |
1833 | - break; |
1834 | - fi |
1835 | - sleep 1 |
1836 | -done |
1837 | - |
1838 | -exit 0 |
1839 | |
1840 | === added file 'debian/patches/0201-fix-mkdir-race' |
1841 | --- debian/patches/0201-fix-mkdir-race 1970-01-01 00:00:00 +0000 |
1842 | +++ debian/patches/0201-fix-mkdir-race 2012-08-25 17:20:25 +0000 |
1843 | @@ -0,0 +1,24 @@ |
1844 | +Description: if mkdir fails with -EEXIST, let it be. |
1845 | +Author: Serge Hallyn <serge.hallyn@ubuntu.com> |
1846 | +Forwarded: yes |
1847 | + |
1848 | +Index: lxc/src/lxc/cgroup.c |
1849 | +=================================================================== |
1850 | +--- lxc.orig/src/lxc/cgroup.c 2012-08-24 10:51:33.375144000 -0500 |
1851 | ++++ lxc/src/lxc/cgroup.c 2012-08-24 10:59:42.293491913 -0500 |
1852 | +@@ -357,9 +357,12 @@ |
1853 | + } |
1854 | + |
1855 | + /* if /sys/fs/cgroup/<cgroup>/<init-cgroup>/lxc does not exist, create it */ |
1856 | +- if (access(cgparent, F_OK) && mkdir(cgparent, 0755)) { |
1857 | +- SYSERROR("failed to create '%s' directory", cgparent); |
1858 | +- return -1; |
1859 | ++ if (access(cgparent, F_OK)) { |
1860 | ++ ret = mkdir(cgparent, 0755); |
1861 | ++ if (ret == -1 && errno == EEXIST) { |
1862 | ++ SYSERROR("failed to create '%s' directory", cgparent); |
1863 | ++ return -1; |
1864 | ++ } |
1865 | + } |
1866 | + |
1867 | + /* |
1868 | |
1869 | === renamed file 'debian/patches/0201-fix-mkdir-race' => 'debian/patches/0201-fix-mkdir-race.moved' |
1870 | === added file 'debian/patches/0202-make-api-start-reliable' |
1871 | --- debian/patches/0202-make-api-start-reliable 1970-01-01 00:00:00 +0000 |
1872 | +++ debian/patches/0202-make-api-start-reliable 2012-08-25 17:20:25 +0000 |
1873 | @@ -0,0 +1,58 @@ |
1874 | +Index: lxc/src/lxc/lxccontainer.c |
1875 | +=================================================================== |
1876 | +--- lxc.orig/src/lxc/lxccontainer.c 2012-08-24 11:46:43.112003985 -0500 |
1877 | ++++ lxc/src/lxc/lxccontainer.c 2012-08-24 12:22:40.683925258 -0500 |
1878 | +@@ -244,6 +244,26 @@ |
1879 | + c->daemonize = 1; |
1880 | + } |
1881 | + |
1882 | ++static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) |
1883 | ++{ |
1884 | ++ int ret; |
1885 | ++ |
1886 | ++ if (!c) |
1887 | ++ return false; |
1888 | ++ |
1889 | ++ ret = lxc_wait(c->name, state, timeout); |
1890 | ++ return ret == 0; |
1891 | ++} |
1892 | ++ |
1893 | ++ |
1894 | ++static bool wait_on_daemonized_start(struct lxc_container *c) |
1895 | ++{ |
1896 | ++ /* we'll probably want to make this timeout configurable? */ |
1897 | ++ int timeout = 5; |
1898 | ++ |
1899 | ++ return lxcapi_wait(c, "RUNNING", timeout); |
1900 | ++} |
1901 | ++ |
1902 | + /* |
1903 | + * I can't decide if it'd be more convenient for callers if we accept '...', |
1904 | + * or a null-terminated array (i.e. execl vs execv) |
1905 | +@@ -298,7 +318,7 @@ |
1906 | + return false; |
1907 | + } |
1908 | + if (pid != 0) |
1909 | +- return true; |
1910 | ++ return wait_on_daemonized_start(c); |
1911 | + /* like daemon(), chdir to / and redirect 0,1,2 to /dev/null */ |
1912 | + chdir("/"); |
1913 | + close(0); |
1914 | +@@ -408,17 +428,6 @@ |
1915 | + return ret == 0; |
1916 | + } |
1917 | + |
1918 | +-static bool lxcapi_wait(struct lxc_container *c, char *state, int timeout) |
1919 | +-{ |
1920 | +- int ret; |
1921 | +- |
1922 | +- if (!c) |
1923 | +- return false; |
1924 | +- |
1925 | +- ret = lxc_wait(c->name, state, timeout); |
1926 | +- return ret == 0; |
1927 | +-} |
1928 | +- |
1929 | + static bool valid_template(char *t) |
1930 | + { |
1931 | + struct stat statbuf; |
1932 | |
1933 | === renamed file 'debian/patches/0202-make-api-start-reliable' => 'debian/patches/0202-make-api-start-reliable.moved' |