Commit be909430 authored by jan.koester's avatar jan.koester
Browse files

test

parent f03e867b
Loading
Loading
Loading
Loading
+100 −1
Changes for src/containerctl.cpp: 100 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -35,14 +35,24 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *   containerctl edit web env=TZ=Europe/Berlin bind=/srv/www:/usr/share/nginx/html:ro
 *   containerctl start|stop|restart|rm web  containerctl kill web [HUP]
 *   containerctl log web [lines]             containerctl jobs
 *   containerctl shell web [command ...]     a shell (or command) inside
 *                                            the running container
 *
 * Settings are key=value with the keys of the API; a key given more than
 * once makes a list (env, arg, bind, dns, ...). */

#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <fcntl.h>
#include <sched.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>

@@ -131,12 +141,100 @@ Fields settings(int argc, char **argv, int from) {
    return f;
}

// containerctl shell: into the namespaces of the container's PID 1, its
// cgroup and its capability bounding set, then a shell or command as root
// of the container, on the caller's terminal
int shell(const std::string &name, int argc, char **argv, int from) {
    sysbus::Message r = call(containerd::method::GetContainer, {{"name", name}});
    if (r.get("state") != "running") {
        std::cerr << "containerctl: " << name << " " << (lang() == "de" ? "läuft nicht" : "isn't running") << std::endl;
        return 1;
    }
    pid_t pid = (pid_t)atoi(r.get("pid").c_str());
    std::string proc = "/proc/" + std::to_string(pid);

    // the capability bounding set and the cgroup of the container's PID 1
    unsigned long long bnd = ~0ULL;
    std::string cgroup;
    {
        std::ifstream st(proc + "/status");
        for (std::string l; std::getline(st, l);)
            if (l.compare(0, 7, "CapBnd:") == 0) bnd = strtoull(l.c_str() + 7, nullptr, 16);
        std::ifstream cg(proc + "/cgroup");
        for (std::string l; std::getline(cg, l);)
            if (l.compare(0, 3, "0::") == 0) cgroup = l.substr(3);
    }
    // open all namespaces first: after entering the mount namespace,
    // /proc is the container's
    const char *kinds[] = {"cgroup", "ipc", "uts", "net", "pid", "mnt"};
    int fds[6];
    for (int i = 0; i < 6; ++i)
        if ((fds[i] = open((proc + "/ns/" + kinds[i]).c_str(), O_RDONLY | O_CLOEXEC)) < 0) {
            std::cerr << "containerctl: " << proc << "/ns/" << kinds[i] << ": " << strerror(errno) << std::endl;
            return 1;
        }
    if (!cgroup.empty()) {
        std::ofstream procs("/sys/fs/cgroup" + cgroup + "/cgroup.procs");
        procs << getpid() << std::flush;
    }
    for (int i = 0; i < 6; ++i) {
        if (setns(fds[i], 0) != 0) {
            std::cerr << "containerctl: setns " << kinds[i] << ": " << strerror(errno) << std::endl;
            return 1;
        }
        close(fds[i]);
    }
    for (int c = 0; c < 64; ++c)
        if (!(bnd & (1ULL << c)) && prctl(PR_CAPBSET_DROP, c, 0, 0, 0) != 0 && errno == EINVAL) break;

    // the PID namespace takes effect for children
    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    pid_t child = fork();
    if (child < 0) {
        std::cerr << "containerctl: fork: " << strerror(errno) << std::endl;
        return 1;
    }
    if (child == 0) {
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);
        if (chdir("/root") != 0 && chdir("/") != 0) _exit(127);
        const char *term = getenv("TERM");
        std::string t = std::string("TERM=") + (term && *term ? term : "xterm");
        char hostname[256] = "";
        gethostname(hostname, sizeof(hostname) - 1);
        std::string ps1 = std::string("PS1=") + hostname + ":\\w# ";
        const char *env[] = {"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOME=/root",
                             "USER=root", "LOGNAME=root", t.c_str(), ps1.c_str(), nullptr};
        std::vector<char *> args;
        if (from < argc)
            for (int i = from; i < argc; ++i) args.push_back(argv[i]);
        else
            args.push_back((char *)"/bin/sh");
        args.push_back(nullptr);
        // searched in the container's PATH (execvpe() searches the
        // caller's: set it first)
        setenv("PATH", env[0] + 5, 1);
        execvpe(args[0], args.data(), (char **)env);
        if (from >= argc) {
            char *bash[] = {(char *)"bash", nullptr};
            execvpe("bash", bash, (char **)env);
        }
        std::cerr << "containerctl: " << args[0] << ": " << strerror(errno) << std::endl;
        _exit(127);
    }
    int status = 0;
    while (waitpid(child, &status, 0) < 0 && errno == EINTR) {}
    return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
}

