<commit_before>/*
 * lxc: linux Container library
 *
 * (C) Copyright IBM Corp. 2007, 2008
 *
 * Authors:
 * <NAME> <daniel.lezcano at free.fr>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#define _GNU_SOURCE
#include "config.h"

#include <alloca.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>

#if HAVE_LIBCAP
#include <sys/capability.h>
#endif

#ifndef HAVE_DECL_PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24
#endif

#ifndef HAVE_DECL_PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif

#ifndef HAVE_DECL_PR_GET_NO_NEW_PRIVS
#define PR_GET_NO_NEW_PRIVS 39
#endif

#include "af_unix.h"
#include "bdev.h"
#include "caps.h"
#include "cgroup.h"
#include "commands.h"
#include "conf.h"
#include "console.h"
#include "error.h"
#include "log.h"
#include "lxclock.h"
#include "lxcseccomp.h"
#include "lxcutmp.h"
#include "mainloop.h"
#include "monitor.h"
#include "namespace.h"
#include "start.h"
#include "sync.h"
#include "utils.h"
#include "lsm/lsm.h"

lxc_log_define(lxc_start, lxc);

extern void mod_all_rdeps(struct lxc_container *c, bool inc);
static bool do_destroy_container(struct lxc_conf *conf);
static int lxc_rmdir_onedev_wrapper(void *data);
static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
					    const char *name);

static void print_top_failing_dir(const char *path)
{
	size_t len = strlen(path);
	char *copy = alloca(len + 1), *p, *e, saved;
	strcpy(copy, path);

	p = copy;
	e = copy + len;
	while (p < e) {
		while (p < e && *p == '/')
			p++;
		while (p < e && *p != '/')
			p++;
		saved = *p;
		*p = '\0';
		if (access(copy, X_OK)) {
			SYSERROR("Could not access %s. Please grant it x "
				 "access, or add an ACL for the container "
				 "root.", copy);
			return;
		}
		*p = saved;
	}
}

static void close_ns(int ns_fd[LXC_NS_MAX])
{
	int i;

	for (i = 0; i < LXC_NS_MAX; i++) {
		if (ns_fd[i] > -1) {
			close(ns_fd[i]);
			ns_fd[i] = -1;
		}
	}
}

/* preserve_ns: open /proc/@pid/ns/@ns for each namespace specified
 * in clone_flags.
 * Return true on success, false on failure.
 */
static bool preserve_ns(int ns_fd[LXC_NS_MAX], int clone_flags, pid_t pid)
{
	int i, ret;

	for (i = 0; i < LXC_NS_MAX; i++)
		ns_fd[i] = -1;

	ret = lxc_preserve_ns(pid, "");
	if (ret < 0) {
		SYSERROR("Kernel does not support attaching to namespaces.");
		return false;
	} else {
		close(ret);
	}

	for (i = 0; i < LXC_NS_MAX; i++) {
		if ((clone_flags & ns_info[i].clone_flag) == 0)
			continue;
		ns_fd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
		if (ns_fd[i] < 0)
			goto error;
	}

	return true;

error:
	if (errno == ENOENT)
		SYSERROR("Kernel does not support attaching to %s namespaces.", ns_info[i].proc_name);
	else
		SYSERROR("Failed to open file descriptor for %s namespace: %s.", ns_info[i].proc_name, strerror(errno));
	close_ns(ns_fd);
	return false;
}

static int attach_ns(const int ns_fd[LXC_NS_MAX]) {
	int i;

	for (i = 0; i < LXC_NS_MAX; i++) {
		if (ns_fd[i] < 0)
			continue;

		if (setns(ns_fd[i], 0) != 0)
			goto error;
	}
	return 0;

error:
	SYSERROR("Failed to attach %s namespace.", ns_info[i].proc_name);
	return -1;
}

static int match_fd(int fd)
{
	return (fd == 0 || fd == 1 || fd == 2);
}

/* Check for any fds we need to close.
 * - If fd_to_ignore != -1, then if we find that fd open we will ignore it.
 * - By default we warn about open fds we find.
 * - If closeall is true, we will close open fds.
 * - If lxc-start was passed "-C", then conf->close_all_fds will be true, in
 *   which case we also close all open fds.
 * - A daemonized container will always pass closeall=true.
 */
