1// Copyright (c) 2011, Google Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10//     * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14//     * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29//
30// Utility class that can persist a SimpleStringDictionary to disk.
31
32#import "client/mac/crash_generation/ConfigFile.h"
33
34#import <Foundation/Foundation.h>
35#include <stdio.h>
36#include <sys/time.h>
37
38#import "client/apple/Framework/BreakpadDefines.h"
39#import "common/mac/GTMDefines.h"
40
41
42namespace google_breakpad {
43
44//=============================================================================
45BOOL EnsureDirectoryPathExists(NSString *dirPath) {
46  NSFileManager *mgr = [NSFileManager defaultManager];
47
48  NSDictionary *attrs =
49    [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
50                                forKey:NSFilePosixPermissions];
51
52  return [mgr createDirectoryAtPath:dirPath
53        withIntermediateDirectories:YES
54                         attributes:attrs
55                              error:nil];
56}
57
58//=============================================================================
59BOOL ConfigFile::WriteData(const void *data, size_t length) {
60  size_t result = write(config_file_, data, length);
61
62  return result == length;
63}
64
65//=============================================================================
66BOOL ConfigFile::AppendConfigData(const char *key,
67                                  const void *data, size_t length) {
68  assert(config_file_ != -1);
69
70  if (!key) {
71    return NO;
72  }
73
74  if (!data) {
75    return NO;
76  }
77
78  // Write the key, \n, length of data (ascii integer), \n, data
79  char buffer[16];
80  char nl = '\n';
81  BOOL result = WriteData(key, strlen(key));
82
83  snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length);
84  result &= WriteData(buffer, strlen(buffer));
85  result &= WriteData(data, length);
86  result &= WriteData(&nl, 1);
87  return result;
88}
89
90//=============================================================================
91BOOL ConfigFile::AppendConfigString(const char *key,
92                                    const char *value) {
93  return AppendConfigData(key, value, strlen(value));
94}
95
96//=============================================================================
97BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) {
98  // Set process uptime parameter
99  struct timeval tv;
100  gettimeofday(&tv, NULL);
101
102  char processUptimeString[32], processCrashtimeString[32];
103  // Set up time if we've received the start time.
104  if (processStartTimeString) {
105    time_t processStartTime = strtol(processStartTimeString, NULL, 10);
106    time_t processUptime = tv.tv_sec - processStartTime;
107    // Store the uptime in milliseconds.
108    sprintf(processUptimeString, "%llu",
109        static_cast<unsigned long long int>(processUptime) * 1000);
110    if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString))
111      return false;
112  }
113
114  sprintf(processCrashtimeString, "%zd", tv.tv_sec);
115  return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME,
116                            processCrashtimeString);
117}
118
119//=============================================================================
120void ConfigFile::WriteFile(const char* directory,
121                           const SimpleStringDictionary *configurationParameters,
122                           const char *dump_dir,
123                           const char *minidump_id) {
124
125  assert(config_file_ == -1);
126
127  // Open and write out configuration file preamble
128  if (directory) {
129    snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX",
130             directory);
131  } else {
132    strlcpy(config_file_path_, "/tmp/Config-XXXXXX",
133            sizeof(config_file_path_));
134  }
135  config_file_ = mkstemp(config_file_path_);
136
137  if (config_file_ == -1) {
138    return;
139  }
140
141  has_created_file_ = true;
142
143  // Add the minidump dir
144  AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir);
145  AppendConfigString(kReporterMinidumpIDKey, minidump_id);
146
147  // Write out the configuration parameters
148  BOOL result = YES;
149  const SimpleStringDictionary &dictionary = *configurationParameters;
150
151  const SimpleStringDictionary::Entry *entry = NULL;
152  SimpleStringDictionary::Iterator iter(dictionary);
153
154  while ((entry = iter.Next())) {
155    result = AppendConfigString(entry->key, entry->value);
156
157    if (!result)
158      break;
159  }
160  AppendCrashTimeParameters(
161      configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME));
162
163  close(config_file_);
164  config_file_ = -1;
165}
166
167} // namespace google_breakpad
168