blob: 614671a2bb04b6a1b4e61d0690822a1f7e9aeed3 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "germ/container_manager.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <map>
#include <memory>
#include <string>
#include <base/logging.h>
#include <base/memory/ref_counted.h>
#include <base/stl_util.h>
#include <base/time/time.h>
#include "germ/container.h"
#include "germ/proto_bindings/soma_container_spec.pb.h"
namespace germ {
ContainerManager::ContainerManager(GermZygote* zygote) : zygote_(zygote) {}
ContainerManager::~ContainerManager() {}
bool ContainerManager::StartContainer(const soma::ContainerSpec& spec) {
const std::string& name = spec.name();
scoped_refptr<Container> container = Lookup(name);
if (container == nullptr) {
container = new Container(spec);
containers_[name] = container;
return DoStart(container);
}
container->set_spec(spec);
container->set_desired_state(Container::State::RUNNING);
switch (container->state()) {
case Container::State::STOPPED:
return DoStart(container);
case Container::State::RUNNING:
// TODO(rickyz): Make kill_delay part of the ContainerSpec.
return DoTerminate(container, base::TimeDelta());
case Container::State::DYING:
// TODO(rickyz): This return value sucks.
return true;
}
NOTREACHED();
return false;
}
bool ContainerManager::TerminateContainer(const std::string& name,
base::TimeDelta kill_delay) {
scoped_refptr<Container> container = Lookup(name);
if (container == nullptr) {
LOG(ERROR) << "Attempted to terminate nonexistent container: " << name;
return false;
}
container->set_desired_state(Container::State::STOPPED);
return DoTerminate(container, kill_delay);
}
bool ContainerManager::DoStart(scoped_refptr<Container> container) {
DCHECK_EQ(Container::State::RUNNING, container->desired_state());
DCHECK_EQ(Container::State::STOPPED, container->state());
if (!container->Launch(zygote_)) {
return false;
}
pid_map_[container->init_pid()] = container;
return true;
}
bool ContainerManager::DoTerminate(scoped_refptr<Container> container,
base::TimeDelta kill_delay) {
switch (container->state()) {
case Container::State::STOPPED:
return true;
case Container::State::RUNNING:
return container->Terminate(zygote_, kill_delay);
case Container::State::DYING:
return true;
}
NOTREACHED();
return false;
}
scoped_refptr<Container> ContainerManager::Lookup(const std::string& name) {
const auto it = containers_.find(name);
if (it == containers_.end()) {
return nullptr;
}
return it->second;
}
void ContainerManager::OnReap(const siginfo_t& info) {
const pid_t pid = info.si_pid;
const auto it = pid_map_.find(pid);
if (it == pid_map_.end()) {
LOG(ERROR) << "Received SIGCHLD from unknown process: " << pid;
return;
}
scoped_refptr<Container> container = it->second;
const std::string& name = container->name();
pid_map_.erase(it);
container->OnReap();
switch (container->desired_state()) {
case Container::State::STOPPED:
CHECK_EQ(1u, containers_.erase(name));
return;
case Container::State::RUNNING:
if (!DoStart(container)) {
LOG(ERROR) << "Failed to restart container: " << name;
// TODO(rickyz): Should we queue up any further attempts to restart the
// container?
CHECK_EQ(1u, containers_.erase(name));
}
return;
case Container::State::DYING:
DLOG(FATAL) << "Invalid desired state for container " << name << ": "
<< container->desired_state();
return;
}
NOTREACHED();
}
} // namespace germ