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