if (t) { path_template = get_template_path(t); if (!path_template) return log_error(false, "Template \"%s\" not found", t); }
...
fd_rootfs = create_container_dir(c); if (fd_rootfs < 0) return log_error(false, "Failed to create container %s", c->name);
/* Mark that this container as being created */ partial_fd = create_partial(fd_rootfs, c); if (partial_fd < 0) { SYSERROR("Failed to mark container as being partially created"); goto out; }
/* No need to get disk lock bc we have the partial lock. */
mask = umask(0022);
/* Create the storage. * Note we can't do this in the same task as we use to execute the * template because of the way zfs works. * After you 'zfs create', zfs mounts the fs only in the initial * namespace. */ pid = fork(); ...
/* Save config file again to store the new rootfs location. */ if (!do_lxcapi_save_config(c, NULL)) { ERROR("Failed to save initial config for %s", c->name); /* Parent task won't see the storage driver in the * config so we delete it. */ bdev->ops->umount(bdev); bdev->ops->destroy(bdev); _exit(EXIT_FAILURE); }
_exit(EXIT_SUCCESS); }
if (!wait_exited(pid)) goto out_unlock;
/* Reload config to get the rootfs. */ lxc_conf_free(c->lxc_conf); c->lxc_conf = NULL;
if (!load_config_locked(c, c->configfile)) goto out_unlock;
if (!create_run_template(c, path_template, !!(flags & LXC_CREATE_QUIET), argv)) goto out_unlock;
/* Now clear out the lxc_conf we have, reload from the created * container. */ do_lxcapi_clear_config(c);
if (t) { if (!prepend_lxc_header(c->configfile, path_template, argv)) { ERROR("Failed to prepend header to config file"); goto out_unlock; } }
/* ... otherwise use default_args. */ if (!argv) { ... argv = default_args; }
/* I'm not sure what locks we want here.Any? Is liblxc's locking enough * here to protect the on disk container? We don't want to exclude * things like lxc_info while the container is running. */ if (c->daemonize) { bool started; char title[2048]; pid_t pid_first, pid_second;
pid_first = fork(); ...
/* first parent */ if (pid_first != 0) { ... /* Wait for container to tell us whether it started * successfully. */ started = wait_on_daemonized_start(handler, pid_first);
/* We don't really care if this doesn't print all the * characters. All that it means is that the proctitle will be * ugly. Similarly, we also don't care if setproctitle() fails. */ ret = strnprintf(title, sizeof(title), "[lxc monitor] %s %s", c->config_path, c->name); if (ret > 0) { ret = setproctitle(title); if (ret < 0) INFO("Failed to set process title to %s", title); else INFO("Set process title to %s", title); }
/* We fork() a second time to be reparented to init. Like * POSIX's daemon() function we change to "/" and redirect * std{in,out,err} to /dev/null. */ pid_second = fork(); if (pid_second < 0) { SYSERROR("Failed to fork first child process"); _exit(EXIT_FAILURE); }
/* second parent */ if (pid_second != 0) { free_init_cmd(init_cmd); lxc_put_handler(handler); _exit(EXIT_SUCCESS); }
/* second child */
/* change to / directory */ ret = chdir("/"); if (ret < 0) { SYSERROR("Failed to change to \"/\" directory"); _exit(EXIT_FAILURE); }
ret = inherit_fds(handler, true); if (ret < 0) _exit(EXIT_FAILURE);
/* redirect std{in,out,err} to /dev/null */ ret = null_stdfds(); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to /dev/null"); _exit(EXIT_FAILURE); }
/* become session leader */ ret = setsid(); if (ret < 0) TRACE("Process %d is already process group leader", lxc_raw_getpid()); } ...
reboot: ...
if (useinit) ret = lxc_execute(c->name, argv, 1, handler, c->config_path, c->daemonize, &c->error_num); else ret = lxc_start(argv, handler, c->config_path, c->daemonize, &c->error_num);
if (!attach_block_device(handler->conf)) { ERROR("Failed to attach block device"); ret = -1; goto out_abort; }
if (!cgroup_ops->monitor_create(cgroup_ops, handler)) { ERROR("Failed to create monitor cgroup"); ret = -1; goto out_abort; }
if (!cgroup_ops->monitor_delegate_controllers(cgroup_ops)) { ERROR("Failed to delegate controllers to monitor cgroup"); ret = -1; goto out_abort; }
if (!cgroup_ops->monitor_enter(cgroup_ops, handler)) { ERROR("Failed to enter monitor cgroup"); ret = -1; goto out_abort; }
ret = resolve_clone_flags(handler); ... ret = lxc_inherit_namespaces(handler); ...
/* If the rootfs is not a blockdev, prevent the container from marking * it readonly. * If the container is unprivileged then skip rootfs pinning. */ ret = lxc_rootfs_init(conf, !list_empty(&conf->id_map)); ...
ret = lxc_spawn(handler); if (ret < 0) { ERROR("Failed to spawn container \"%s\"", name); goto out_detach_blockdev; }
handler->conf->reboot = REBOOT_NONE;
ret = lxc_poll(name, handler); if (ret) { ERROR("LXC mainloop exited with error: %d", ret); goto out_delete_network; }
if (!handler->init_died && handler->pid > 0) { ERROR("Child process is not killed"); ret = -1; goto out_delete_network; }
status = lxc_wait_for_pid_status(handler->pid); if (status < 0) SYSERROR("Failed to retrieve status for %d", handler->pid);
/* If the child process exited but was not signaled, it didn't call * reboot. This should mean it was an lxc-execute which simply exited. * In any case, treat it as a 'halt'. */ if (WIFSIGNALED(status)) { int signal_nr = WTERMSIG(status); switch(signal_nr) { case SIGINT: /* halt */ DEBUG("%s(%d) - Container \"%s\" is halting", signal_name(signal_nr), signal_nr, name); break; case SIGHUP: /* reboot */ DEBUG("%s(%d) - Container \"%s\" is rebooting", signal_name(signal_nr), signal_nr, name); handler->conf->reboot = REBOOT_REQ; break; case SIGSYS: /* seccomp */ DEBUG("%s(%d) - Container \"%s\" violated its seccomp policy", signal_name(signal_nr), signal_nr, name); break; default: DEBUG("%s(%d) - Container \"%s\" init exited", signal_name(signal_nr), signal_nr, name); break; } } ... }
staticintlxc_spawn(struct lxc_handler *handler) { ... if (!lxc_sync_init(handler)) return-1;
ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, handler->data_sock); if (ret < 0) goto out_sync_fini; data_sock0 = handler->data_sock[0]; data_sock1 = handler->data_sock[1];
if (container_uses_namespace(handler, CLONE_NEWNET)) { ret = lxc_find_gateway_addresses(handler); if (ret) { ERROR("Failed to find gateway addresses"); goto out_sync_fini; } }
if (!cgroup_ops->payload_create(cgroup_ops, handler)) { ERROR("Failed creating cgroups"); goto out_delete_net; }
/* Create a process in a new set of namespaces. */ // 如果没有需要继承的namespace,则执行下面的代码 if (inherits_namespaces(handler)) { ... } else { int cgroup_fd = -EBADF;
/* * Tell the parent task it can begin to configure the container and wait * for it to finish. */ if (!lxc_sync_wake_parent(handler, START_SYNC_CONFIGURE)) goto out_error;
/* Unshare cgroup namespace after we have setup our cgroups. If we do it * earlier we end up with a wrong view of /proc/self/cgroup. For * example, assume we unshare(CLONE_NEWCGROUP) first, and then create * the cgroup for the container, say /sys/fs/cgroup/cpuset/lxc/c, then * /proc/self/cgroup would show us: * * 8:cpuset:/lxc/c * * whereas it should actually show * * 8:cpuset:/ */ if (handler->ns_unshare_flags & CLONE_NEWCGROUP) { ret = unshare(CLONE_NEWCGROUP); if (ret < 0) { if (errno != EINVAL) { SYSERROR("Failed to unshare CLONE_NEWCGROUP"); goto out_warn_father; }
handler->ns_clone_flags &= ~CLONE_NEWCGROUP; SYSINFO("Kernel does not support CLONE_NEWCGROUP"); } else { INFO("Unshared CLONE_NEWCGROUP"); } } ...
/* Setup the container, ip, names, utsname, ... */ ret = lxc_setup(handler); if (ret < 0) { ERROR("Failed to setup container \"%s\"", handler->name); goto out_warn_father; }
...
if (handler->conf->console.pty < 0 && handler->daemonize) { if (devnull_fd < 0) { devnull_fd = open_devnull(); if (devnull_fd < 0) goto out_warn_father; }
ret = set_stdfds(devnull_fd); if (ret < 0) { ERROR("Failed to redirect std{in,out,err} to \"/dev/null\""); goto out_warn_father; } }
...
setsid(); ...
/* Reset the environment variables the user requested in a clear * environment. */ ret = clearenv(); /* Don't error out though. */ if (ret < 0) SYSERROR("Failed to clear environment.");
ret = lxc_set_environment(handler->conf); if (ret < 0) goto out_warn_father;
ret = putenv("container=lxc"); if (ret < 0) { SYSERROR("Failed to set environment variable: container=lxc"); goto out_warn_father; }
...
/* * After this call, we are in error because this ops should not return * as it execs. */ handler->ops->start(handler, handler->data); ... return-1; }