Index: client/settings.cc |
diff --git a/client/settings.cc b/client/settings.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ecc83a58d65b995e1fc5c86ce6b8331414dab8cd |
--- /dev/null |
+++ b/client/settings.cc |
@@ -0,0 +1,224 @@ |
+// Copyright 2015 The Crashpad Authors. All rights reserved. |
+// |
+// 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. |
+ |
+#include "client/settings.h" |
+ |
+#include <fcntl.h> |
+#include <unistd.h> |
+#include <uuid/uuid.h> |
+ |
+#include "base/logging.h" |
+#include "base/posix/eintr_wrapper.h" |
+ |
+namespace crashpad { |
+ |
+namespace { |
+ |
+const uint16_t kSettingsVersion = 1; |
Mark Mentovai
2015/03/09 19:12:36
Why not make this a static const inside Settings::
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ |
+} // namespace |
+ |
+struct Settings::Data { |
+ Data() : version(kSettingsVersion), |
+ client_id(), |
+ uploads_enabled(false), |
+ last_upload_attempt_time(0) {} |
+ |
+ uint16_t version; |
Mark Mentovai
2015/03/09 19:12:36
Revise the struct order for better packing, to ens
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ UUID client_id; |
+ bool uploads_enabled; |
+ time_t last_upload_attempt_time; |
Mark Mentovai
2015/03/09 19:12:35
time_t has a variable size in 32-bit and 64-bit en
Robert Sesek
2015/03/09 21:16:27
Done.
|
+}; |
+ |
+Settings::Settings(const base::FilePath& file_path) |
+ : file_path_(file_path) { |
+} |
+ |
+Settings::~Settings() { |
+} |
+ |
+bool Settings::Initialize() { |
Mark Mentovai
2015/03/09 19:12:35
InitializationStateDcheck here and in public entry
Robert Sesek
2015/03/09 21:16:27
I think that clutters up Initialize more than it h
Mark Mentovai
2015/03/09 22:00:57
Robert Sesek wrote:
Robert Sesek
2015/03/09 22:11:00
Sure, but it means not being able to return the re
Mark Mentovai
2015/03/09 22:26:29
Robert Sesek wrote:
|
+ ScopedFileHandle handle(HANDLE_EINTR( |
+ open(file_path(), |
+ O_CREAT | O_EXCL | O_WRONLY | O_EXLOCK, |
+ 0644))); |
+ |
+ // The file was created, so this is a new database that needs to be |
+ // initialized with a client ID. |
+ if (handle.is_valid()) { |
+ return InitializeSettings(handle.get()); |
+ } |
+ |
+ // The file wasn't created, try opening it for a write operation. |
Mark Mentovai
2015/03/09 19:12:36
Since you only do a read in most cases, say in the
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ handle.reset(OpenForWriting().release()); |
+ if (!handle.is_valid()) { |
+ return false; |
+ } |
+ |
+ // Ensure that the file is valid. If it fails to read, re-initialize the |
+ // settings. |
+ Data settings; |
+ if (!ReadSettings(handle.get(), &settings)) { |
+ return RecoverSettings(handle.get(), &settings); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool Settings::GetClientID(UUID* client_id) { |
+ Data settings; |
+ if (!OpenAndReadSettings(&settings)) { |
+ return false; |
+ } |
+ |
+ *client_id = settings.client_id; |
+ return true; |
+} |
+ |
+bool Settings::GetUploadsEnabled(bool* enabled) { |
+ Data settings; |
+ if (!OpenAndReadSettings(&settings)) { |
+ return false; |
+ } |
+ |
+ *enabled = settings.uploads_enabled; |
+ return true; |
+} |
+ |
+bool Settings::SetUploadsEnabled(bool enabled) { |
+ ScopedFileHandle handle = OpenForWriting(); |
+ if (!handle.is_valid()) |
+ return false; |
+ |
+ Data settings; |
+ if (!ReadSettings(handle.get(), &settings)) { |
+ if (!RecoverSettings(handle.get(), &settings)) |
+ return false; |
+ } |
+ |
+ settings.uploads_enabled = enabled; |
+ |
+ return WriteSettings(handle.get(), &settings); |
+} |
+ |
+bool Settings::GetLastUploadAttemptTime(time_t* time) { |
+ Data settings; |
+ if (!OpenAndReadSettings(&settings)) |
+ return false; |
+ |
+ *time = settings.last_upload_attempt_time; |
+ return true; |
+} |
+ |
+bool Settings::SetLastUploadAttemptTime(time_t time) { |
+ ScopedFileHandle handle = OpenForWriting(); |
Mark Mentovai
2015/03/09 19:12:35
The bulk of this function is identical to SetUploa
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ if (!handle.is_valid()) |
+ return false; |
+ |
+ Data settings; |
+ if (!ReadSettings(handle.get(), &settings)) { |
+ if (!RecoverSettings(handle.get(), &settings)) |
+ return false; |
+ } |
+ |
+ settings.last_upload_attempt_time = time; |
+ |
+ return WriteSettings(handle.get(), &settings); |
+} |
+ |
+ScopedFileHandle Settings::OpenForReading() { |
+ ScopedFileHandle handle(HANDLE_EINTR(open(file_path(), O_RDONLY | O_SHLOCK))); |
+ PLOG_IF(ERROR, !handle.is_valid()) << "open for reading"; |
+ return handle.Pass(); |
+} |
+ |
+ScopedFileHandle Settings::OpenForWriting() { |
+ ScopedFileHandle handle(HANDLE_EINTR(open(file_path(), O_RDWR | O_EXLOCK))); |
Mark Mentovai
2015/03/09 19:12:36
Add O_CREAT so that we can recover when the file g
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ PLOG_IF(ERROR, !handle.is_valid()) << "open for writing"; |
+ return handle.Pass(); |
+} |
+ |
+bool Settings::OpenAndReadSettings(Data* out_data) { |
+ ScopedFileHandle handle = OpenForReading(); |
+ if (!handle.is_valid()) |
+ return false; |
+ |
+ if (ReadSettings(handle.get(), out_data)) |
+ return true; |
+ |
+ // The settings file is corrupt, so reinitialize it. |
+ handle.reset(); |
+ |
+ // The settings failed to be read, so re-initialize them. |
+ return RecoverSettings(kInvalidFileHandle, out_data); |
+} |
+ |
+bool Settings::ReadSettings(FileHandle handle, Data* out_data) { |
+ if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) |
+ return false; |
+ |
+ if (!LoggingReadFile(handle, out_data, sizeof(*out_data))) |
+ return false; |
+ |
+ if (out_data->version != kSettingsVersion) { |
+ LOG(ERROR) << "Settings version is not " << kSettingsVersion; |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool Settings::WriteSettings(FileHandle handle, const Data* data) { |
+ if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) |
+ return false; |
+ |
+ if (HANDLE_EINTR(ftruncate(handle, 0)) != 0) { |
+ PLOG(ERROR) << "ftruncate settings file"; |
+ return false; |
+ } |
+ |
+ return LoggingWriteFile(handle, data, sizeof(*data)); |
+} |
+ |
+bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { |
+ LOG(INFO) << "Recovering settings file " << file_path(); |
+ |
+ ScopedFileHandle scoped_handle; |
+ if (handle == kInvalidFileHandle) { |
+ scoped_handle.reset(OpenForWriting().release()); |
Mark Mentovai
2015/03/09 19:12:35
If you open the handle yourself here, you need to
Robert Sesek
2015/03/09 21:16:27
Done.
|
+ handle = scoped_handle.get(); |
+ } |
+ |
+ if (handle == kInvalidFileHandle) { |
+ LOG(ERROR) << "Invalid file handle"; |
+ return false; |
+ } |
+ |
+ if (!InitializeSettings(handle)) |
Mark Mentovai
2015/03/09 19:12:35
You can make InitializeSettings() accept a Data* o
Robert Sesek
2015/03/09 21:16:27
It would be unnecessary to do that for the other c
|
+ return false; |
+ |
+ return ReadSettings(handle, out_data); |
+} |
+ |
+bool Settings::InitializeSettings(FileHandle handle) { |
+ uuid_t uuid; |
+ uuid_generate(uuid); |
+ |
+ Data settings; |
+ settings.client_id.InitializeFromBytes(uuid); |
+ |
+ return WriteSettings(handle, &settings); |
+} |
+ |
+} // namespace crashpad |