// callback for find_file_in_subdirs()
void found_subdir_in_dir(const char *dir) {
	debug(D_CGROUP, "examining cgroup dir '%s'", dir);

	struct cgroup *cg = cgroup_find(dir);
	if(!cg) {
		if(*dir && cgroup_max_depth > 0) {
			int depth = 0;
			const char *s;

			for(s = dir; *s ;s++)
				if(unlikely(*s == '/'))
					depth++;

			if(depth > cgroup_max_depth) {
				info("cgroup '%s' is too deep (%d, while max is %d)", dir, depth, cgroup_max_depth);
				return;
			}
		}
		debug(D_CGROUP, "will add dir '%s' as cgroup", dir);
		cg = cgroup_add(dir);
	}

	if(cg) cg->available = 1;
}
Exemple #2
0
int main(int argc, char *argv[]) {
    int rc, sync[2];

    pid_t pid = -1;

    siginfo_t status;

    struct mount *mounts = NULL;
    struct netif *netifs = NULL;
    struct cgroup *cgroups = NULL;
    struct user *users = NULL;
#if HAVE_LIBCAP_NG
    struct capability *caps = NULL;
#endif

    char *master;
    _close_ int master_fd = -1;

    char ephemeral_dir[] = "/tmp/pflask-ephemeral-XXXXXX";

    int clone_flags = CLONE_NEWNS  |
                          CLONE_NEWIPC |
                          CLONE_NEWPID |
#ifdef CLONE_NEWCGROUP
                          CLONE_NEWCGROUP |
#endif
                          CLONE_NEWUTS;

    struct gengetopt_args_info args;

    if (cmdline_parser(argc, argv, &args) != 0)
        return 1;

    for (unsigned int i = 0; i < args.mount_given; i++) {
        validate_optlist("--mount", args.mount_arg[i]);
        mount_add_from_spec(&mounts, args.mount_arg[i]);
    }

    for (unsigned int i = 0; i < args.netif_given; i++) {
        clone_flags |= CLONE_NEWNET;

        if (args.netif_arg != NULL) {
            validate_optlist("--netif", args.netif_arg[i]);
            netif_add_from_spec(&netifs, args.netif_arg[i]);
        }
    }

    if (args.user_given && !args.user_map_given) {
        uid_t uid;
        gid_t gid;

        clone_flags |= CLONE_NEWUSER;

        if (user_get_uid_gid(args.user_arg, &uid, &gid)) {
            user_add_map(&users, 'u', uid, uid, 1);
            user_add_map(&users, 'g', gid, gid, 1);
        }
    }

    for (unsigned int i = 0; i < args.user_map_given; i++) {
        size_t count;
        uid_t id, host_id;

        char *start = args.user_map_arg[i], *end = NULL;

        validate_optlist("--user-map", args.user_map_arg[i]);

        clone_flags |= CLONE_NEWUSER;

        id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        host_id = strtoul(start, &end, 10);
        if (*end != ':')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        start = end + 1;

        count = strtoul(start, &end, 10);
        if (*end != '\0')
            fail_printf("Invalid value '%s' for --user-map",
                        args.user_map_arg[i]);

        user_add_map(&users, 'u', id, host_id, count);
        user_add_map(&users, 'g', id, host_id, count);
    }

    for (unsigned int i = 0; i < args.cgroup_given; i++)
        cgroup_add(&cgroups, args.cgroup_arg[i]);

#if HAVE_LIBCAP_NG
    for (unsigned int i = 0; i < args.caps_given; i++)
        capability_add(&caps, args.caps_arg[i]);
#endif

    if (args.no_userns_flag)
        clone_flags &= ~(CLONE_NEWUSER);

    if (args.no_mountns_flag)
        clone_flags &= ~(CLONE_NEWNS);

    if (args.no_netns_flag)
        clone_flags &= ~(CLONE_NEWNET);

    if (args.no_ipcns_flag)
        clone_flags &= ~(CLONE_NEWIPC);

    if (args.no_utsns_flag)
        clone_flags &= ~(CLONE_NEWUTS);

    if (args.no_pidns_flag)
        clone_flags &= ~(CLONE_NEWPID);

    if (args.attach_given) {
        master_fd = recv_pty(args.attach_arg);
        fail_if(master_fd < 0, "Invalid PID '%u'", args.attach_arg);

        process_pty(master_fd);
        return 0;
    }

    open_master_pty(&master_fd, &master);

    if (args.detach_flag)
        do_daemonize();

    sync_init(sync);

    if (args.ephemeral_flag) {
        if (!mkdtemp(ephemeral_dir))
            sysf_printf("mkdtemp()");
    }

    pid = do_clone(&clone_flags);

    if (!pid) {
        closep(&master_fd);

        rc = prctl(PR_SET_PDEATHSIG, SIGKILL);
        sys_fail_if(rc < 0, "prctl(PR_SET_PDEATHSIG)");

        rc = setsid();
        sys_fail_if(rc < 0, "setsid()");

        sync_barrier_parent(sync, SYNC_START);

        sync_close(sync);

        open_slave_pty(master);

        setup_user(args.user_arg);

        if (args.hostname_given) {
            rc = sethostname(args.hostname_arg,
                             strlen(args.hostname_arg));
            sys_fail_if(rc < 0, "Error setting hostname");
        }

        setup_mount(mounts, args.chroot_arg, args.ephemeral_flag ?
                                               ephemeral_dir : NULL);

        if (args.chroot_given) {
            setup_nodes(args.chroot_arg);

            setup_ptmx(args.chroot_arg);

            setup_symlinks(args.chroot_arg);

            setup_console(args.chroot_arg, master);

            do_chroot(args.chroot_arg);
        }

        if (clone_flags & CLONE_NEWNET)
            config_netif();

        umask(0022);

#if HAVE_LIBCAP_NG
        setup_capabilities(caps);
#endif

        if (args.chdir_given) {
            rc = chdir(args.chdir_arg);
            sys_fail_if(rc < 0, "Error changing cwd");
        }

        if (args.chroot_given) {
            char *term = getenv("TERM");

            if (!args.keepenv_flag)
                clearenv();

            setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);
            setenv("USER", args.user_arg, 1);
            setenv("LOGNAME", args.user_arg, 1);
            if (term)
                setenv("TERM", term, 1);
        }

        for (unsigned int i = 0; i < args.setenv_given; i++) {
            rc = putenv(strdup(args.setenv_arg[i]));
            sys_fail_if(rc != 0, "Error setting environment");
        }

        setenv("container", "pflask", 1);

        if (argc > optind)
            rc = execvpe(argv[optind], argv + optind, environ);
        else
            rc = execle("/bin/bash", "-bash", NULL, environ);

        sys_fail_if(rc < 0, "Error executing command");
    }

    sync_wait_child(sync, SYNC_START);

    if (args.chroot_given && (clone_flags & CLONE_NEWUSER))
        setup_console_owner(master, users);

    setup_cgroup(cgroups, pid);

    setup_netif(netifs, pid);

#ifdef HAVE_DBUS
    register_machine(pid, args.chroot_given ? args.chroot_arg : "");
#endif

    if (clone_flags & CLONE_NEWUSER)
        setup_user_map(users, pid);

    sync_wake_child(sync, SYNC_DONE);

    sync_close(sync);

    if (args.detach_flag)
        serve_pty(master_fd);
    else
        process_pty(master_fd);

    kill(pid, SIGKILL);

    rc = waitid(P_PID, pid, &status, WEXITED);
    sys_fail_if(rc < 0, "Error waiting for child");

    switch (status.si_code) {
    case CLD_EXITED:
        if (status.si_status != 0)
            err_printf("Child failed with code '%d'",
                       status.si_status);
        else
            ok_printf("Child exited");
        break;

    case CLD_KILLED:
        err_printf("Child was terminated by signal '%d'",
                   status.si_status);
        break;

    default:
        err_printf("Child failed");
        break;
    }

    sync_close(sync);

    clean_cgroup(cgroups);

    if (args.ephemeral_flag) {
        rc = rmdir(ephemeral_dir);
        sys_fail_if(rc != 0, "Error deleting ephemeral directory: %s",
                             ephemeral_dir);
    }

    cmdline_parser_free(&args);

    return status.si_status;
}