Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / common / mac / mock_launchd.cc
1 // Copyright (c) 2012 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/common/mac/mock_launchd.h"
6
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <sys/socket.h>
9 #include <sys/un.h>
10
11 #include "base/basictypes.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "chrome/common/chrome_version_info.h"
21 #include "chrome/common/mac/launchd.h"
22 #include "chrome/common/service_process_util.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 static sockaddr_un* throwaway_sockaddr_un;
26 static const size_t kMaxPipeNameLength =
27     sizeof(throwaway_sockaddr_un->sun_path);
28
29 // static
30 bool MockLaunchd::MakeABundle(const base::FilePath& dst,
31                               const std::string& name,
32                               base::FilePath* bundle_root,
33                               base::FilePath* executable) {
34   *bundle_root = dst.Append(name + std::string(".app"));
35   base::FilePath contents = bundle_root->AppendASCII("Contents");
36   base::FilePath mac_os = contents.AppendASCII("MacOS");
37   *executable = mac_os.Append(name);
38   base::FilePath info_plist = contents.Append("Info.plist");
39
40   if (!base::CreateDirectory(mac_os)) {
41     return false;
42   }
43   const char *data = "#! testbundle\n";
44   int len = strlen(data);
45   if (base::WriteFile(*executable, data, len) != len) {
46     return false;
47   }
48   if (chmod(executable->value().c_str(), 0555) != 0) {
49     return false;
50   }
51
52   chrome::VersionInfo version_info;
53
54   const char* info_plist_format =
55       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
56       "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
57           "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
58       "<plist version=\"1.0\">\n"
59       "<dict>\n"
60       "  <key>CFBundleDevelopmentRegion</key>\n"
61       "  <string>English</string>\n"
62       "  <key>CFBundleExecutable</key>\n"
63       "  <string>%s</string>\n"
64       "  <key>CFBundleIdentifier</key>\n"
65       "  <string>com.test.%s</string>\n"
66       "  <key>CFBundleInfoDictionaryVersion</key>\n"
67       "  <string>6.0</string>\n"
68       "  <key>CFBundleShortVersionString</key>\n"
69       "  <string>%s</string>\n"
70       "  <key>CFBundleVersion</key>\n"
71       "  <string>1</string>\n"
72       "</dict>\n"
73       "</plist>\n";
74   std::string info_plist_data =
75       base::StringPrintf(info_plist_format,
76                          name.c_str(),
77                          name.c_str(),
78                          version_info.Version().c_str());
79   len = info_plist_data.length();
80   if (base::WriteFile(info_plist, info_plist_data.c_str(), len) != len) {
81     return false;
82   }
83   const UInt8* bundle_root_path =
84       reinterpret_cast<const UInt8*>(bundle_root->value().c_str());
85   base::ScopedCFTypeRef<CFURLRef> url(
86       CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
87                                               bundle_root_path,
88                                               bundle_root->value().length(),
89                                               true));
90   base::ScopedCFTypeRef<CFBundleRef> bundle(
91       CFBundleCreate(kCFAllocatorDefault, url));
92   return bundle.get();
93 }
94
95 MockLaunchd::MockLaunchd(const base::FilePath& file,
96                          base::MessageLoop* loop,
97                          bool create_socket,
98                          bool as_service)
99     : file_(file),
100       message_loop_(loop),
101       create_socket_(create_socket),
102       as_service_(as_service),
103       restart_called_(false),
104       remove_called_(false),
105       checkin_called_(false),
106       write_called_(false),
107       delete_called_(false) {
108   std::string pipe_suffix("_SOCKET");
109   base::FilePath socket_path = file_;
110   while (socket_path.value().length() + pipe_suffix.length() >
111          kMaxPipeNameLength - 2) {
112     socket_path = socket_path.DirName();
113   }
114   pipe_name_ = socket_path.value() + pipe_suffix;
115 }
116
117 MockLaunchd::~MockLaunchd() {
118 }
119
120 CFDictionaryRef MockLaunchd::CopyExports() {
121   if (!create_socket_) {
122     ADD_FAILURE();
123     return NULL;
124   }
125
126   CFStringRef env_var =
127       base::mac::NSToCFCast(GetServiceProcessLaunchDSocketEnvVar());
128   base::ScopedCFTypeRef<CFStringRef> socket_path(CFStringCreateWithCString(
129       kCFAllocatorDefault, pipe_name_.c_str(), kCFStringEncodingUTF8));
130   const void *keys[] = { env_var };
131   const void *values[] = { socket_path };
132   COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match);
133   return CFDictionaryCreate(kCFAllocatorDefault,
134                             keys,
135                             values,
136                             arraysize(keys),
137                             &kCFTypeDictionaryKeyCallBacks,
138                             &kCFTypeDictionaryValueCallBacks);
139 }
140
141 CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) {
142   if (!as_service_) {
143     scoped_ptr<MultiProcessLock> running_lock(
144         TakeNamedLock(pipe_name_, false));
145     if (running_lock.get())
146       return NULL;
147   }
148
149   CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM);
150   CFStringRef program_pid = CFSTR(LAUNCH_JOBKEY_PID);
151   const void *keys[] = { program, program_pid };
152   base::ScopedCFTypeRef<CFStringRef> path(
153       base::SysUTF8ToCFStringRef(file_.value()));
154   int process_id = base::GetCurrentProcId();
155   base::ScopedCFTypeRef<CFNumberRef> pid(
156       CFNumberCreate(NULL, kCFNumberIntType, &process_id));
157   const void *values[] = { path, pid };
158   COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match);
159   return CFDictionaryCreate(kCFAllocatorDefault,
160                             keys,
161                             values,
162                             arraysize(keys),
163                             &kCFTypeDictionaryKeyCallBacks,
164                             &kCFTypeDictionaryValueCallBacks);
165 }
166
167 CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
168   checkin_called_ = true;
169   CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM);
170   CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS);
171   base::ScopedCFTypeRef<CFStringRef> path(
172       base::SysUTF8ToCFStringRef(file_.value()));
173   const void *array_values[] = { path.get() };
174   base::ScopedCFTypeRef<CFArrayRef> args(CFArrayCreate(
175       kCFAllocatorDefault, array_values, 1, &kCFTypeArrayCallBacks));
176
177   if (!create_socket_) {
178     const void *keys[] = { program, program_args };
179     const void *values[] = { path, args };
180     COMPILE_ASSERT(arraysize(keys) == arraysize(values),
181                    array_sizes_must_match);
182     return CFDictionaryCreate(kCFAllocatorDefault,
183                               keys,
184                               values,
185                               arraysize(keys),
186                               &kCFTypeDictionaryKeyCallBacks,
187                               &kCFTypeDictionaryValueCallBacks);
188   }
189
190   CFStringRef socket_key = CFSTR(LAUNCH_JOBKEY_SOCKETS);
191   int local_pipe = -1;
192   EXPECT_TRUE(as_service_);
193
194   // Create unix_addr structure.
195   struct sockaddr_un unix_addr = {0};
196   unix_addr.sun_family = AF_UNIX;
197   size_t path_len =
198       base::strlcpy(unix_addr.sun_path, pipe_name_.c_str(), kMaxPipeNameLength);
199   DCHECK_EQ(pipe_name_.length(), path_len);
200   unix_addr.sun_len = SUN_LEN(&unix_addr);
201
202   CFSocketSignature signature;
203   signature.protocolFamily = PF_UNIX;
204   signature.socketType = SOCK_STREAM;
205   signature.protocol = 0;
206   size_t unix_addr_len = offsetof(struct sockaddr_un,
207                                   sun_path) + path_len + 1;
208   base::ScopedCFTypeRef<CFDataRef> address(
209       CFDataCreate(NULL, reinterpret_cast<UInt8*>(&unix_addr), unix_addr_len));
210   signature.address = address;
211
212   CFSocketRef socket =
213       CFSocketCreateWithSocketSignature(NULL, &signature, 0, NULL, NULL);
214
215   local_pipe = CFSocketGetNative(socket);
216   EXPECT_NE(-1, local_pipe);
217   if (local_pipe == -1) {
218     if (error) {
219       *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX,
220                              errno, NULL);
221     }
222     return NULL;
223   }
224
225   base::ScopedCFTypeRef<CFNumberRef> socket_fd(
226       CFNumberCreate(NULL, kCFNumberIntType, &local_pipe));
227   const void *socket_array_values[] = { socket_fd };
228   base::ScopedCFTypeRef<CFArrayRef> sockets(CFArrayCreate(
229       kCFAllocatorDefault, socket_array_values, 1, &kCFTypeArrayCallBacks));
230   CFStringRef socket_dict_key = CFSTR("ServiceProcessSocket");
231   const void *socket_keys[] = { socket_dict_key };
232   const void *socket_values[] = { sockets };
233   COMPILE_ASSERT(arraysize(socket_keys) == arraysize(socket_values),
234                  socket_array_sizes_must_match);
235   base::ScopedCFTypeRef<CFDictionaryRef> socket_dict(
236       CFDictionaryCreate(kCFAllocatorDefault,
237                          socket_keys,
238                          socket_values,
239                          arraysize(socket_keys),
240                          &kCFTypeDictionaryKeyCallBacks,
241                          &kCFTypeDictionaryValueCallBacks));
242   const void *keys[] = { program, program_args, socket_key };
243   const void *values[] = { path, args, socket_dict };
244   COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match);
245   return CFDictionaryCreate(kCFAllocatorDefault,
246                             keys,
247                             values,
248                             arraysize(keys),
249                             &kCFTypeDictionaryKeyCallBacks,
250                             &kCFTypeDictionaryValueCallBacks);
251 }
252
253 bool MockLaunchd::RemoveJob(CFStringRef label, CFErrorRef* error) {
254   remove_called_ = true;
255   message_loop_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
256   return true;
257 }
258
259 bool MockLaunchd::RestartJob(Domain domain,
260                              Type type,
261                              CFStringRef name,
262                              CFStringRef session_type) {
263   restart_called_ = true;
264   message_loop_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
265   return true;
266 }
267
268 CFMutableDictionaryRef MockLaunchd::CreatePlistFromFile(
269     Domain domain,
270     Type type,
271     CFStringRef name)  {
272   base::ScopedCFTypeRef<CFDictionaryRef> dict(CopyDictionaryByCheckingIn(NULL));
273   return CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict);
274 }
275
276 bool MockLaunchd::WritePlistToFile(Domain domain,
277                                    Type type,
278                                    CFStringRef name,
279                                    CFDictionaryRef dict) {
280   write_called_ = true;
281   return true;
282 }
283
284 bool MockLaunchd::DeletePlist(Domain domain,
285                               Type type,
286                               CFStringRef name) {
287   delete_called_ = true;
288   return true;
289 }
290
291 void MockLaunchd::SignalReady() {
292   ASSERT_TRUE(as_service_);
293   running_lock_.reset(TakeNamedLock(pipe_name_, true));
294 }