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 | |
|
Mark Mentovai
2012/01/20 20:41:35
No blank line here.
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 9 #include <sys/socket.h> | |
| 10 #include <sys/un.h> | |
| 11 | |
| 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" | |
| 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 const size_t kMaxPipeNameLength = 104; | |
|
Mark Mentovai
2012/01/20 20:41:35
Rather than hard-coding 104 again here, can you us
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 26 | |
| 16 // static | 27 // static |
| 17 bool MockLaunchd::MakeABundle(const FilePath& dst, | 28 bool MockLaunchd::MakeABundle(const FilePath& dst, |
| 18 const std::string& name, | 29 const std::string& name, |
| 19 FilePath* bundle_root, | 30 FilePath* bundle_root, |
| 20 FilePath* executable) { | 31 FilePath* executable) { |
| 21 *bundle_root = dst.Append(name + std::string(".app")); | 32 *bundle_root = dst.Append(name + std::string(".app")); |
| 22 FilePath contents = bundle_root->AppendASCII("Contents"); | 33 FilePath contents = bundle_root->AppendASCII("Contents"); |
| 23 FilePath mac_os = contents.AppendASCII("MacOS"); | 34 FilePath mac_os = contents.AppendASCII("MacOS"); |
| 24 *executable = mac_os.Append(name); | 35 *executable = mac_os.Append(name); |
| 25 FilePath info_plist = contents.Append("Info.plist"); | 36 FilePath info_plist = contents.Append("Info.plist"); |
| 26 | 37 |
| 27 if (!file_util::CreateDirectory(mac_os)) { | 38 if (!file_util::CreateDirectory(mac_os)) { |
| 28 return false; | 39 return false; |
| 29 } | 40 } |
| 30 const char *data = "#! testbundle\n"; | 41 const char *data = "#! testbundle\n"; |
| 31 int len = strlen(data); | 42 int len = strlen(data); |
| 32 if (file_util::WriteFile(*executable, data, len) != len) { | 43 if (file_util::WriteFile(*executable, data, len) != len) { |
| 33 return false; | 44 return false; |
| 34 } | 45 } |
| 35 if (chmod(executable->value().c_str(), 0555) != 0) { | 46 if (chmod(executable->value().c_str(), 0555) != 0) { |
| 36 return false; | 47 return false; |
| 37 } | 48 } |
| 38 | 49 |
| 50 chrome::VersionInfo version_info; | |
| 51 | |
| 39 const char* info_plist_format = | 52 const char* info_plist_format = |
| 40 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | 53 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| 41 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " | 54 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " |
| 42 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" | 55 "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" |
| 43 "<plist version=\"1.0\">\n" | 56 "<plist version=\"1.0\">\n" |
| 44 "<dict>\n" | 57 "<dict>\n" |
| 45 " <key>CFBundleDevelopmentRegion</key>\n" | 58 " <key>CFBundleDevelopmentRegion</key>\n" |
| 46 " <string>English</string>\n" | 59 " <string>English</string>\n" |
| 47 " <key>CFBundleIdentifier</key>\n" | 60 " <key>CFBundleIdentifier</key>\n" |
| 48 " <string>com.test.%s</string>\n" | 61 " <string>com.test.%s</string>\n" |
| 49 " <key>CFBundleInfoDictionaryVersion</key>\n" | 62 " <key>CFBundleInfoDictionaryVersion</key>\n" |
| 50 " <string>6.0</string>\n" | 63 " <string>6.0</string>\n" |
| 51 " <key>CFBundleExecutable</key>\n" | 64 " <key>CFBundleExecutable</key>\n" |
| 52 " <string>%s</string>\n" | 65 " <string>%s</string>\n" |
| 53 " <key>CFBundleVersion</key>\n" | 66 " <key>CFBundleVersion</key>\n" |
| 54 " <string>1</string>\n" | 67 " <string>1</string>\n" |
| 68 " <key>CFBundleShortVersionString</key>\n" | |
|
Mark Mentovai
2012/01/20 20:41:35
The keys in this dict were already sorted, so you
Scott Byer
2012/01/20 23:13:37
They weren't completely, all fixed up.
Done.
| |
| 69 " <string>%s</string>\n" | |
| 55 "</dict>\n" | 70 "</dict>\n" |
| 56 "</plist>\n"; | 71 "</plist>\n"; |
| 57 std::string info_plist_data = base::StringPrintf(info_plist_format, | 72 std::string info_plist_data = |
| 58 name.c_str(), | 73 base::StringPrintf(info_plist_format, |
| 59 name.c_str()); | 74 name.c_str(), |
| 75 name.c_str(), | |
| 76 version_info.Version().c_str()); | |
| 60 len = info_plist_data.length(); | 77 len = info_plist_data.length(); |
| 61 if (file_util::WriteFile(info_plist, info_plist_data.c_str(), len) != len) { | 78 if (file_util::WriteFile(info_plist, info_plist_data.c_str(), len) != len) { |
| 62 return false; | 79 return false; |
| 63 } | 80 } |
| 64 const UInt8* bundle_root_path = | 81 const UInt8* bundle_root_path = |
| 65 reinterpret_cast<const UInt8*>(bundle_root->value().c_str()); | 82 reinterpret_cast<const UInt8*>(bundle_root->value().c_str()); |
| 66 base::mac::ScopedCFTypeRef<CFURLRef> url( | 83 base::mac::ScopedCFTypeRef<CFURLRef> url( |
| 67 CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, | 84 CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, |
| 68 bundle_root_path, | 85 bundle_root_path, |
| 69 bundle_root->value().length(), | 86 bundle_root->value().length(), |
| 70 true)); | 87 true)); |
| 71 base::mac::ScopedCFTypeRef<CFBundleRef> bundle( | 88 base::mac::ScopedCFTypeRef<CFBundleRef> bundle( |
| 72 CFBundleCreate(kCFAllocatorDefault, url)); | 89 CFBundleCreate(kCFAllocatorDefault, url)); |
| 73 return bundle.get(); | 90 return bundle.get(); |
| 74 } | 91 } |
| 75 | 92 |
| 93 MockLaunchd::MockLaunchd(const FilePath& file, MessageLoop* loop, | |
| 94 bool create_socket, bool as_service) | |
| 95 : file_(file), | |
| 96 message_loop_(loop), | |
| 97 create_socket_(create_socket), | |
| 98 as_service_(as_service), | |
| 99 restart_called_(false), | |
| 100 remove_called_(false), | |
| 101 job_called_(false), | |
| 102 checkin_called_(false), | |
| 103 write_called_(false), | |
| 104 delete_called_(false) { | |
| 105 std::string pipe_suffix("_SOCKET"); | |
| 106 FilePath socket_path = file_; | |
| 107 while (socket_path.value().length() + pipe_suffix.length() > | |
|
Mark Mentovai
2012/01/20 20:41:35
Shouldn’t this be >= or -2? You’ll need to add a s
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 108 kMaxPipeNameLength - 1) | |
|
Mark Mentovai
2012/01/20 20:41:35
Can we use {braces} here for readability because t
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 109 socket_path = socket_path.DirName(); | |
|
Mark Mentovai
2012/01/20 20:41:35
I guess this approach is OK for test code. The pat
| |
| 110 pipe_name_ = socket_path.value() + pipe_suffix; | |
| 111 } | |
| 112 | |
| 113 MockLaunchd::~MockLaunchd() { | |
| 114 } | |
| 115 | |
| 76 CFDictionaryRef MockLaunchd::CopyExports() { | 116 CFDictionaryRef MockLaunchd::CopyExports() { |
| 77 ADD_FAILURE(); | 117 if (!create_socket_) { |
| 78 return NULL; | 118 ADD_FAILURE(); |
| 119 return NULL; | |
| 120 } | |
| 121 | |
| 122 CFStringRef env_var = | |
| 123 base::mac::NSToCFCast(GetServiceProcessLaunchDSocketEnvVar()); | |
| 124 CFStringRef socket_path = CFStringCreateWithCString(kCFAllocatorDefault, | |
|
Mark Mentovai
2012/01/20 20:41:35
Leaked? Use a ScopedCFTypeRef.
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 125 pipe_name_.c_str(), | |
| 126 kCFStringEncodingUTF8); | |
| 127 const void *keys[] = { env_var }; | |
| 128 const void *values[] = { socket_path }; | |
|
Mark Mentovai
2012/01/20 20:41:35
You can DCHECK (probably statically, even, a la CO
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 129 return CFDictionaryCreate(kCFAllocatorDefault, | |
| 130 keys, | |
| 131 values, | |
| 132 arraysize(keys), | |
| 133 &kCFTypeDictionaryKeyCallBacks, | |
| 134 &kCFTypeDictionaryValueCallBacks); | |
| 79 } | 135 } |
| 80 | 136 |
| 81 CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) { | 137 CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) { |
| 82 ADD_FAILURE(); | 138 if (!as_service_) { |
| 83 return NULL; | 139 scoped_ptr<MultiProcessLock> running_lock( |
| 140 TakeNamedLock(pipe_name_, false)); | |
| 141 if (running_lock.get()) | |
| 142 return NULL; | |
| 143 } | |
| 144 | |
| 145 CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); | |
| 146 CFStringRef program_pid = CFSTR(LAUNCH_JOBKEY_PID); | |
| 147 const void *keys[] = { program, program_pid }; | |
| 148 base::mac::ScopedCFTypeRef<CFStringRef> path( | |
| 149 base::SysUTF8ToCFStringRef(file_.value())); | |
| 150 int process_id = base::GetCurrentProcId(); | |
| 151 base::mac::ScopedCFTypeRef<CFNumberRef> pid( | |
| 152 CFNumberCreate(NULL, kCFNumberIntType, &process_id)); | |
| 153 const void *values[] = { path, pid }; | |
|
Mark Mentovai
2012/01/20 20:41:35
Here too? 177 also?
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 154 return CFDictionaryCreate(kCFAllocatorDefault, | |
| 155 keys, | |
| 156 values, | |
| 157 arraysize(keys), | |
| 158 &kCFTypeDictionaryKeyCallBacks, | |
| 159 &kCFTypeDictionaryValueCallBacks); | |
| 84 } | 160 } |
| 85 | 161 |
| 86 CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { | 162 CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { |
| 87 checkin_called_ = true; | 163 checkin_called_ = true; |
| 88 CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); | 164 CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); |
| 89 CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS); | 165 CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS); |
| 90 const void *keys[] = { program, program_args }; | |
| 91 base::mac::ScopedCFTypeRef<CFStringRef> path( | 166 base::mac::ScopedCFTypeRef<CFStringRef> path( |
| 92 base::SysUTF8ToCFStringRef(file_.value())); | 167 base::SysUTF8ToCFStringRef(file_.value())); |
| 93 const void *array_values[] = { path.get() }; | 168 const void *array_values[] = { path.get() }; |
| 94 base::mac::ScopedCFTypeRef<CFArrayRef> args( | 169 base::mac::ScopedCFTypeRef<CFArrayRef> args( |
| 95 CFArrayCreate(kCFAllocatorDefault, | 170 CFArrayCreate(kCFAllocatorDefault, |
| 96 array_values, | 171 array_values, |
| 97 1, | 172 1, |
| 98 &kCFTypeArrayCallBacks)); | 173 &kCFTypeArrayCallBacks)); |
| 99 const void *values[] = { path, args }; | 174 |
| 175 if (!create_socket_) { | |
| 176 const void *keys[] = { program, program_args }; | |
| 177 const void *values[] = { path, args }; | |
| 178 return CFDictionaryCreate(kCFAllocatorDefault, | |
| 179 keys, | |
| 180 values, | |
| 181 arraysize(keys), | |
| 182 &kCFTypeDictionaryKeyCallBacks, | |
| 183 &kCFTypeDictionaryValueCallBacks); | |
| 184 } | |
| 185 | |
| 186 CFStringRef socket_key = CFSTR(LAUNCH_JOBKEY_SOCKETS); | |
| 187 int local_pipe = -1; | |
| 188 EXPECT_TRUE(as_service_); | |
| 189 | |
| 190 // Create unix_addr structure. | |
| 191 struct sockaddr_un unix_addr; | |
| 192 memset(&unix_addr, 0, sizeof(unix_addr)); | |
|
Mark Mentovai
2012/01/20 20:41:35
Or just
struct sockaddr_un unix_addr = {0};
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 193 unix_addr.sun_family = AF_UNIX; | |
| 194 int path_len = snprintf(unix_addr.sun_path, kMaxPipeNameLength, | |
| 195 "%s", pipe_name_.c_str()); | |
|
Mark Mentovai
2012/01/20 20:41:35
Don’t use snprintf to null-terminate. You can use
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 196 DCHECK_EQ(static_cast<int>(pipe_name_.length()), path_len); | |
| 197 size_t unix_addr_len = offsetof(struct sockaddr_un, | |
|
Mark Mentovai
2012/01/20 20:41:35
I would prefer to hold off on computing this until
Scott Byer
2012/01/20 23:13:37
Moved down to just before use. It's just complicat
| |
| 198 sun_path) + path_len + 1; | |
| 199 unix_addr.sun_len = SUN_LEN(&unix_addr); | |
| 200 | |
| 201 CFSocketSignature signature; | |
| 202 signature.protocolFamily = PF_UNIX; | |
| 203 signature.socketType = SOCK_STREAM; | |
| 204 signature.protocol = 0; | |
| 205 signature.address = CFDataCreate(NULL, reinterpret_cast<UInt8*>(&unix_addr), | |
| 206 unix_addr_len); | |
| 207 CFSocketRef socket = | |
| 208 CFSocketCreateWithSocketSignature(NULL, &signature, NULL, NULL, NULL); | |
|
Mark Mentovai
2012/01/20 20:41:35
Does this consume the reference to signature.addre
Scott Byer
2012/01/20 23:13:37
It's not spelled out explicitly in the docs, but b
| |
| 209 | |
| 210 local_pipe = CFSocketGetNative(socket); | |
| 211 EXPECT_NE(-1, local_pipe); | |
| 212 if (local_pipe == -1) { | |
| 213 if (error) | |
| 214 *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, | |
|
Mark Mentovai
2012/01/20 20:41:35
{brace me}
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 215 errno, NULL); | |
| 216 return NULL; | |
| 217 } | |
| 218 | |
| 219 base::mac::ScopedCFTypeRef<CFNumberRef> socket_fd( | |
| 220 CFNumberCreate(NULL, kCFNumberIntType, &local_pipe)); | |
| 221 const void *socket_array_values[] = { socket_fd }; | |
| 222 base::mac::ScopedCFTypeRef<CFArrayRef> sockets( | |
| 223 CFArrayCreate(kCFAllocatorDefault, | |
| 224 socket_array_values, | |
| 225 1, | |
| 226 &kCFTypeArrayCallBacks)); | |
| 227 CFStringRef socket_dict_key = CFSTR("ServiceProcessSocket"); | |
| 228 const void *socket_keys[] = { socket_dict_key }; | |
| 229 const void *socket_values[] = { sockets }; | |
|
Mark Mentovai
2012/01/20 20:41:35
arraysizes equal? 238 too?
Scott Byer
2012/01/20 23:13:37
Done.
| |
| 230 base::mac::ScopedCFTypeRef<CFDictionaryRef> socket_dict( | |
| 231 CFDictionaryCreate(kCFAllocatorDefault, | |
| 232 socket_keys, | |
| 233 socket_values, | |
| 234 arraysize(socket_keys), | |
| 235 &kCFTypeDictionaryKeyCallBacks, | |
| 236 &kCFTypeDictionaryValueCallBacks)); | |
| 237 const void *keys[] = { program, program_args, socket_key }; | |
| 238 const void *values[] = { path, args, socket_dict }; | |
| 100 return CFDictionaryCreate(kCFAllocatorDefault, | 239 return CFDictionaryCreate(kCFAllocatorDefault, |
| 101 keys, | 240 keys, |
| 102 values, | 241 values, |
| 103 arraysize(keys), | 242 arraysize(keys), |
| 104 &kCFTypeDictionaryKeyCallBacks, | 243 &kCFTypeDictionaryKeyCallBacks, |
| 105 &kCFTypeDictionaryValueCallBacks); | 244 &kCFTypeDictionaryValueCallBacks); |
| 106 } | 245 } |
| 107 | 246 |
| 108 bool MockLaunchd::RemoveJob(CFStringRef label, CFErrorRef* error) { | 247 bool MockLaunchd::RemoveJob(CFStringRef label, CFErrorRef* error) { |
| 109 remove_called_ = true; | 248 remove_called_ = true; |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 136 write_called_ = true; | 275 write_called_ = true; |
| 137 return true; | 276 return true; |
| 138 } | 277 } |
| 139 | 278 |
| 140 bool MockLaunchd::DeletePlist(Domain domain, | 279 bool MockLaunchd::DeletePlist(Domain domain, |
| 141 Type type, | 280 Type type, |
| 142 CFStringRef name) { | 281 CFStringRef name) { |
| 143 delete_called_ = true; | 282 delete_called_ = true; |
| 144 return true; | 283 return true; |
| 145 } | 284 } |
| 285 | |
| 286 void MockLaunchd::SignalReady() { | |
| 287 ASSERT_TRUE(as_service_); | |
| 288 running_lock_.reset(TakeNamedLock(pipe_name_, true)); | |
| 289 } | |
| OLD | NEW |