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