if (handler->conf->maincmd_fd >= 0)
lxc_abstract_unix_close(handler->conf->maincmd_fd);
+ if (handler->monitor_status_fd >= 0)
+ close(handler->monitor_status_fd);
+
if (handler->state_socket_pair[0] >= 0)
close(handler->state_socket_pair[0]);
handler->data_sock[0] = handler->data_sock[1] = -1;
handler->conf = conf;
handler->lxcpath = lxcpath;
+ handler->monitor_status_fd = -EBADF;
handler->pinfd = -1;
handler->pidfd = -EBADF;
handler->proc_pidfd = -EBADF;
int lxc_init(const char *name, struct lxc_handler *handler)
{
+ __do_close_prot_errno int status_fd = -EBADF;
int ret;
const char *loglevel;
struct lxc_conf *conf = handler->conf;
handler->monitor_pid = lxc_raw_getpid();
+ status_fd = open("/proc/self/status", O_RDONLY | O_CLOEXEC);
+ if (status_fd < 0) {
+ SYSERROR("Failed to open monitor status fd");
+ goto out_close_maincmd_fd;
+ }
lsm_init();
TRACE("Initialized LSM");
TRACE("Initialized LSM");
INFO("Container \"%s\" is initialized", name);
+ handler->monitor_status_fd = move_fd(status_fd);
return 0;
out_destroy_cgroups:
struct lxc_handler *handler = data;
ATTR_UNUSED __do_close_prot_errno int data_sock0 = handler->data_sock[0],
data_sock1 = handler->data_sock[1];
+ __do_close_prot_errno int status_fd = -EBADF;
int ret;
uid_t new_uid;
gid_t new_gid;
lxc_sync_fini_parent(handler);
+ if (lxc_abstract_unix_recv_fds(handler->data_sock[1], &status_fd, 1, NULL, 0) < 0) {
+ ERROR("Failed to receive status file descriptor to child process");
+ goto out_warn_father;
+ }
+
/* This prctl must be before the synchro, so if the parent dies before
* we set the parent death signal, we will detect its death with the
* synchro right after, otherwise we have a window where the parent can
* exit before we set the pdeath signal leading to a unsupervized
* container.
*/
- ret = lxc_set_death_signal(SIGKILL, handler->monitor_pid);
+ ret = lxc_set_death_signal(SIGKILL, handler->monitor_pid, status_fd);
if (ret < 0) {
SYSERROR("Failed to set PR_SET_PDEATHSIG to SIGKILL");
goto out_warn_father;
goto out_warn_father;
/* set{g,u}id() clears deathsignal */
- ret = lxc_set_death_signal(SIGKILL, handler->monitor_pid);
+ ret = lxc_set_death_signal(SIGKILL, handler->monitor_pid, status_fd);
if (ret < 0) {
SYSERROR("Failed to set PR_SET_PDEATHSIG to SIGKILL");
goto out_warn_father;
}
if (handler->conf->monitor_signal_pdeath != SIGKILL) {
- ret = lxc_set_death_signal(handler->conf->monitor_signal_pdeath, handler->monitor_pid);
+ ret = lxc_set_death_signal(handler->conf->monitor_signal_pdeath,
+ handler->monitor_pid, status_fd);
if (ret < 0) {
SYSERROR("Failed to set PR_SET_PDEATHSIG to %d",
handler->conf->monitor_signal_pdeath);
lxc_sync_fini_child(handler);
+ if (lxc_abstract_unix_send_fds(handler->data_sock[0], &handler->monitor_status_fd, 1, NULL, 0) < 0) {
+ ERROR("Failed to send status file descriptor to child process");
+ goto out_delete_net;
+ }
+ close_prot_errno_disarm(handler->monitor_status_fd);
+
/* Map the container uids. The container became an invalid userid the
* moment it was cloned with CLONE_NEWUSER. This call doesn't change
* anything immediately, but allows the container to setuid(0) (0 being
return n;
}
-int lxc_set_death_signal(int signal, pid_t parent)
+static int process_dead(/* takes */ int status_fd)
+{
+ __do_close_prot_errno int dupfd = -EBADF;
+ __do_free char *line = NULL;
+ __do_fclose FILE *f = NULL;
+ int ret = 0;
+ size_t n = 0;
+
+ dupfd = dup(status_fd);
+ if (dupfd < 0)
+ return -1;
+
+ if (fd_cloexec(dupfd, true) < 0)
+ return -1;
+
+ /* transfer ownership of fd */
+ f = fdopen(move_fd(dupfd), "re");
+ if (!f)
+ return -1;
+
+ ret = 0;
+ while (getline(&line, &n, f) != -1) {
+ char *state;
+
+ if (strncmp(line, "State:", 6))
+ continue;
+
+ state = lxc_trim_whitespace_in_place(line + 6);
+ /* only check whether process is dead or zombie for now */
+ if (*state == 'X' || *state == 'Z')
+ ret = 1;
+ }
+
+ return ret;
+}
+
+int lxc_set_death_signal(int signal, pid_t parent, int parent_status_fd)
{
int ret;
pid_t ppid;
ret = prctl(PR_SET_PDEATHSIG, prctl_arg(signal), prctl_arg(0),
prctl_arg(0), prctl_arg(0));
- /* If not in a PID namespace, check whether we have been orphaned. */
+ /* verify that we haven't been orphaned in the meantime */
ppid = (pid_t)syscall(SYS_getppid);
- if (ppid && ppid != parent) {
- ret = raise(SIGKILL);
- if (ret < 0)
- return -1;
+ if (ppid == 0) { /* parent outside our pidns */
+ if (parent_status_fd < 0)
+ return 0;
+
+ if (process_dead(parent_status_fd) == 1)
+ return raise(SIGKILL);
+ } else if (ppid != parent) {
+ return raise(SIGKILL);
}
if (ret < 0)