shill: Manager: Track loaded profiles internally

Before this change, the list of logged-in profiles was passed to
shill by the shill.conf init script.  This change allows shill to
track the list of profiles (and the hashes assigned to them)
internally, ignoring the "--push" flag.  A follow-on change will
remove the --push flag entirely.

BUG=chromium:231858
TEST=Unit tests + manual: Restart shill while logged in and make
sure "list-profiles" shows the user profile is loaded.  Reboot
while logged in and make sure "list-profiles" only shows the default
profile.  Log in and make sure /var/run/shill/loaded_profile_list
contains the user profile name.

Change-Id: Ib3d8cd6d78eeeec532cf876a38da6986587403aa
Reviewed-on: https://gerrit.chromium.org/gerrit/48582
Tested-by: Paul Stewart <pstew@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
diff --git a/profile.cc b/profile.cc
index 7d84243..f719243 100644
--- a/profile.cc
+++ b/profile.cc
@@ -11,6 +11,7 @@
 #include <base/file_path.h>
 #include <base/file_util.h>
 #include <base/stl_util.h>
+#include <base/string_split.h>
 #include <base/string_util.h>
 #include <base/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
@@ -31,6 +32,10 @@
 
 namespace shill {
 
+// static
+const char Profile::kUserProfileListPathname[] =
+    RUNDIR "/loaded_profile_list";
+
 Profile::Profile(ControlInterface *control_interface,
                  Metrics *metrics,
                  Manager *manager,
@@ -267,6 +272,62 @@
       "~%s/%s", name.user.c_str(), name.identifier.c_str());
 }
 
+// static
+vector<Profile::Identifier> Profile::LoadUserProfileList(const FilePath &path) {
+  vector<Identifier> profile_identifiers;
+  string profile_data;
+  if (!file_util::ReadFileToString(path, &profile_data)) {
+    return profile_identifiers;
+  }
+
+  vector<string> profile_lines;
+  base::SplitStringDontTrim(profile_data, '\n', &profile_lines);
+  vector<string>::const_iterator it;
+  for (it = profile_lines.begin(); it != profile_lines.end(); ++it) {
+    const string &line = *it;
+    if (line.empty()) {
+      // This will be the case on the last line, so let's not complain about it.
+      continue;
+    }
+    size_t space = line.find(' ');
+    if (space == string::npos || space == 0) {
+      LOG(ERROR) << "Invalid line found in " << path.value()
+                 << ": " << line;
+      continue;
+    }
+    string name(line.begin(), line.begin() + space);
+    Identifier identifier;
+    if (!ParseIdentifier(name, &identifier) || identifier.user.empty()) {
+      LOG(ERROR) << "Invalid profile name found in " << path.value()
+                 << ": " << name;
+      continue;
+    }
+    identifier.user_hash = string(line.begin() + space + 1, line.end());
+    profile_identifiers.push_back(identifier);
+  }
+
+  return profile_identifiers;
+}
+
+// static
+bool Profile::SaveUserProfileList(const FilePath &path,
+                                  const vector<ProfileRefPtr> &profiles) {
+  vector<string> lines;
+  vector<ProfileRefPtr>::const_iterator it;
+  for (it = profiles.begin(); it != profiles.end(); ++it) {
+    Identifier &id = (*it)->name_;
+    if (id.user.empty()) {
+      continue;
+    }
+    lines.push_back(base::StringPrintf("%s %s\n",
+                                       IdentifierToString(id).c_str(),
+                                       id.user_hash.c_str()));
+  }
+  string content = JoinString(lines, "");
+  size_t ret = file_util::WriteFile(path, content.c_str(), content.length());
+  return ret == content.length();
+}
+
 bool Profile::MatchesIdentifier(const Identifier &name) const {
   return name.user == name_.user && name.identifier == name_.identifier;
 }