1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/version_loader.h" 6 7 #include <vector> 8 9 #include "base/file_path.h" 10 #include "base/file_util.h" 11 #include "base/message_loop.h" 12 #include "base/string_split.h" 13 #include "base/string_util.h" 14 #include "base/threading/thread.h" 15 #include "base/time.h" 16 #include "chrome/browser/browser_process.h" 17 #include "content/browser/browser_thread.h" 18 19 namespace chromeos { 20 21 // File to look for version number in. 22 static const char kPathVersion[] = "/etc/lsb-release"; 23 24 // TODO(rkc): Remove once we change over the Chrome OS version format. 25 // Done for http://code.google.com/p/chromium-os/issues/detail?id=15789 26 static const size_t kTrimVersion = 2; 27 28 // File to look for firmware number in. 29 static const char kPathFirmware[] = "/var/log/bios_info.txt"; 30 31 VersionLoader::VersionLoader() : backend_(new Backend()) { 32 } 33 34 // Beginning of line we look for that gives full version number. 35 // Format: x.x.xx.x (Developer|Official build extra info) board info 36 // static 37 const char VersionLoader::kFullVersionPrefix[] = 38 "CHROMEOS_RELEASE_DESCRIPTION="; 39 40 // Same but for short version (x.x.xx.x). 41 // static 42 const char VersionLoader::kVersionPrefix[] = "CHROMEOS_RELEASE_VERSION="; 43 44 // Beginning of line we look for that gives the firmware version. 45 const char VersionLoader::kFirmwarePrefix[] = "version"; 46 47 VersionLoader::Handle VersionLoader::GetVersion( 48 CancelableRequestConsumerBase* consumer, 49 VersionLoader::GetVersionCallback* callback, 50 VersionFormat format) { 51 if (!g_browser_process->file_thread()) { 52 // This should only happen if Chrome is shutting down, so we don't do 53 // anything. 54 return 0; 55 } 56 57 scoped_refptr<GetVersionRequest> request(new GetVersionRequest(callback)); 58 AddRequest(request, consumer); 59 60 g_browser_process->file_thread()->message_loop()->PostTask( 61 FROM_HERE, 62 NewRunnableMethod(backend_.get(), &Backend::GetVersion, request, format)); 63 return request->handle(); 64 } 65 66 VersionLoader::Handle VersionLoader::GetFirmware( 67 CancelableRequestConsumerBase* consumer, 68 VersionLoader::GetFirmwareCallback* callback) { 69 if (!g_browser_process->file_thread()) { 70 // This should only happen if Chrome is shutting down, so we don't do 71 // anything. 72 return 0; 73 } 74 75 scoped_refptr<GetFirmwareRequest> request(new GetFirmwareRequest(callback)); 76 AddRequest(request, consumer); 77 78 g_browser_process->file_thread()->message_loop()->PostTask( 79 FROM_HERE, 80 NewRunnableMethod(backend_.get(), &Backend::GetFirmware, request)); 81 return request->handle(); 82 } 83 84 void VersionLoader::EnablePlatformVersions(bool enable) { 85 backend_.get()->set_parse_as_platform(enable); 86 } 87 88 // static 89 std::string VersionLoader::ParseVersion(const std::string& contents, 90 const std::string& prefix) { 91 // The file contains lines such as: 92 // XXX=YYY 93 // AAA=ZZZ 94 // Split the lines and look for the one that starts with prefix. The version 95 // file is small, which is why we don't try and be tricky. 96 std::vector<std::string> lines; 97 base::SplitString(contents, '\n', &lines); 98 for (size_t i = 0; i < lines.size(); ++i) { 99 if (StartsWithASCII(lines[i], prefix, false)) { 100 std::string version = lines[i].substr(std::string(prefix).size()); 101 if (version.size() > 1 && version[0] == '"' && 102 version[version.size() - 1] == '"') { 103 // Trim trailing and leading quotes. 104 version = version.substr(1, version.size() - 2); 105 } 106 return version; 107 } 108 } 109 return std::string(); 110 } 111 112 // static 113 std::string VersionLoader::ParseFirmware(const std::string& contents) { 114 // The file contains lines such as: 115 // vendor | ... 116 // version | ... 117 // release_date | ... 118 // We don't make any assumption that the spaces between "version" and "|" is 119 // fixed. So we just match kFirmwarePrefix at the start of the line and find 120 // the first character that is not "|" or space 121 122 std::vector<std::string> lines; 123 base::SplitString(contents, '\n', &lines); 124 for (size_t i = 0; i < lines.size(); ++i) { 125 if (StartsWithASCII(lines[i], kFirmwarePrefix, false)) { 126 std::string str = lines[i].substr(std::string(kFirmwarePrefix).size()); 127 size_t found = str.find_first_not_of("| "); 128 if (found != std::string::npos) 129 return str.substr(found); 130 } 131 } 132 return std::string(); 133 } 134 135 void VersionLoader::Backend::GetVersion( 136 scoped_refptr<GetVersionRequest> request, 137 VersionFormat format) { 138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 139 if (request->canceled()) 140 return; 141 142 std::string version; 143 std::string contents; 144 const FilePath file_path(kPathVersion); 145 if (file_util::ReadFileToString(file_path, &contents)) { 146 version = ParseVersion( 147 contents, 148 (format == VERSION_FULL) ? kFullVersionPrefix : kVersionPrefix); 149 150 // TODO(rkc): Fix this once we move to xx.yyy version numbers for Chrome OS 151 // instead of 0.xx.yyy 152 // Done for http://code.google.com/p/chromium-os/issues/detail?id=15789 153 if (parse_as_platform_) { 154 if (version.size() > kTrimVersion) { 155 version = version.substr(kTrimVersion); 156 // Strip the major version. 157 size_t first_dot = version.find("."); 158 if (first_dot != std::string::npos) { 159 version = version.substr(first_dot + 1); 160 } 161 } 162 } 163 } 164 165 if (format == VERSION_SHORT_WITH_DATE) { 166 base::PlatformFileInfo fileinfo; 167 if (file_util::GetFileInfo(file_path, &fileinfo)) { 168 base::Time::Exploded ctime; 169 fileinfo.creation_time.UTCExplode(&ctime); 170 version += StringPrintf("-%02u.%02u.%02u", 171 ctime.year % 100, 172 ctime.month, 173 ctime.day_of_month); 174 } 175 } 176 177 request->ForwardResult(GetVersionCallback::TupleType(request->handle(), 178 version)); 179 } 180 181 void VersionLoader::Backend::GetFirmware( 182 scoped_refptr<GetFirmwareRequest> request) { 183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 184 if (request->canceled()) 185 return; 186 187 std::string firmware; 188 std::string contents; 189 const FilePath file_path(kPathFirmware); 190 if (file_util::ReadFileToString(file_path, &contents)) { 191 firmware = ParseFirmware(contents); 192 } 193 194 request->ForwardResult(GetFirmwareCallback::TupleType(request->handle(), 195 firmware)); 196 } 197 198 } // namespace chromeos 199