int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore)
{
	struct dirent *direntp;
	int fd, fddir;
	DIR *dir;

	if (conf && conf->close_all_fds)
		closeall = true;

restart:
	dir = opendir("/proc/self/fd");
	if (!dir) {
		WARN("Failed to open directory: %s.", strerror(errno));
		return -1;
	}

	fddir = dirfd(dir);

	while ((direntp = readdir(dir))) {
		if (!direntp)
			break;

		if (!strcmp(direntp->d_name, "."))
			continue;

		if (!strcmp(direntp->d_name, ".."))
			continue;

		if (lxc_safe_int(direntp->d_name, &fd) < 0) {
			INFO("Could not parse file descriptor for: %s", direntp->d_name);
			continue;
		}

		if (fd == fddir || fd == lxc_log_fd || fd == fd_to_ignore)
			continue;

		if (current_config && fd == current_config->logfd)
			continue;

		if (match_fd(fd))
			continue;

		if (closeall) {
			close(fd);
			closedir(dir);
			INFO("Closed inherited fd: %d.", fd);
			goto restart;
		}
		WARN("Inherited fd: %d.", fd);
	}

	/* Only enable syslog at this point to avoid the above logging function
	 * to open a new fd and make the check_inherited function enter an
	 * infinite loop.
	 */
	lxc_log_enable_syslog();

	closedir(dir); /* cannot fail */
	return 0;
}

static int setup_signal_fd(sigset_t *oldmask)
{
	sigset_t mask;
	int fd;

	/* Block everything except serious error signals. */
	if (sigfillset(&mask) ||
	    sigdelset(&mask, SIGILL) ||
	    sigdelset(&mask, SIGSEGV) ||
	    sigdelset(&mask, SIGBUS) ||
	    sigdelset(&mask, SIGWINCH) ||
	    sigprocmask(SIG_BLOCK, &mask, oldmask)) {
		SYSERROR("Failed to set signal mask.");
		return -1;
	}

	fd = signalfd(-1, &mask, 0);
	if (fd < 0) {
		SYSERROR("Failed to create signal file descriptor.");
		return -1;
	}

	if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
		SYSERROR("Failed to set FD_CLOEXEC on the signal file descriptor: %d.", fd);
		close(fd);
		return -1;
	}

	DEBUG("Set SIGCHLD handler with file descriptor: %d.", fd);

	return fd;
}

static int signal_handler(int fd, uint32_t events, void *data,
			  struct lxc_epoll_descr *descr)
{
	struct signalfd_siginfo siginfo;
	siginfo_t info;
	int ret;
	pid_t *pid = data;
	bool init_died = false;

	ret = read(fd, &siginfo, sizeof(siginfo));
	if (ret < 0) {
		ERROR("Failed to read signal info from signal file descriptor: %d.", fd);
		return -1;
	}

	if (ret != sizeof(siginfo)) {
		ERROR("Unexpected size for siginfo struct.");
		return -1;
	}

	/* Check whether init is running. */
	info.si_pid = 0;
	ret = waitid(P_PID, *pid, &info, WEXITED | WNOWAIT | WNOHANG);
	if (ret == 0 && info.si_pid == *pid)
		init_died = true;

	if (siginfo.ssi_signo != SIGCHLD) {
		kill(*pid, siginfo.ssi_signo);
		INFO("Forwarded signal %d to pid %d.", siginfo.ssi_signo, *pid);
		return init_died ? 1 : 0;
	}

	if (siginfo.ssi_code == CLD_STOPPED) {
		INFO("Container init process was stopped.");
		return init_died ? 1 : 0;
	} else if (siginfo.ssi_code == CLD_CONTINUED) {
		INFO("Container init process was continued.");
		return init_died ? 1 : 0;
	}

	/* More robustness, protect ourself from a SIGCHLD sent
	 * by a process different from the container init.
	 */
	if (siginfo.ssi_pid != *pid) {
		NOTICE("Received SIGCHLD from pid %d instead of container init %d.", siginfo.ssi_pid, *pid);
		return init_died ? 1 : 0;
	}

	DEBUG("Container init process %d exited.", *pid);
	return 1;
}

