lxc_container.c 7.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 * Copyright IBM Corp. 2008
 *
 * lxc_container.c: file description
 *
 * Authors:
 *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include <config.h>

#ifdef WITH_LXC

#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <unistd.h>

#include "lxc_container.h"
#include "util.h"
37
#include "memory.h"
38
#include "veth.h"
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)

/**
 * lxcExecContainerInit:
 * @vmDef: Ptr to vm definition structure
 *
 * Exec the container init string.  The container init will replace then
 * be running in the current process
 *
 * Returns 0 on success or -1 in case of error
 */
static int lxcExecContainerInit(const lxc_vm_def_t *vmDef)
{
    int rc = -1;
    char* execString;
    size_t execStringLen = strlen(vmDef->init) + 1 + 5;

58 59
    if (VIR_ALLOC_N(execString, execStringLen) < 0) {
        lxcError(NULL, NULL, VIR_ERR_NO_MEMORY,
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
                 _("failed to calloc memory for init string: %s"),
                 strerror(errno));
        goto error_out;
    }

    strcpy(execString, "exec ");
    strcat(execString, vmDef->init);

    execl("/bin/sh", "sh", "-c", execString, (char*)NULL);
    lxcError(NULL, NULL, VIR_ERR_NO_MEMORY,
             _("execl failed to exec init: %s"), strerror(errno));

error_out:
    exit(rc);
}

/**
 * lxcSetContainerStdio:
 * @ttyName: Name of tty to set as the container console
 *
 * Sets the given tty as the primary conosole for the container as well as
 * stdout, stdin and stderr.
 *
 * Returns 0 on success or -1 in case of error
 */
85
static int lxcSetContainerStdio(const char *ttyPath)
86 87 88
{
    int rc = -1;
    int ttyfd;
89
    int open_max, i;
90 91 92 93 94 95 96

    if (setsid() < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("setsid failed: %s"), strerror(errno));
        goto error_out;
    }

97
    ttyfd = open(ttyPath, O_RDWR|O_NOCTTY);
98 99
    if (ttyfd < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
100
                 _("open(%s) failed: %s"), ttyPath, strerror(errno));
101 102 103 104 105 106 107 108 109
        goto error_out;
    }

    if (ioctl(ttyfd, TIOCSCTTY, NULL) < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("ioctl(TIOCSTTY) failed: %s"), strerror(errno));
        goto cleanup;
    }

110 111 112 113 114 115
    /* Just in case someone forget to set FD_CLOEXEC, explicitly
     * close all FDs before executing the container */
    open_max = sysconf (_SC_OPEN_MAX);
    for (i = 0; i < open_max; i++)
        if (i != ttyfd)
            close(i);
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151

    if (dup2(ttyfd, 0) < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("dup2(stdin) failed: %s"), strerror(errno));
        goto cleanup;
    }

    if (dup2(ttyfd, 1) < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("dup2(stdout) failed: %s"), strerror(errno));
        goto cleanup;
    }

    if (dup2(ttyfd, 2) < 0) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("dup2(stderr) failed: %s"), strerror(errno));
        goto cleanup;
    }

    rc = 0;

cleanup:
    close(ttyfd);

error_out:
    return rc;
}

/**
 * lxcExecWithTty:
 * @vm: Ptr to vm structure
 *
 * Sets container console and stdio and then execs container init
 *
 * Returns 0 on success or -1 in case of error
 */
152
static int lxcExecWithTty(lxc_vm_def_t *vmDef, char *ttyPath)
153 154 155
{
    int rc = -1;

156
    if(lxcSetContainerStdio(ttyPath) < 0) {
157 158 159 160 161 162 163 164 165
        goto exit_with_error;
    }

    lxcExecContainerInit(vmDef);

exit_with_error:
    exit(rc);
}

166 167
/**
 * lxcWaitForContinue:
168
 * @monitor: monitor FD from parent
169 170 171 172 173 174 175
 *
 * This function will wait for the container continue message from the
 * parent process.  It will send this message on the socket pair stored in
 * the vm structure once it has completed the post clone container setup.
 *
 * Returns 0 on success or -1 in case of error
 */
176
static int lxcWaitForContinue(int monitor)
177 178 179 180
{
    lxc_message_t msg;
    int readLen;

181 182 183
    readLen = saferead(monitor, &msg, sizeof(msg));
    if (readLen != sizeof(msg) ||
        msg != LXC_CONTINUE_MSG) {
184 185 186
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("Failed to read the container continue message: %s"),
                 strerror(errno));
187
        return -1;
188 189 190 191
    }

    DEBUG0("Received container continue message");

192
    return 0;
193 194 195 196 197 198 199 200 201 202
}

/**
 * lxcEnableInterfaces:
 * @vm: Pointer to vm structure
 *
 * This function will enable the interfaces for this container.
 *
 * Returns 0 on success or nonzero in case of error
 */
203
static int lxcEnableInterfaces(const lxc_vm_def_t *def)
204 205 206 207
{
    int rc = 0;
    const lxc_net_def_t *net;

208
    for (net = def->nets; net; net = net->next) {
209 210 211 212 213 214 215 216
        DEBUG("Enabling %s", net->containerVeth);
        rc =  vethInterfaceUpOrDown(net->containerVeth, 1);
        if (0 != rc) {
            goto error_out;
        }
    }

    /* enable lo device only if there were other net devices */
217
    if (def->nets)
218 219 220 221 222 223
        rc = vethInterfaceUpOrDown("lo", 1);

error_out:
    return rc;
}

224 225 226 227 228 229 230 231 232 233 234 235
/**
 * lxcChild:
 * @argv: Pointer to container arguments
 *
 * This function is run in the process clone()'d in lxcStartContainer.
 * Perform a number of container setup tasks:
 *     Setup container file system
 *     mount container /proca
 * Then exec's the container init
 *
 * Returns 0 on success or -1 in case of error
 */
236
int lxcChild( void *data )
237 238
{
    int rc = -1;
239 240
    lxc_child_argv_t *argv = data;
    lxc_vm_def_t *vmDef = argv->config;
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
    lxc_mount_t *curMount;
    int i;

    if (NULL == vmDef) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("lxcChild() passed invalid vm definition"));
        goto cleanup;
    }

    /* handle the bind mounts first before doing anything else that may */
    /* then access those mounted dirs */
    curMount = vmDef->mounts;
    for (i = 0; curMount; curMount = curMount->next) {
        rc = mount(curMount->source,
                   curMount->target,
                   NULL,
                   MS_BIND,
                   NULL);
        if (0 != rc) {
            lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                     _("failed to mount %s at %s for container: %s"),
                     curMount->source, curMount->target, strerror(errno));
            goto cleanup;
        }
    }

    /* mount /proc */
    rc = mount("lxcproc", "/proc", "proc", 0, NULL);
    if (0 != rc) {
        lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
                 _("failed to mount /proc for container: %s"),
                 strerror(errno));
        goto cleanup;
    }

276
    /* Wait for interface devices to show up */
277
    if (0 != (rc = lxcWaitForContinue(argv->monitor))) {
278 279 280 281
        goto cleanup;
    }

    /* enable interfaces */
282
    if (0 != (rc = lxcEnableInterfaces(vmDef))) {
283 284 285
        goto cleanup;
    }

286
    rc = lxcExecWithTty(vmDef, argv->ttyPath);
287 288 289 290 291 292 293
    /* this function will only return if an error occured */

cleanup:
    return rc;
}

#endif /* WITH_LXC */