Merge lp:~ubuntu-branches/ubuntu/quantal/lxc/quantal-201208251712 into lp:ubuntu/quantal/lxc

Proposed by Ubuntu Package Importer
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
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+121314@code.launchpad.net

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://bugs.launchpad.net/udd linking to this merge proposal.

(this is an automatically generated message)

To post a comment you must log in.

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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'

Subscribers

People subscribed via source and target branches