Chromium Code Reviews| Index: chrome/common/mac/mock_launchd.cc |
| diff --git a/chrome/common/mac/mock_launchd.cc b/chrome/common/mac/mock_launchd.cc |
| index f9310df6a06984253413d445c252d2338173a4c4..96721fb44453cfc2430c86f738178c972740408b 100644 |
| --- a/chrome/common/mac/mock_launchd.cc |
| +++ b/chrome/common/mac/mock_launchd.cc |
| @@ -4,15 +4,28 @@ |
| #include "chrome/common/mac/mock_launchd.h" |
| +#include <CoreFoundation/CoreFoundation.h> |
| +#include <sys/socket.h> |
| +#include <sys/un.h> |
| + |
| #include "base/file_path.h" |
| #include "base/file_util.h" |
| +#include "base/mac/foundation_util.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/message_loop.h" |
| +#include "base/process_util.h" |
| +#include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/sys_string_conversions.h" |
| +#include "chrome/common/chrome_version_info.h" |
| #include "chrome/common/mac/launchd.h" |
| +#include "chrome/common/service_process_util.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| +static sockaddr_un* throwaway_sockaddr_un; |
| +static const size_t kMaxPipeNameLength = |
| + sizeof(throwaway_sockaddr_un->sun_path); |
| + |
| // static |
| bool MockLaunchd::MakeABundle(const FilePath& dst, |
| const std::string& name, |
| @@ -36,6 +49,8 @@ bool MockLaunchd::MakeABundle(const FilePath& dst, |
| return false; |
| } |
| + chrome::VersionInfo version_info; |
| + |
| const char* info_plist_format = |
| "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " |
| @@ -44,19 +59,23 @@ bool MockLaunchd::MakeABundle(const FilePath& dst, |
| "<dict>\n" |
| " <key>CFBundleDevelopmentRegion</key>\n" |
| " <string>English</string>\n" |
| + " <key>CFBundleExecutable</key>\n" |
| + " <string>%s</string>\n" |
| " <key>CFBundleIdentifier</key>\n" |
| " <string>com.test.%s</string>\n" |
| " <key>CFBundleInfoDictionaryVersion</key>\n" |
| " <string>6.0</string>\n" |
| - " <key>CFBundleExecutable</key>\n" |
| + " <key>CFBundleShortVersionString</key>\n" |
| " <string>%s</string>\n" |
| " <key>CFBundleVersion</key>\n" |
| " <string>1</string>\n" |
| "</dict>\n" |
| "</plist>\n"; |
| - std::string info_plist_data = base::StringPrintf(info_plist_format, |
| - name.c_str(), |
| - name.c_str()); |
| + std::string info_plist_data = |
| + base::StringPrintf(info_plist_format, |
| + name.c_str(), |
| + name.c_str(), |
| + version_info.Version().c_str()); |
| len = info_plist_data.length(); |
| if (file_util::WriteFile(info_plist, info_plist_data.c_str(), len) != len) { |
| return false; |
| @@ -73,21 +92,82 @@ bool MockLaunchd::MakeABundle(const FilePath& dst, |
| return bundle.get(); |
| } |
| +MockLaunchd::MockLaunchd(const FilePath& file, MessageLoop* loop, |
| + bool create_socket, bool as_service) |
| + : file_(file), |
| + message_loop_(loop), |
| + create_socket_(create_socket), |
| + as_service_(as_service), |
| + restart_called_(false), |
| + remove_called_(false), |
| + job_called_(false), |
| + checkin_called_(false), |
| + write_called_(false), |
| + delete_called_(false) { |
| + std::string pipe_suffix("_SOCKET"); |
| + FilePath socket_path = file_; |
| + while (socket_path.value().length() + pipe_suffix.length() > |
| + kMaxPipeNameLength - 2) { |
| + socket_path = socket_path.DirName(); |
| + } |
| + pipe_name_ = socket_path.value() + pipe_suffix; |
| +} |
| + |
| +MockLaunchd::~MockLaunchd() { |
| +} |
| + |
| CFDictionaryRef MockLaunchd::CopyExports() { |
| - ADD_FAILURE(); |
| - return NULL; |
| + if (!create_socket_) { |
| + ADD_FAILURE(); |
| + return NULL; |
| + } |
| + |
| + CFStringRef env_var = |
| + base::mac::NSToCFCast(GetServiceProcessLaunchDSocketEnvVar()); |
| + base::mac::ScopedCFTypeRef<CFStringRef> socket_path( |
| + CFStringCreateWithCString(kCFAllocatorDefault, pipe_name_.c_str(), |
| + kCFStringEncodingUTF8)); |
| + const void *keys[] = { env_var }; |
| + const void *values[] = { socket_path }; |
| + COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match); |
| + return CFDictionaryCreate(kCFAllocatorDefault, |
| + keys, |
| + values, |
| + arraysize(keys), |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks); |
| } |
| CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) { |
| - ADD_FAILURE(); |
| - return NULL; |
| + if (!as_service_) { |
| + scoped_ptr<MultiProcessLock> running_lock( |
| + TakeNamedLock(pipe_name_, false)); |
| + if (running_lock.get()) |
| + return NULL; |
| + } |
| + |
| + CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); |
| + CFStringRef program_pid = CFSTR(LAUNCH_JOBKEY_PID); |
| + const void *keys[] = { program, program_pid }; |
| + base::mac::ScopedCFTypeRef<CFStringRef> path( |
| + base::SysUTF8ToCFStringRef(file_.value())); |
| + int process_id = base::GetCurrentProcId(); |
| + base::mac::ScopedCFTypeRef<CFNumberRef> pid( |
| + CFNumberCreate(NULL, kCFNumberIntType, &process_id)); |
| + const void *values[] = { path, pid }; |
| + COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match); |
| + return CFDictionaryCreate(kCFAllocatorDefault, |
| + keys, |
| + values, |
| + arraysize(keys), |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks); |
| } |
| CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { |
| checkin_called_ = true; |
| CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); |
| CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS); |
| - const void *keys[] = { program, program_args }; |
| base::mac::ScopedCFTypeRef<CFStringRef> path( |
| base::SysUTF8ToCFStringRef(file_.value())); |
| const void *array_values[] = { path.get() }; |
| @@ -96,7 +176,77 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { |
| array_values, |
| 1, |
| &kCFTypeArrayCallBacks)); |
| - const void *values[] = { path, args }; |
| + |
| + if (!create_socket_) { |
| + const void *keys[] = { program, program_args }; |
| + const void *values[] = { path, args }; |
| + COMPILE_ASSERT(arraysize(keys) == arraysize(values), |
| + array_sizes_must_match); |
| + return CFDictionaryCreate(kCFAllocatorDefault, |
| + keys, |
| + values, |
| + arraysize(keys), |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks); |
| + } |
| + |
| + CFStringRef socket_key = CFSTR(LAUNCH_JOBKEY_SOCKETS); |
| + int local_pipe = -1; |
| + EXPECT_TRUE(as_service_); |
| + |
| + // Create unix_addr structure. |
| + struct sockaddr_un unix_addr = {0}; |
| + unix_addr.sun_family = AF_UNIX; |
| + 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.
|
| + base::strlcpy(unix_addr.sun_path, pipe_name_.c_str(), kMaxPipeNameLength); |
| + DCHECK_EQ(static_cast<int>(pipe_name_.length()), path_len); |
| + unix_addr.sun_len = SUN_LEN(&unix_addr); |
| + |
| + CFSocketSignature signature; |
| + signature.protocolFamily = PF_UNIX; |
| + signature.socketType = SOCK_STREAM; |
| + signature.protocol = 0; |
| + size_t unix_addr_len = offsetof(struct sockaddr_un, |
| + sun_path) + path_len + 1; |
| + signature.address = CFDataCreate(NULL, reinterpret_cast<UInt8*>(&unix_addr), |
| + unix_addr_len); |
| + CFSocketRef socket = |
| + CFSocketCreateWithSocketSignature(NULL, &signature, NULL, NULL, NULL); |
| + 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.
|
| + |
| + local_pipe = CFSocketGetNative(socket); |
| + EXPECT_NE(-1, local_pipe); |
| + if (local_pipe == -1) { |
| + if (error) { |
| + *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, |
| + errno, NULL); |
| + } |
| + return NULL; |
| + } |
| + |
| + base::mac::ScopedCFTypeRef<CFNumberRef> socket_fd( |
| + CFNumberCreate(NULL, kCFNumberIntType, &local_pipe)); |
| + const void *socket_array_values[] = { socket_fd }; |
| + base::mac::ScopedCFTypeRef<CFArrayRef> sockets( |
| + CFArrayCreate(kCFAllocatorDefault, |
| + socket_array_values, |
| + 1, |
| + &kCFTypeArrayCallBacks)); |
| + CFStringRef socket_dict_key = CFSTR("ServiceProcessSocket"); |
| + const void *socket_keys[] = { socket_dict_key }; |
| + const void *socket_values[] = { sockets }; |
| + 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
|
| + array_sizes_must_match); |
| + base::mac::ScopedCFTypeRef<CFDictionaryRef> socket_dict( |
| + CFDictionaryCreate(kCFAllocatorDefault, |
| + socket_keys, |
| + socket_values, |
| + arraysize(socket_keys), |
| + &kCFTypeDictionaryKeyCallBacks, |
| + &kCFTypeDictionaryValueCallBacks)); |
| + const void *keys[] = { program, program_args, socket_key }; |
| + const void *values[] = { path, args, socket_dict }; |
| + COMPILE_ASSERT(arraysize(keys) == arraysize(values), array_sizes_must_match); |
| return CFDictionaryCreate(kCFAllocatorDefault, |
| keys, |
| values, |
| @@ -143,3 +293,8 @@ bool MockLaunchd::DeletePlist(Domain domain, |
| delete_called_ = true; |
| return true; |
| } |
| + |
| +void MockLaunchd::SignalReady() { |
| + ASSERT_TRUE(as_service_); |
| + running_lock_.reset(TakeNamedLock(pipe_name_, true)); |
| +} |