int lxc_set_state(const char *name, struct lxc_handler *handler,
		  lxc_state_t state)
{
	ssize_t ret;
	struct lxc_list *cur, *next;
	struct state_client *client;
	struct lxc_msg msg = {.type = lxc_msg_state, .value = state};

	process_lock();
	/* Only set state under process lock held so that we don't cause
	 * lxc_cmd_state_server() to miss a state.
	 */
	handler->state = state;
	TRACE("set container state to %s", lxc_state2str(state));

	if (lxc_list_empty(&handler->state_clients)) {
		TRACE("no state clients registered");
		process_unlock();
		return 0;<commit_msg>start: send state to legacy lxc-monitord state server even if no state clients registered<commit_after>/*
 * lxc: linux Container library
 *
 * (C) Copyright IBM Corp. 2007, 2008
 *
 * Authors:
 * <NAME> <<EMAIL>.lezcano at free.fr>
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#define _GNU_SOURCE
#include "config.h"

#include <alloca.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>

#if HAVE_LIBCAP
#include <sys/capability.h>
#endif

#ifndef HAVE_DECL_PR_CAPBSET_DROP
#define PR_CAPBSET_DROP 24
#endif

#ifndef HAVE_DECL_PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS 38
#endif

#ifndef HAVE_DECL_PR_GET_NO_NEW_PRIVS
#define PR_GET_NO_NEW_PRIVS 39
#endif

#include "af_unix.h"
#include "bdev.h"
#include "caps.h"
#include "cgroup.h"
#include "commands.h"
#include "conf.h"
#include "console.h"
#include "error.h"
#include "log.h"
#include "lxclock.h"
#include "lxcseccomp.h"
#include "lxcutmp.h"
#include "mainloop.h"
#include "monitor.h"
#include "namespace.h"
#include "start.h"
#include "sync.h"
#include "utils.h"
#include "lsm/lsm.h"

lxc_log_define(lxc_start, lxc);

extern void mod_all_rdeps(struct lxc_container *c, bool inc);
static bool do_destroy_container(struct lxc_conf *conf);
static int lxc_rmdir_onedev_wrapper(void *data);
static void lxc_destroy_container_on_signal(struct lxc_handler *handler,
					    const char *name);

static void print_top_failing_dir(const char *path)
{
	size_t len = strlen(path);
	char *copy = alloca(len + 1), *p, *e, saved;
	strcpy(copy, path);

	p = copy;
	e = copy + len;
	while (p < e) {
		while (p < e && *p == '/')
			p++;
		while (p < e && *p != '/')
			p++;
		saved = *p;
		*p = '\0';
		if (access(copy, X_OK)) {
			SYSERROR("Could not access %s. Please grant it x "
				 "access, or add an ACL for the container "
				 "root.", copy);
			return;
		}
		*p = saved;
	}
}

static void close_ns(int ns_fd[LXC_NS_MAX])
{
	int i;

	for (i = 0; i < LXC_NS_MAX; i++) {
		if (ns_fd[i] > -1) {
			close(ns_fd[i]);
			ns_fd[i] = -1;
		}
	}
}

/* preserve_ns: open /proc/@pid/ns/@ns for each namespace specified
 * in clone_flags.
 * Return true on success, false on failure.
 */
static bool preserve_ns(int ns_fd[LXC_NS_MAX], int clone_flags, pid_t pid)
{
	int i, ret;

	for (i = 0; i < LXC_NS_MAX; i++)
		ns_fd[i] = -1;

	ret = lxc_preserve_ns(pid, "");
	if (ret < 0) {
		SYSERROR("Kernel does not support attaching to namespaces.");
		return false;
	} else {
		close(ret);
	}

	for (i = 0; i < LXC_NS_MAX; i++) {
		if ((clone_flags & ns_info[i].clone_flag) == 0)
			continue;
		ns_fd[i] = lxc_preserve_ns(pid, ns_info[i].proc_name);
		if (ns_fd[i] < 0)
			goto error;
	}

	return true;

error:
	if (errno == ENOENT)
		SYSERROR("Kernel does not support attaching to %s namespaces.", ns_info[i].proc_name);
	else
		SYSERROR("Failed to open file descriptor for %s namespace: %s.", ns_info[i].proc_name, strerror(errno));
	close_ns(ns_fd);
	return false;
}

static int attach_ns(const int ns_fd[LXC_NS_MAX]) {
	int i;

	for (i = 0; i < LXC_NS_MAX; i++) {
		if (ns_fd[i] < 0)
			continue;

		if (setns(ns_fd[i], 0) != 0)
			goto error;
	}
	return 0;

error:
	SYSERROR("Failed to attach %s namespace.", ns_info[i].proc_name);
	return -1;
}

static int match_fd(int fd)
{
	return (fd == 0 || fd == 1 || fd == 2);
}

/* Check for any fds we need to close.
 * - If fd_to_ignore != -1, then if we find that fd open we will ignore it.
 * - By default we warn about open fds we find.
 * - If closeall is true, we will close open fds.
 * - If lxc-start was passed "-C", then conf->close_all_fds will be true, in
 *   which case we also close all open fds.
 * - A daemonized container will always pass closeall=true.
 */
int lxc_check_inherited(struct lxc_conf *conf, bool closeall, int fd_to_ignore)
{
	struct dirent *direntp;
	int fd, fddir;
	DIR *dir;

	if (conf && conf->close_all_fds)
		closeall = true;

restart:
	dir = opendir("/proc/self/fd");
	if (!dir) {
		WARN("Failed to open directory: %s.", strerror(errno));
		return -1;
	}

	fddir = dirfd(dir);

	while ((direntp = readdir(dir))) {
		if (!direntp)
			break;

		if (!strcmp(direntp->d_name, "."))
			continue;

		if (!strcmp(direntp->d_name, ".."))
			continue;

		if (lxc_safe_int(direntp->d_name, &fd) < 0) {
			INFO("Could not parse file descriptor for: %s", direntp->d_name);
			continue;
		}

		if (fd == fddir || fd == lxc_log_fd || fd == fd_to_ignore)
			continue;

		if (current_config && fd == current_config->logfd)
			continue;

		if (match_fd(fd))
			continue;

		if (closeall) {
			close(fd);
			closedir(dir);
			INFO("Closed inherited fd: %d.", fd);
			goto restart;
		}
		WARN("Inherited fd: %d.", fd);
	}

	/* Only enable syslog at this point to avoid the above logging function
	 * to open a new fd and make the check_inherited function enter an
	 * infinite loop.
	 */
	lxc_log_enable_syslog();

	closedir(dir); /* cannot fail */
	return 0;
}

static int setup_signal_fd(sigset_t *oldmask)
{
	sigset_t mask;
	int fd;

	/* Block everything except serious error signals. */
	if (sigfillset(&mask) ||
	    sigdelset(&mask, SIGILL) ||
	    sigdelset(&mask, SIGSEGV) ||
	    sigdelset(&mask, SIGBUS) ||
	    sigdelset(&mask, SIGWINCH) ||
	    sigprocmask(SIG_BLOCK, &mask, oldmask)) {
		SYSERROR("Failed to set signal mask.");
		return -1;
	}

	fd = signalfd(-1, &mask, 0);
	if (fd < 0) {
		SYSERROR("Failed to create signal file descriptor.");
		return -1;
	}

	if (fcntl(fd, F_SETFD, FD_CLOEXEC)) {
		SYSERROR("Failed to set FD_CLOEXEC on the signal file descriptor: %d.", fd);
		close(fd);
		return -1;
	}

	DEBUG("Set SIGCHLD handler with file descriptor: %d.", fd);

	return fd;
}

static int signal_handler(int fd, uint32_t events, void *data,
			  struct lxc_epoll_descr *descr)
{
	struct signalfd_siginfo siginfo;
	siginfo_t info;
	int ret;
	pid_t *pid = data;
	bool init_died = false;

	ret = read(fd, &siginfo, sizeof(siginfo));
	if (ret < 0) {
		ERROR("Failed to read signal info from signal file descriptor: %d.", fd);
		return -1;
	}

	if (ret != sizeof(siginfo)) {
		ERROR("Unexpected size for siginfo struct.");
		return -1;
	}

	/* Check whether init is running. */
	info.si_pid = 0;
	ret = waitid(P_PID, *pid, &info, WEXITED | WNOWAIT | WNOHANG);
	if (ret == 0 && info.si_pid == *pid)
		init_died = true;

	if (siginfo.ssi_signo != SIGCHLD) {
		kill(*pid, siginfo.ssi_signo);
		INFO("Forwarded signal %d to pid %d.", siginfo.ssi_signo, *pid);
		return init_died ? 1 : 0;
	}

	if (siginfo.ssi_code == CLD_STOPPED) {
		INFO("Container init process was stopped.");
		return init_died ? 1 : 0;
	} else if (siginfo.ssi_code == CLD_CONTINUED) {
		INFO("Container init process was continued.");
		return init_died ? 1 : 0;
	}

	/* More robustness, protect ourself from a SIGCHLD sent
	 * by a process different from the container init.
	 */
	if (siginfo.ssi_pid != *pid) {
		NOTICE("Received SIGCHLD from pid %d instead of container init %d.", siginfo.ssi_pid, *pid);
		return init_died ? 1 : 0;
	}

	DEBUG("Container init process %d exited.", *pid);
	return 1;
}

int lxc_set_state(const char *name, struct lxc_handler *handler,
		  lxc_state_t state)
{
	ssize_t ret;
	struct lxc_list *cur, *next;
	struct state_client *client;
	struct lxc_msg msg = {.type = lxc_msg_state, .value = state};

	process_lock();
	/* Only set state under process lock held so that we don't cause
	 * lxc_cmd_state_server() to miss a state.
	 */
	handler->state = state;
	TRACE("set container state to %s", lxc_state2str(state));

	if (lxc_list_empty(&handler->state_clients)) {
		TRACE("no state clients registered");
		process_unlock();
		lxc_monitor_send_state(name, state, handler->lxcpath);
		return 0;
{'fraction_non_alphanumeric': 0.1392691415313225, 'fraction_numerical': 0.008700696055684454, 'mean_word_length': 3.793160967472894, 'pattern_counts': {'":': 0, '<': 78, '<?xml version=': 0, '>': 73, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3691782', 'n_tokens_mistral': 6988, 'n_tokens_neox': 6489, 'n_words': 1973}