int usage() {
    std::cerr << "usage: containerctl images | pull IMAGE | rmi IMAGE\n"
                 "       containerctl list | status NAME | log NAME [LINES] | jobs\n"
                 "       containerctl create NAME image=IMAGE [key=value ...]\n"
                 "       containerctl edit NAME key=value ...\n"
                 "       containerctl start|stop|restart|rm NAME | kill NAME [SIGNAL]\n";
                 "       containerctl start|stop|restart|rm NAME | kill NAME [SIGNAL]\n"
                 "       containerctl shell NAME [COMMAND ...]\n";
    return 2;
}

@@ -216,5 +314,6 @@ int main(int argc, char **argv) {
        return 0;
    }
    if (cmd == "rm") return follow(call(m::DeleteContainer, {{"name", arg}}));
    if (cmd == "shell") return shell(arg, argc, argv, 3);
    return usage();
}
+4 −1
Changes for src/containers.cpp: 4 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -291,9 +291,12 @@ Result startOne(const std::string &name) {
        if (!isDir(layerDir(layer))) return fail(tr("a layer of %1 is missing; pull it again", "eine Schicht von %1 fehlt; bitte neu laden", {s.image}), 500);
    if (s.network == "bridge" && !isDir("/sys/class/net/" + s.bridge + "/bridge"))
        return fail(tr("there is no bridge %1", "es gibt keine Bridge %1", {s.bridge}), 409);
    // a missing bind mount source is created as a directory (a file to
    // mount has to exist)
    for (auto &b : s.binds) {
        std::string src = b.substr(0, b.find(':'));
        if (!exists(src)) return fail(tr("the bind mount source %1 doesn't exist", "die Quelle %1 des Bind-Mounts gibt es nicht", {src}), 409);
        if (!exists(src) && !mkdirs(src, 0755))
            return fail(sysErr(tr("creating the bind mount source %1", "Anlegen der Bind-Mount-Quelle %1", {src}), errno), 500);
    }
    for (auto &d : s.devices) {
        std::string src = d.substr(0, d.find(':'));
+7 −1
Changes for src/devices.cpp: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -100,10 +100,16 @@ std::string attachDeviceFilter(const std::string &cgroup, const std::vector<Devi
    load.insns = (uint64_t)(uintptr_t)p.data();
    load.insn_cnt = (uint32_t)p.size();
    load.license = (uint64_t)(uintptr_t) "GPL";
    // without a verifier log first: a log longer than the buffer (a long
    // device list) fails the load with ENOSPC on its own
    int prog = (int)syscall(SYS_bpf, BPF_PROG_LOAD, &load, sizeof(load));
    if (prog < 0) {
        // again with the log, for the reason
        load.log_buf = (uint64_t)(uintptr_t)log;
        load.log_size = sizeof(log);
        load.log_level = 1;
    int prog = (int)syscall(SYS_bpf, BPF_PROG_LOAD, &load, sizeof(load));
        prog = (int)syscall(SYS_bpf, BPF_PROG_LOAD, &load, sizeof(load));
    }
    if (prog < 0) {
        int e = errno;
        std::string why = sysErr("bpf(BPF_PROG_LOAD)", e);
+7 −2
Changes for src/netlink.cpp: 7 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -136,17 +136,22 @@ bool parseMac(const std::string &s, uint8_t out[6]) {

} // namespace

std::string createVeth(const std::string &name, const std::string &peer, const std::string &peerMac) {
std::string createVeth(const std::string &name, const std::string &peer, const std::string &peerMac,
                       const std::string &hostMac) {
    Msg m(RTM_NEWLINK, NLM_F_CREATE | NLM_F_EXCL);
    m.fixed<ifinfomsg>()->ifi_family = AF_UNSPEC;
    m.str(IFLA_IFNAME, name);
    uint8_t mac[6];
    if (!hostMac.empty()) {
        if (!parseMac(hostMac, mac)) return "invalid MAC address " + hostMac;
        m.attr(IFLA_ADDRESS, mac, 6);
    }
    size_t info = m.begin(IFLA_LINKINFO);
    m.str(IFLA_INFO_KIND, "veth");
    size_t data = m.begin(IFLA_INFO_DATA);
    size_t p = m.begin(VETH_INFO_PEER);
    m.fixed<ifinfomsg>()->ifi_family = AF_UNSPEC;
    m.str(IFLA_IFNAME, peer);
    uint8_t mac[6];
    if (!peerMac.empty()) {
        if (!parseMac(peerMac, mac)) return "invalid MAC address " + peerMac;
        m.attr(IFLA_ADDRESS, mac, 6);
+4 −2
Changes for src/netlink.h: 4 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -38,8 +38,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

namespace containerd::nl {

// peerMac: "02:.." for the peer (the container's end), "" for a random one
std::string createVeth(const std::string &name, const std::string &peer, const std::string &peerMac = "");
// peerMac: "02:.." for the peer (the container's end), hostMac for name
// (the host's end); "" for a random one
std::string createVeth(const std::string &name, const std::string &peer, const std::string &peerMac = "",
                       const std::string &hostMac = "");
std::string setMaster(const std::string &ifname, const std::string &bridge);
std::string setUp(const std::string &ifname);
std::string moveToNetns(const std::string &ifname, pid_t pid);
Loading