shill: Implement CreateProfile, PushProfile, PopProfile
Collateral fixes: Profiles now create a header at the beginning
of the file, and this feature is plumbed down through key_file_store
and glib. InitStorage can be configured to fail if the profile
already exists/doesn't yet exist.
BUG=chromium-os:22221
TEST=New unit tests
Change-Id: Ie7c2d0dee97891b7850cec75b74052fce77eee8f
Reviewed-on: https://gerrit.chromium.org/gerrit/10884
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/manager.cc b/manager.cc
index 5f08ccb..584fb92 100644
--- a/manager.cc
+++ b/manager.cc
@@ -59,6 +59,7 @@
device_info_(control_interface, dispatcher, this),
modem_info_(control_interface, dispatcher, this, glib),
running_(false),
+ connect_profiles_to_rpc_(true),
ephemeral_profile_(new EphemeralProfile(control_interface, this)),
control_interface_(control_interface),
glib_(glib) {
@@ -120,7 +121,7 @@
this,
storage_path_,
props_));
- CHECK(profiles_[0]->InitStorage(glib_));
+ CHECK(profiles_[0]->InitStorage(glib_, Profile::kCreateOrOpenExisting, NULL));
running_ = true;
adaptor_->UpdateRunning();
@@ -145,6 +146,153 @@
profiles_.push_back(profile);
}
+void Manager::CreateProfile(const std::string &name, Error *error) {
+ Profile::Identifier ident;
+ if (!Profile::ParseIdentifier(name, &ident)) {
+ const string kMessage = "Invalid profile name " + name;
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kInvalidArguments, kMessage);
+ return;
+ }
+ ProfileRefPtr profile(new Profile(control_interface_,
+ this,
+ ident,
+ user_storage_format_,
+ false));
+ if (!profile->InitStorage(glib_, Profile::kCreateNew, error)) {
+ return;
+ }
+
+ // Save profile data out, and then let the scoped pointer fall out of scope.
+ if (!profile->Save()) {
+ const string kMessage = "Profile name " + name + " could not be saved";
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kInternalError, kMessage);
+ return;
+ }
+}
+
+void Manager::PushProfile(const string &name, Error *error) {
+
+ Profile::Identifier ident;
+ if (!Profile::ParseIdentifier(name, &ident)) {
+ const string kMessage = "Invalid profile name " + name;
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kInvalidArguments, kMessage);
+ return;
+ }
+
+ for (vector<ProfileRefPtr>::const_iterator it = profiles_.begin();
+ it != profiles_.end();
+ ++it) {
+ if ((*it)->MatchesIdentifier(ident)) {
+ const string kMessage = "Profile name " + name + " is already on stack";
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kAlreadyExists, kMessage);
+ return;
+ }
+ }
+
+ if (ident.user.empty()) {
+ // The manager will have only one machine-wide profile, and this is the
+ // DefaultProfile. This means no other profiles can be loaded that do
+ // not have a user component.
+ // TODO(pstew): This is all well and good, but WiFi autotests try to
+ // creating a default profile (by a name other than "default") in order
+ // to avoid leaving permanent side effects to devices under test. This
+ // whole thing will need to be reworked in order to allow that to happen,
+ // or the autotests (or their expectations) will need to change.
+ const string kMessage = "Cannot load non-default global profile " + name;
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kInvalidArguments, kMessage);
+ return;
+ }
+
+ ProfileRefPtr profile(new Profile(control_interface_,
+ this,
+ ident,
+ user_storage_format_,
+ connect_profiles_to_rpc_));
+ if (!profile->InitStorage(glib_, Profile::kOpenExisting, error)) {
+ return;
+ }
+
+ AdoptProfile(profile);
+
+ // Offer each registered Service the opportunity to join this new Profile.
+ vector<ServiceRefPtr>::iterator it;
+ for (it = services_.begin(); it != services_.end(); ++it) {
+ profile->MergeService(*it);
+ }
+
+ // TODO(pstew): Now shop the Profile contents around to Devices which
+ // can create non-visible services.
+}
+
+void Manager::PopProfileInternal() {
+ CHECK(!profiles_.empty());
+ ProfileRefPtr active_profile = profiles_.back();
+ profiles_.pop_back();
+ vector<ServiceRefPtr>::iterator s_it;
+ for (s_it = services_.begin(); s_it != services_.end(); ++s_it) {
+ if ((*s_it)->profile().get() == active_profile.get()) {
+ vector<ProfileRefPtr>::reverse_iterator p_it;
+ for (p_it = profiles_.rbegin(); p_it != profiles_.rend(); ++p_it) {
+ if ((*p_it)->MergeService(*s_it)) {
+ break;
+ }
+ }
+ if (p_it == profiles_.rend()) {
+ ephemeral_profile_->AdoptService(*s_it);
+ }
+ }
+ }
+}
+
+void Manager::PopProfile(const std::string &name, Error *error) {
+ Profile::Identifier ident;
+ if (profiles_.empty()) {
+ const string kMessage = "Profile stack is empty";
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kNotFound, kMessage);
+ return;
+ }
+ ProfileRefPtr active_profile = profiles_.back();
+ if (!Profile::ParseIdentifier(name, &ident)) {
+ const string kMessage = "Invalid profile name " + name;
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kInvalidArguments, kMessage);
+ return;
+ }
+ if (!active_profile->MatchesIdentifier(ident)) {
+ const string kMessage = name + " is not the active profile";
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kNotSupported, kMessage);
+ return;
+ }
+ PopProfileInternal();
+}
+
+void Manager::PopAnyProfile(Error *error) {
+ Profile::Identifier ident;
+ if (profiles_.empty()) {
+ const string kMessage = "Profile stack is empty";
+ LOG(ERROR) << kMessage;
+ CHECK(error);
+ error->Populate(Error::kNotFound, kMessage);
+ return;
+ }
+ PopProfileInternal();
+}
+
const ProfileRefPtr &Manager::ActiveProfile() {
DCHECK_NE(profiles_.size(), 0);
return profiles_.back();