| // |
| // Copyright (C) 2012 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| #ifndef UPDATE_ENGINE_UTILS_H_ |
| #define UPDATE_ENGINE_UTILS_H_ |
| |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #include <algorithm> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/posix/eintr_wrapper.h> |
| #include <base/time/time.h> |
| #include <chromeos/key_value_store.h> |
| #include <chromeos/secure_blob.h> |
| #include "metrics/metrics_library.h" |
| |
| #include "update_engine/action.h" |
| #include "update_engine/action_processor.h" |
| #include "update_engine/connection_manager_interface.h" |
| #include "update_engine/constants.h" |
| #include "update_engine/file_descriptor.h" |
| #include "update_engine/metrics.h" |
| #include "update_engine/update_metadata.pb.h" |
| |
| namespace chromeos_update_engine { |
| |
| class SystemState; |
| |
| namespace utils { |
| |
| // Converts a struct timespec representing a number of seconds since |
| // the Unix epoch to a base::Time. Sub-microsecond time is rounded |
| // down. |
| base::Time TimeFromStructTimespec(struct timespec *ts); |
| |
| // Formats |vec_str| as a string of the form ["<elem1>", "<elem2>"]. |
| // Does no escaping, only use this for presentation in error messages. |
| std::string StringVectorToString(const std::vector<std::string> &vec_str); |
| |
| // Calculates the p2p file id from payload hash and size |
| std::string CalculateP2PFileId(const std::string& payload_hash, |
| size_t payload_size); |
| |
| // Parse the firmware version from one line of output from the |
| // "mosys" command. |
| std::string ParseECVersion(std::string input_line); |
| |
| // Writes the data passed to path. The file at path will be overwritten if it |
| // exists. Returns true on success, false otherwise. |
| bool WriteFile(const char* path, const void* data, int data_len); |
| |
| // Calls write() or pwrite() repeatedly until all count bytes at buf are |
| // written to fd or an error occurs. Returns true on success. |
| bool WriteAll(int fd, const void* buf, size_t count); |
| bool PWriteAll(int fd, const void* buf, size_t count, off_t offset); |
| |
| bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count); |
| bool PWriteAll(FileDescriptorPtr fd, |
| const void* buf, |
| size_t count, |
| off_t offset); |
| |
| // Calls pread() repeatedly until count bytes are read, or EOF is reached. |
| // Returns number of bytes read in *bytes_read. Returns true on success. |
| bool PReadAll(int fd, void* buf, size_t count, off_t offset, |
| ssize_t* out_bytes_read); |
| |
| bool PReadAll(FileDescriptorPtr fd, void* buf, size_t count, off_t offset, |
| ssize_t* out_bytes_read); |
| |
| // Opens |path| for reading and appends its entire content to the container |
| // pointed to by |out_p|. Returns true upon successfully reading all of the |
| // file's content, false otherwise, in which case the state of the output |
| // container is unknown. ReadFileChunk starts reading the file from |offset|; if |
| // |size| is not -1, only up to |size| bytes are read in. |
| bool ReadFile(const std::string& path, chromeos::Blob* out_p); |
| bool ReadFile(const std::string& path, std::string* out_p); |
| bool ReadFileChunk(const std::string& path, off_t offset, off_t size, |
| chromeos::Blob* out_p); |
| |
| // Invokes |cmd| in a pipe and appends its stdout to the container pointed to by |
| // |out_p|. Returns true upon successfully reading all of the output, false |
| // otherwise, in which case the state of the output container is unknown. |
| bool ReadPipe(const std::string& cmd, std::string* out_p); |
| |
| // Returns the size of the block device at path, or the file descriptor fd. If |
| // an error occurs, -1 is returned. |
| off_t BlockDevSize(const std::string& path); |
| off_t BlockDevSize(int fd); |
| |
| // Returns the size of the file at path, or the file desciptor fd. If the file |
| // is actually a block device, this function will automatically call |
| // BlockDevSize. If the file doesn't exist or some error occurrs, -1 is |
| // returned. |
| off_t FileSize(const std::string& path); |
| off_t FileSize(int fd); |
| |
| std::string ErrnoNumberAsString(int err); |
| |
| // Returns true if the file exists for sure. Returns false if it doesn't exist, |
| // or an error occurs. |
| bool FileExists(const char* path); |
| |
| // Returns true if |path| exists and is a symbolic link. |
| bool IsSymlink(const char* path); |
| |
| // Try attaching UBI |volume_num|. If there is any error executing required |
| // commands to attach the volume, this function returns false. This function |
| // only returns true if "/dev/ubi%d_0" becomes available in |timeout| seconds. |
| bool TryAttachingUbiVolume(int volume_num, int timeout); |
| |
| // If |base_filename_template| is neither absolute (starts with "/") nor |
| // explicitly relative to the current working directory (starts with "./" or |
| // "../"), then it is prepended the system's temporary directory. On success, |
| // stores the name of the new temporary file in |filename|. If |fd| is |
| // non-null, the file descriptor returned by mkstemp is written to it and |
| // kept open; otherwise, it is closed. The template must end with "XXXXXX". |
| // Returns true on success. |
| bool MakeTempFile(const std::string& base_filename_template, |
| std::string* filename, |
| int* fd); |
| |
| // If |base_dirname_template| is neither absolute (starts with "/") nor |
| // explicitly relative to the current working directory (starts with "./" or |
| // "../"), then it is prepended the system's temporary directory. On success, |
| // stores the name of the new temporary directory in |dirname|. The template |
| // must end with "XXXXXX". Returns true on success. |
| bool MakeTempDirectory(const std::string& base_dirname_template, |
| std::string* dirname); |
| |
| // Splits the partition device name into the block device name and partition |
| // number. For example, "/dev/sda3" will be split into {"/dev/sda", 3} and |
| // "/dev/mmcblk0p2" into {"/dev/mmcblk0", 2} |
| // Returns false when malformed device name is passed in. |
| // If both output parameters are omitted (null), can be used |
| // just to test the validity of the device name. Note that the function |
| // simply checks if the device name looks like a valid device, no other |
| // checks are performed (i.e. it doesn't check if the device actually exists). |
| bool SplitPartitionName(const std::string& partition_name, |
| std::string* out_disk_name, |
| int* out_partition_num); |
| |
| // Builds a partition device name from the block device name and partition |
| // number. For example: |
| // {"/dev/sda", 1} => "/dev/sda1" |
| // {"/dev/mmcblk2", 12} => "/dev/mmcblk2p12" |
| // Returns empty string when invalid parameters are passed in |
| std::string MakePartitionName(const std::string& disk_name, |
| int partition_num); |
| |
| // Similar to "MakePartitionName" but returns a name that is suitable for |
| // mounting. On NAND system we can write to "/dev/ubiX_0", which is what |
| // MakePartitionName returns, but we cannot mount that device. To mount, we |
| // have to use "/dev/ubiblockX_0" for rootfs. Stateful and OEM partitions are |
| // mountable with "/dev/ubiX_0". The input is a partition device such as |
| // /dev/sda3. Return empty string on error. |
| std::string MakePartitionNameForMount(const std::string& part_name); |
| |
| // Synchronously mount or unmount a filesystem. Return true on success. |
| // When mounting, it will attempt to mount the the device as "ext3", "ext2" and |
| // "squashfs", with the passed |flags| options. |
| bool MountFilesystem(const std::string& device, const std::string& mountpoint, |
| unsigned long flags); // NOLINT(runtime/int) |
| bool UnmountFilesystem(const std::string& mountpoint); |
| |
| // Returns the block count and the block byte size of the file system on |
| // |device| (which may be a real device or a path to a filesystem image) or on |
| // an opened file descriptor |fd|. The actual file-system size is |block_count| |
| // * |block_size| bytes. Returns true on success, false otherwise. |
| bool GetFilesystemSize(const std::string& device, |
| int* out_block_count, |
| int* out_block_size); |
| bool GetFilesystemSizeFromFD(int fd, |
| int* out_block_count, |
| int* out_block_size); |
| |
| // Determines the block count and block size of the ext3 fs. At least 2048 bytes |
| // are required to parse the first superblock. Returns whether the buffer |
| // contains a valid ext3 filesystem and the values were parsed. |
| bool GetExt3Size(const uint8_t* buffer, size_t buffer_size, |
| int* out_block_count, |
| int* out_block_size); |
| |
| // Determines the block count and block size of the squashfs v4 fs. At least 96 |
| // bytes are required to parse the header of the filesystem. Since squashfs |
| // doesn't define a physical block size, a value of 4096 is used for the block |
| // size, which is the default padding when creating the filesystem. |
| // Returns whether the buffer contains a valid squashfs v4 header and the size |
| // was parsed. Only little endian squashfs is supported. |
| bool GetSquashfs4Size(const uint8_t* buffer, size_t buffer_size, |
| int* out_block_count, |
| int* out_block_size); |
| |
| // Returns whether the filesystem is an ext[234] filesystem. In case of failure, |
| // such as if the file |device| doesn't exists or can't be read, it returns |
| // false. |
| bool IsExtFilesystem(const std::string& device); |
| |
| // Returns whether the filesystem is a squashfs filesystem. In case of failure, |
| // such as if the file |device| doesn't exists or can't be read, it returns |
| // false. |
| bool IsSquashfsFilesystem(const std::string& device); |
| |
| // Returns a human-readable string with the file format based on magic constants |
| // on the header of the file. |
| std::string GetFileFormat(const std::string& path); |
| |
| // Returns the string representation of the given UTC time. |
| // such as "11/14/2011 14:05:30 GMT". |
| std::string ToString(const base::Time utc_time); |
| |
| // Returns true or false depending on the value of b. |
| std::string ToString(bool b); |
| |
| // Returns a string representation of the given enum. |
| std::string ToString(DownloadSource source); |
| |
| // Returns a string representation of the given enum. |
| std::string ToString(PayloadType payload_type); |
| |
| // Schedules a Main Loop callback to trigger the crash reporter to perform an |
| // upload as if this process had crashed. |
| void ScheduleCrashReporterUpload(); |
| |
| // Fuzzes an integer |value| randomly in the range: |
| // [value - range / 2, value + range - range / 2] |
| int FuzzInt(int value, unsigned int range); |
| |
| // Log a string in hex to LOG(INFO). Useful for debugging. |
| void HexDumpArray(const uint8_t* const arr, const size_t length); |
| inline void HexDumpString(const std::string& str) { |
| HexDumpArray(reinterpret_cast<const uint8_t*>(str.data()), str.size()); |
| } |
| inline void HexDumpVector(const chromeos::Blob& vect) { |
| HexDumpArray(vect.data(), vect.size()); |
| } |
| |
| template<typename KeyType, typename ValueType> |
| bool MapContainsKey(const std::map<KeyType, ValueType>& m, const KeyType& k) { |
| return m.find(k) != m.end(); |
| } |
| template<typename KeyType> |
| bool SetContainsKey(const std::set<KeyType>& s, const KeyType& k) { |
| return s.find(k) != s.end(); |
| } |
| |
| template<typename T> |
| bool VectorContainsValue(const std::vector<T>& vect, const T& value) { |
| return std::find(vect.begin(), vect.end(), value) != vect.end(); |
| } |
| |
| template<typename T> |
| bool VectorIndexOf(const std::vector<T>& vect, const T& value, |
| typename std::vector<T>::size_type* out_index) { |
| typename std::vector<T>::const_iterator it = std::find(vect.begin(), |
| vect.end(), |
| value); |
| if (it == vect.end()) { |
| return false; |
| } else { |
| *out_index = it - vect.begin(); |
| return true; |
| } |
| } |
| |
| // Cgroups cpu shares constants. 1024 is the default shares a standard process |
| // gets and 2 is the minimum value. We set High as a value that gives the |
| // update-engine 2x the cpu share of a standard process. |
| enum CpuShares { |
| kCpuSharesHigh = 2048, |
| kCpuSharesNormal = 1024, |
| kCpuSharesLow = 2, |
| }; |
| |
| // Sets the current process shares to |shares|. Returns true on |
| // success, false otherwise. |
| bool SetCpuShares(CpuShares shares); |
| |
| // Converts seconds into human readable notation including days, hours, minutes |
| // and seconds. For example, 185 will yield 3m5s, 4300 will yield 1h11m40s, and |
| // 360000 will yield 4d4h0m0s. Zero padding not applied. Seconds are always |
| // shown in the result. |
| std::string FormatSecs(unsigned secs); |
| |
| // Converts a TimeDelta into human readable notation including days, hours, |
| // minutes, seconds and fractions of a second down to microsecond granularity, |
| // as necessary; for example, an output of 5d2h0m15.053s means that the input |
| // time was precise to the milliseconds only. Zero padding not applied, except |
| // for fractions. Seconds are always shown, but fractions thereof are only shown |
| // when applicable. If |delta| is negative, the output will have a leading '-' |
| // followed by the absolute duration. |
| std::string FormatTimeDelta(base::TimeDelta delta); |
| |
| // This method transforms the given error code to be suitable for UMA and |
| // for error classification purposes by removing the higher order bits and |
| // aggregating error codes beyond the enum range, etc. This method is |
| // idempotent, i.e. if called with a value previously returned by this method, |
| // it'll return the same value again. |
| ErrorCode GetBaseErrorCode(ErrorCode code); |
| |
| // Transforms a ErrorCode value into a metrics::DownloadErrorCode. |
| // This obviously only works for errors related to downloading so if |code| |
| // is e.g. |ErrorCode::kFilesystemCopierError| then |
| // |kDownloadErrorCodeInputMalformed| is returned. |
| metrics::DownloadErrorCode GetDownloadErrorCode(ErrorCode code); |
| |
| // Transforms a ErrorCode value into a metrics::AttemptResult. |
| // |
| // If metrics::AttemptResult::kPayloadDownloadError is returned, you |
| // can use utils::GetDownloadError() to get more detail. |
| metrics::AttemptResult GetAttemptResult(ErrorCode code); |
| |
| // Calculates the internet connection type given |type| and |tethering|. |
| metrics::ConnectionType GetConnectionType(NetworkConnectionType type, |
| NetworkTethering tethering); |
| |
| // Returns a string representation of the ErrorCodes (either the base |
| // error codes or the bit flags) for logging purposes. |
| std::string CodeToString(ErrorCode code); |
| |
| // Creates the powerwash marker file with the appropriate commands in it. Uses |
| // |file_path| as the path to the marker file if non-null, otherwise uses the |
| // global default. Returns true if successfully created. False otherwise. |
| bool CreatePowerwashMarkerFile(const char* file_path); |
| |
| // Deletes the marker file used to trigger Powerwash using clobber-state. Uses |
| // |file_path| as the path to the marker file if non-null, otherwise uses the |
| // global default. Returns true if successfully deleted. False otherwise. |
| bool DeletePowerwashMarkerFile(const char* file_path); |
| |
| // Decodes the data in |base64_encoded| and stores it in a temporary |
| // file. Returns false if the given data is empty, not well-formed |
| // base64 or if an error occurred. If true is returned, the decoded |
| // data is stored in the file returned in |out_path|. The file should |
| // be deleted when no longer needed. |
| bool DecodeAndStoreBase64String(const std::string& base64_encoded, |
| base::FilePath *out_path); |
| |
| // Converts |time| to an Omaha InstallDate which is defined as "the |
| // number of PST8PDT calendar weeks since Jan 1st 2007 0:00 PST, times |
| // seven" with PST8PDT defined as "Pacific Time" (e.g. UTC-07:00 if |
| // daylight savings is observed and UTC-08:00 otherwise.) |
| // |
| // If the passed in |time| variable is before Monday January 1st 2007 |
| // 0:00 PST, False is returned and the value returned in |
| // |out_num_days| is undefined. Otherwise the number of PST8PDT |
| // calendar weeks since that date times seven is returned in |
| // |out_num_days| and the function returns True. |
| // |
| // (NOTE: This function does not currently take daylight savings time |
| // into account so the result may up to one hour off. This is because |
| // the glibc date and timezone routines depend on the TZ environment |
| // variable and changing environment variables is not thread-safe. |
| bool ConvertToOmahaInstallDate(base::Time time, int *out_num_days); |
| |
| // This function returns the duration on the wallclock since the last |
| // time it was called for the same |state_variable_key| value. |
| // |
| // If the function returns |true|, the duration (always non-negative) |
| // is returned in |out_duration|. If the function returns |false| |
| // something went wrong or there was no previous measurement. |
| bool WallclockDurationHelper(SystemState* system_state, |
| const std::string& state_variable_key, |
| base::TimeDelta* out_duration); |
| |
| // This function returns the duration on the monotonic clock since the |
| // last time it was called for the same |storage| pointer. |
| // |
| // You should pass a pointer to a 64-bit integer in |storage| which |
| // should be initialized to 0. |
| // |
| // If the function returns |true|, the duration (always non-negative) |
| // is returned in |out_duration|. If the function returns |false| |
| // something went wrong or there was no previous measurement. |
| bool MonotonicDurationHelper(SystemState* system_state, |
| int64_t* storage, |
| base::TimeDelta* out_duration); |
| |
| // Look for the minor version value in the passed |store| and set |
| // |minor_version| to that value. Return whether the value was found and valid. |
| bool GetMinorVersion(const chromeos::KeyValueStore& store, |
| uint32_t* minor_version); |
| |
| // This function reads the specified data in |extents| into |out_data|. The |
| // extents are read from the file at |path|. |out_data_size| is the size of |
| // |out_data|. Returns false if the number of bytes to read given in |
| // |extents| does not equal |out_data_size|. |
| bool ReadExtents(const std::string& path, const std::vector<Extent>& extents, |
| chromeos::Blob* out_data, ssize_t out_data_size, |
| size_t block_size); |
| |
| // Read the current boot identifier and store it in |boot_id|. This identifier |
| // is constants during the same boot of the kernel and is regenerated after |
| // reboot. Returns whether it succeeded getting the boot_id. |
| bool GetBootId(std::string* boot_id); |
| |
| } // namespace utils |
| |
| |
| // Utility class to close a file descriptor |
| class ScopedFdCloser { |
| public: |
| explicit ScopedFdCloser(int* fd) : fd_(fd) {} |
| ~ScopedFdCloser() { |
| if (should_close_ && fd_ && (*fd_ >= 0) && !IGNORE_EINTR(close(*fd_))) |
| *fd_ = -1; |
| } |
| void set_should_close(bool should_close) { should_close_ = should_close; } |
| private: |
| int* fd_; |
| bool should_close_ = true; |
| DISALLOW_COPY_AND_ASSIGN(ScopedFdCloser); |
| }; |
| |
| // Utility class to delete a file when it goes out of scope. |
| class ScopedPathUnlinker { |
| public: |
| explicit ScopedPathUnlinker(const std::string& path) |
| : path_(path), |
| should_remove_(true) {} |
| ~ScopedPathUnlinker() { |
| if (should_remove_ && unlink(path_.c_str()) < 0) { |
| PLOG(ERROR) << "Unable to unlink path " << path_; |
| } |
| } |
| void set_should_remove(bool should_remove) { should_remove_ = should_remove; } |
| |
| private: |
| const std::string path_; |
| bool should_remove_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedPathUnlinker); |
| }; |
| |
| // Utility class to delete an empty directory when it goes out of scope. |
| class ScopedDirRemover { |
| public: |
| explicit ScopedDirRemover(const std::string& path) |
| : path_(path), |
| should_remove_(true) {} |
| ~ScopedDirRemover() { |
| if (should_remove_ && (rmdir(path_.c_str()) < 0)) { |
| PLOG(ERROR) << "Unable to remove dir " << path_; |
| } |
| } |
| void set_should_remove(bool should_remove) { should_remove_ = should_remove; } |
| |
| protected: |
| const std::string path_; |
| |
| private: |
| bool should_remove_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedDirRemover); |
| }; |
| |
| // Utility class to unmount a filesystem mounted on a temporary directory and |
| // delete the temporary directory when it goes out of scope. |
| class ScopedTempUnmounter : public ScopedDirRemover { |
| public: |
| explicit ScopedTempUnmounter(const std::string& path) : |
| ScopedDirRemover(path) {} |
| ~ScopedTempUnmounter() { |
| utils::UnmountFilesystem(path_); |
| } |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ScopedTempUnmounter); |
| }; |
| |
| // A little object to call ActionComplete on the ActionProcessor when |
| // it's destructed. |
| class ScopedActionCompleter { |
| public: |
| explicit ScopedActionCompleter(ActionProcessor* processor, |
| AbstractAction* action) |
| : processor_(processor), |
| action_(action), |
| code_(ErrorCode::kError), |
| should_complete_(true) {} |
| ~ScopedActionCompleter() { |
| if (should_complete_) |
| processor_->ActionComplete(action_, code_); |
| } |
| void set_code(ErrorCode code) { code_ = code; } |
| void set_should_complete(bool should_complete) { |
| should_complete_ = should_complete; |
| } |
| ErrorCode get_code() const { return code_; } |
| |
| private: |
| ActionProcessor* processor_; |
| AbstractAction* action_; |
| ErrorCode code_; |
| bool should_complete_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedActionCompleter); |
| }; |
| |
| } // namespace chromeos_update_engine |
| |
| #define TEST_AND_RETURN_FALSE_ERRNO(_x) \ |
| do { \ |
| bool _success = static_cast<bool>(_x); \ |
| if (!_success) { \ |
| std::string _msg = \ |
| chromeos_update_engine::utils::ErrnoNumberAsString(errno); \ |
| LOG(ERROR) << #_x " failed: " << _msg; \ |
| return false; \ |
| } \ |
| } while (0) |
| |
| #define TEST_AND_RETURN_FALSE(_x) \ |
| do { \ |
| bool _success = static_cast<bool>(_x); \ |
| if (!_success) { \ |
| LOG(ERROR) << #_x " failed."; \ |
| return false; \ |
| } \ |
| } while (0) |
| |
| #define TEST_AND_RETURN_ERRNO(_x) \ |
| do { \ |
| bool _success = static_cast<bool>(_x); \ |
| if (!_success) { \ |
| std::string _msg = \ |
| chromeos_update_engine::utils::ErrnoNumberAsString(errno); \ |
| LOG(ERROR) << #_x " failed: " << _msg; \ |
| return; \ |
| } \ |
| } while (0) |
| |
| #define TEST_AND_RETURN(_x) \ |
| do { \ |
| bool _success = static_cast<bool>(_x); \ |
| if (!_success) { \ |
| LOG(ERROR) << #_x " failed."; \ |
| return; \ |
| } \ |
| } while (0) |
| |
| #define TEST_AND_RETURN_FALSE_ERRCODE(_x) \ |
| do { \ |
| errcode_t _error = (_x); \ |
| if (_error) { \ |
| errno = _error; \ |
| LOG(ERROR) << #_x " failed: " << _error; \ |
| return false; \ |
| } \ |
| } while (0) |
| |
| #endif // UPDATE_ENGINE_UTILS_H_ |