blob: 246bfaa74e94ed8abe414fc50ab701b5668913ad [file] [log] [blame]
Darin Petkov296889c2010-07-23 16:20:54 -07001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/utils.h"
Darin Petkovf74eb652010-08-04 12:08:38 -07006
adlr@google.com3defe6a2009-12-04 20:57:17 +00007#include <sys/mount.h>
Darin Petkovc6c135c2010-08-11 13:36:18 -07008#include <sys/resource.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +00009#include <sys/stat.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
Andrew de los Reyes970bb282009-12-09 16:34:04 -080013#include <fcntl.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
Darin Petkovf74eb652010-08-04 12:08:38 -070018
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include <algorithm>
Darin Petkovf74eb652010-08-04 12:08:38 -070020
21#include "base/file_path.h"
22#include "base/file_util.h"
Darin Petkov5c0a8af2010-08-24 13:39:13 -070023#include "base/rand_util.h"
Darin Petkovf74eb652010-08-04 12:08:38 -070024#include "base/string_util.h"
Chris Masone790e62e2010-08-12 10:41:18 -070025#include "base/logging.h"
Andrew de los Reyes970bb282009-12-09 16:34:04 -080026#include "update_engine/file_writer.h"
Darin Petkov33d30642010-08-04 10:18:57 -070027#include "update_engine/omaha_request_params.h"
Darin Petkov296889c2010-07-23 16:20:54 -070028#include "update_engine/subprocess.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000029
30using std::min;
31using std::string;
32using std::vector;
33
34namespace chromeos_update_engine {
35
36namespace utils {
37
Darin Petkov33d30642010-08-04 10:18:57 -070038bool IsOfficialBuild() {
39 OmahaRequestDeviceParams params;
40 if (!params.Init("", "")) {
41 return true;
42 }
43 return params.app_track != "buildbot-build" &&
44 params.app_track != "developer-build";
45}
46
Andrew de los Reyes970bb282009-12-09 16:34:04 -080047bool WriteFile(const char* path, const char* data, int data_len) {
48 DirectFileWriter writer;
49 TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
50 O_WRONLY | O_CREAT | O_TRUNC,
51 0666));
52 ScopedFileWriterCloser closer(&writer);
53 TEST_AND_RETURN_FALSE_ERRNO(data_len == writer.Write(data, data_len));
54 return true;
55}
56
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070057bool WriteAll(int fd, const void* buf, size_t count) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070058 const char* c_buf = static_cast<const char*>(buf);
59 ssize_t bytes_written = 0;
60 while (bytes_written < static_cast<ssize_t>(count)) {
61 ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
62 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
63 bytes_written += rc;
64 }
65 return true;
66}
67
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070068bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
69 const char* c_buf = static_cast<const char*>(buf);
70 ssize_t bytes_written = 0;
71 while (bytes_written < static_cast<ssize_t>(count)) {
72 ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
73 offset + bytes_written);
74 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
75 bytes_written += rc;
76 }
77 return true;
78}
79
80bool PReadAll(int fd, void* buf, size_t count, off_t offset,
81 ssize_t* out_bytes_read) {
82 char* c_buf = static_cast<char*>(buf);
83 ssize_t bytes_read = 0;
84 while (bytes_read < static_cast<ssize_t>(count)) {
85 ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
86 offset + bytes_read);
87 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
88 if (rc == 0) {
89 break;
90 }
91 bytes_read += rc;
92 }
93 *out_bytes_read = bytes_read;
94 return true;
Darin Petkov296889c2010-07-23 16:20:54 -070095
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070096}
97
adlr@google.com3defe6a2009-12-04 20:57:17 +000098bool ReadFile(const std::string& path, std::vector<char>* out) {
99 CHECK(out);
100 FILE* fp = fopen(path.c_str(), "r");
101 if (!fp)
102 return false;
103 const size_t kChunkSize = 1024;
104 size_t read_size;
105 do {
106 char buf[kChunkSize];
107 read_size = fread(buf, 1, kChunkSize, fp);
108 if (read_size == 0)
109 break;
110 out->insert(out->end(), buf, buf + read_size);
111 } while (read_size == kChunkSize);
112 bool success = !ferror(fp);
113 TEST_AND_RETURN_FALSE_ERRNO(fclose(fp) == 0);
114 return success;
115}
116
117bool ReadFileToString(const std::string& path, std::string* out) {
118 vector<char> data;
119 bool success = ReadFile(path, &data);
120 if (!success) {
121 return false;
122 }
123 (*out) = string(&data[0], data.size());
124 return true;
125}
126
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700127off_t FileSize(const string& path) {
128 struct stat stbuf;
129 int rc = stat(path.c_str(), &stbuf);
130 CHECK_EQ(rc, 0);
131 if (rc < 0)
132 return rc;
133 return stbuf.st_size;
134}
135
adlr@google.com3defe6a2009-12-04 20:57:17 +0000136void HexDumpArray(const unsigned char* const arr, const size_t length) {
137 const unsigned char* const char_arr =
138 reinterpret_cast<const unsigned char* const>(arr);
139 LOG(INFO) << "Logging array of length: " << length;
140 const unsigned int bytes_per_line = 16;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700141 for (uint32_t i = 0; i < length; i += bytes_per_line) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000142 const unsigned int bytes_remaining = length - i;
143 const unsigned int bytes_per_this_line = min(bytes_per_line,
144 bytes_remaining);
145 char header[100];
146 int r = snprintf(header, sizeof(header), "0x%08x : ", i);
147 TEST_AND_RETURN(r == 13);
148 string line = header;
149 for (unsigned int j = 0; j < bytes_per_this_line; j++) {
150 char buf[20];
151 unsigned char c = char_arr[i + j];
152 r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
153 TEST_AND_RETURN(r == 3);
154 line += buf;
155 }
156 LOG(INFO) << line;
157 }
158}
159
160namespace {
161class ScopedDirCloser {
162 public:
163 explicit ScopedDirCloser(DIR** dir) : dir_(dir) {}
164 ~ScopedDirCloser() {
165 if (dir_ && *dir_) {
166 int r = closedir(*dir_);
167 TEST_AND_RETURN_ERRNO(r == 0);
168 *dir_ = NULL;
169 dir_ = NULL;
170 }
171 }
172 private:
173 DIR** dir_;
174};
175} // namespace {}
176
177bool RecursiveUnlinkDir(const std::string& path) {
178 struct stat stbuf;
179 int r = lstat(path.c_str(), &stbuf);
180 TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT));
181 if ((r < 0) && (errno == ENOENT))
182 // path request is missing. that's fine.
183 return true;
184 if (!S_ISDIR(stbuf.st_mode)) {
185 TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) ||
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700186 (errno == ENOENT));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000187 // success or path disappeared before we could unlink.
188 return true;
189 }
190 {
191 // We have a dir, unlink all children, then delete dir
192 DIR *dir = opendir(path.c_str());
193 TEST_AND_RETURN_FALSE_ERRNO(dir);
194 ScopedDirCloser dir_closer(&dir);
195 struct dirent dir_entry;
196 struct dirent *dir_entry_p;
197 int err = 0;
198 while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) {
199 if (dir_entry_p == NULL) {
200 // end of stream reached
201 break;
202 }
203 // Skip . and ..
204 if (!strcmp(dir_entry_p->d_name, ".") ||
205 !strcmp(dir_entry_p->d_name, ".."))
206 continue;
207 TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" +
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700208 dir_entry_p->d_name));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000209 }
210 TEST_AND_RETURN_FALSE(err == 0);
211 }
212 // unlink dir
213 TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT));
214 return true;
215}
216
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700217string RootDevice(const string& partition_device) {
Darin Petkovf74eb652010-08-04 12:08:38 -0700218 FilePath device_path(partition_device);
219 if (device_path.DirName().value() != "/dev") {
220 return "";
221 }
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700222 string::const_iterator it = --partition_device.end();
223 for (; it >= partition_device.begin(); --it) {
224 if (!isdigit(*it))
225 break;
226 }
227 // Some devices contain a p before the partitions. For example:
228 // /dev/mmc0p4 should be shortened to /dev/mmc0.
229 if (*it == 'p')
230 --it;
231 return string(partition_device.begin(), it + 1);
232}
233
234string PartitionNumber(const string& partition_device) {
235 CHECK(!partition_device.empty());
236 string::const_iterator it = --partition_device.end();
237 for (; it >= partition_device.begin(); --it) {
238 if (!isdigit(*it))
239 break;
240 }
241 return string(it + 1, partition_device.end());
242}
243
Darin Petkovf74eb652010-08-04 12:08:38 -0700244string SysfsBlockDevice(const string& device) {
245 FilePath device_path(device);
246 if (device_path.DirName().value() != "/dev") {
247 return "";
248 }
249 return FilePath("/sys/block").Append(device_path.BaseName()).value();
250}
251
252bool IsRemovableDevice(const std::string& device) {
253 string sysfs_block = SysfsBlockDevice(device);
254 string removable;
255 if (sysfs_block.empty() ||
256 !file_util::ReadFileToString(FilePath(sysfs_block).Append("removable"),
257 &removable)) {
258 return false;
259 }
260 TrimWhitespaceASCII(removable, TRIM_ALL, &removable);
261 return removable == "1";
262}
263
adlr@google.com3defe6a2009-12-04 20:57:17 +0000264std::string ErrnoNumberAsString(int err) {
265 char buf[100];
266 buf[0] = '\0';
267 return strerror_r(err, buf, sizeof(buf));
268}
269
270std::string NormalizePath(const std::string& path, bool strip_trailing_slash) {
271 string ret;
272 bool last_insert_was_slash = false;
273 for (string::const_iterator it = path.begin(); it != path.end(); ++it) {
274 if (*it == '/') {
275 if (last_insert_was_slash)
276 continue;
277 last_insert_was_slash = true;
278 } else {
279 last_insert_was_slash = false;
280 }
281 ret.push_back(*it);
282 }
283 if (strip_trailing_slash && last_insert_was_slash) {
284 string::size_type last_non_slash = ret.find_last_not_of('/');
285 if (last_non_slash != string::npos) {
286 ret.resize(last_non_slash + 1);
287 } else {
288 ret = "";
289 }
290 }
291 return ret;
292}
293
294bool FileExists(const char* path) {
295 struct stat stbuf;
296 return 0 == lstat(path, &stbuf);
297}
298
299std::string TempFilename(string path) {
300 static const string suffix("XXXXXX");
301 CHECK(StringHasSuffix(path, suffix));
302 do {
303 string new_suffix;
304 for (unsigned int i = 0; i < suffix.size(); i++) {
305 int r = rand() % (26 * 2 + 10); // [a-zA-Z0-9]
306 if (r < 26)
307 new_suffix.append(1, 'a' + r);
308 else if (r < (26 * 2))
309 new_suffix.append(1, 'A' + r - 26);
310 else
311 new_suffix.append(1, '0' + r - (26 * 2));
312 }
313 CHECK_EQ(new_suffix.size(), suffix.size());
314 path.resize(path.size() - new_suffix.size());
315 path.append(new_suffix);
316 } while (FileExists(path.c_str()));
317 return path;
318}
319
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700320bool MakeTempFile(const std::string& filename_template,
321 std::string* filename,
322 int* fd) {
323 DCHECK(filename || fd);
324 vector<char> buf(filename_template.size() + 1);
325 memcpy(&buf[0], filename_template.data(), filename_template.size());
326 buf[filename_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700327
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700328 int mkstemp_fd = mkstemp(&buf[0]);
329 TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
330 if (filename) {
331 *filename = &buf[0];
332 }
333 if (fd) {
334 *fd = mkstemp_fd;
335 } else {
336 close(mkstemp_fd);
337 }
338 return true;
339}
340
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700341bool MakeTempDirectory(const std::string& dirname_template,
342 std::string* dirname) {
343 DCHECK(dirname);
344 vector<char> buf(dirname_template.size() + 1);
345 memcpy(&buf[0], dirname_template.data(), dirname_template.size());
346 buf[dirname_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700347
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700348 char* return_code = mkdtemp(&buf[0]);
349 TEST_AND_RETURN_FALSE_ERRNO(return_code != NULL);
350 *dirname = &buf[0];
351 return true;
352}
353
adlr@google.com3defe6a2009-12-04 20:57:17 +0000354bool StringHasSuffix(const std::string& str, const std::string& suffix) {
355 if (suffix.size() > str.size())
356 return false;
357 return 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
358}
359
360bool StringHasPrefix(const std::string& str, const std::string& prefix) {
361 if (prefix.size() > str.size())
362 return false;
363 return 0 == str.compare(0, prefix.size(), prefix);
364}
365
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700366const string BootDevice() {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000367 string proc_cmdline;
368 if (!ReadFileToString("/proc/cmdline", &proc_cmdline))
369 return "";
370 // look for "root=" in the command line
371 string::size_type pos = 0;
372 if (!StringHasPrefix(proc_cmdline, "root=")) {
373 pos = proc_cmdline.find(" root=") + 1;
374 }
375 if (pos == string::npos) {
376 // can't find root=
377 return "";
378 }
379 // at this point, pos is the point in the string where "root=" starts
380 string ret;
381 pos += strlen("root="); // advance to the device name itself
382 while (pos < proc_cmdline.size()) {
383 char c = proc_cmdline[pos];
384 if (c == ' ')
385 break;
386 ret += c;
387 pos++;
388 }
389 return ret;
390 // TODO(adlr): use findfs to figure out UUID= or LABEL= filesystems
391}
392
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700393const string BootKernelDevice(const std::string& boot_device) {
394 // Currntly this assumes the last digit of the boot device is
395 // 3, 5, or 7, and changes it to 2, 4, or 6, respectively, to
396 // get the kernel device.
397 string ret = boot_device;
398 if (ret.empty())
399 return ret;
400 char last_char = ret[ret.size() - 1];
401 if (last_char == '3' || last_char == '5' || last_char == '7') {
402 ret[ret.size() - 1] = last_char - 1;
403 return ret;
404 }
405 return "";
406}
407
adlr@google.com3defe6a2009-12-04 20:57:17 +0000408bool MountFilesystem(const string& device,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700409 const string& mountpoint,
410 unsigned long mountflags) {
411 int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", mountflags, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000412 if (rc < 0) {
413 string msg = ErrnoNumberAsString(errno);
414 LOG(ERROR) << "Unable to mount destination device: " << msg << ". "
415 << device << " on " << mountpoint;
416 return false;
417 }
418 return true;
419}
420
421bool UnmountFilesystem(const string& mountpoint) {
422 TEST_AND_RETURN_FALSE_ERRNO(umount(mountpoint.c_str()) == 0);
423 return true;
424}
425
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700426bool GetBootloader(BootLoader* out_bootloader) {
427 // For now, hardcode to syslinux.
428 *out_bootloader = BootLoader_SYSLINUX;
429 return true;
430}
431
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700432const char* GetGErrorMessage(const GError* error) {
433 if (!error)
434 return "Unknown error.";
435 return error->message;
436}
437
Darin Petkov296889c2010-07-23 16:20:54 -0700438bool Reboot() {
439 vector<string> command;
440 command.push_back("/sbin/shutdown");
441 command.push_back("-r");
442 command.push_back("now");
443 int rc = 0;
444 Subprocess::SynchronousExec(command, &rc);
445 TEST_AND_RETURN_FALSE(rc == 0);
446 return true;
447}
448
Darin Petkovc6c135c2010-08-11 13:36:18 -0700449bool SetProcessPriority(ProcessPriority priority) {
450 int prio = static_cast<int>(priority);
451 LOG(INFO) << "Setting process priority to " << prio;
452 TEST_AND_RETURN_FALSE(setpriority(PRIO_PROCESS, 0, prio) == 0);
453 return true;
454}
455
456int ComparePriorities(ProcessPriority priority_lhs,
457 ProcessPriority priority_rhs) {
458 return static_cast<int>(priority_rhs) - static_cast<int>(priority_lhs);
459}
460
Darin Petkov5c0a8af2010-08-24 13:39:13 -0700461int FuzzInt(int value, unsigned int range) {
462 int min = value - range / 2;
463 int max = value + range - range / 2;
464 return base::RandInt(min, max);
465}
466
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800467const char* const kStatefulPartition = "/mnt/stateful_partition";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000468
469} // namespace utils
470
471} // namespace chromeos_update_engine