Commit 2788395c authored by Elly Fong-Jones's avatar Elly Fong-Jones Committed by Commit Bot

mac: drop dependencies on GTM service management, phase 1

This change drops uses of GTMSMJobRemove and GTMSMJobSubmit, and replaces themm
with uses of new functions in the mac::services namespace.

Bug: 903800
Change-Id: I0e38ddf3532274bd8ff0ea861f14707a183cd3ff
Reviewed-on: https://chromium-review.googlesource.com/c/1337823Reviewed-by: default avatarMark Mentovai <mark@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Commit-Queue: Avi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#610864}
parent 9c0db623
...@@ -17,15 +17,8 @@ ...@@ -17,15 +17,8 @@
using content::BrowserThread; using content::BrowserThread;
void ServiceProcessControl::Launcher::DoRun() { void ServiceProcessControl::Launcher::DoRun() {
base::ScopedCFTypeRef<CFDictionaryRef> launchd_plist( launched_ = mac::services::SubmitJob(
CreateServiceProcessLaunchdPlist(cmd_line_.get(), false)); GetServiceProcessJobOptions(cmd_line_.get(), false));
CFErrorRef error = NULL;
if (!GTMSMJobSubmit(launchd_plist, &error)) {
LOG(ERROR) << error;
CFRelease(error);
} else {
launched_ = true;
}
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
base::Bind(&Launcher::Notify, this)); base::Bind(&Launcher::Notify, this));
} }
...@@ -9,57 +9,39 @@ ...@@ -9,57 +9,39 @@
#include "base/mac/scoped_nsobject.h" #include "base/mac/scoped_nsobject.h"
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/common/mac/service_management.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/google_toolbox_for_mac/src/Foundation/GTMServiceManagement.h"
namespace { TEST(ServiceProcessControlMac, TestJobSubmitRemove) {
// Returns the parameters needed to launch a really simple script from launchd.
NSDictionary* TestJobDictionary(NSString* label_ns) {
NSString* shell_script_ns = @"sleep 10; echo TestGTMSMJobSubmitRemove";
base::scoped_nsobject<NSMutableArray> job_arguments(
[[NSMutableArray alloc] init]);
[job_arguments addObject:@"/bin/sh"];
[job_arguments addObject:@"-c"];
[job_arguments addObject:shell_script_ns];
NSMutableDictionary* job_dictionary_ns = [NSMutableDictionary dictionary];
[job_dictionary_ns setObject:label_ns forKey:@LAUNCH_JOBKEY_LABEL];
[job_dictionary_ns setObject:[NSNumber numberWithBool:YES]
forKey:@LAUNCH_JOBKEY_RUNATLOAD];
[job_dictionary_ns setObject:job_arguments
forKey:@LAUNCH_JOBKEY_PROGRAMARGUMENTS];
return job_dictionary_ns;
};
} // namespace
TEST(ServiceProcessControlMac, TestGTMSMJobSubmitRemove) {
NSString* label_ns = @"com.chromium.ServiceProcessStateFileManipulationTest"; NSString* label_ns = @"com.chromium.ServiceProcessStateFileManipulationTest";
std::string label(label_ns.UTF8String); std::string label(label_ns.UTF8String);
CFStringRef label_cf = base::mac::NSToCFCast(label_ns);
// If the job is loaded or running, remove it. // If the job is loaded or running, remove it.
pid_t pid = base::mac::PIDForJob(label); pid_t pid = base::mac::PIDForJob(label);
CFErrorRef error = NULL;
if (pid >= 0) if (pid >= 0)
ASSERT_TRUE(GTMSMJobRemove(label_cf, &error)); ASSERT_TRUE(mac::services::RemoveJob(label));
// The job should not be loaded or running. // The job should not be loaded or running.
pid = base::mac::PIDForJob(label); pid = base::mac::PIDForJob(label);
EXPECT_LT(pid, 0); EXPECT_LT(pid, 0);
// Submit a new job. // Submit a new job.
NSDictionary* job_dictionary_ns = TestJobDictionary(label_ns); mac::services::JobOptions options;
CFDictionaryRef job_dictionary_cf = base::mac::NSToCFCast(job_dictionary_ns); options.label = label;
ASSERT_TRUE(GTMSMJobSubmit(job_dictionary_cf, &error)); options.executable_path = "/bin/sh";
options.arguments = {"sh", "-c", "sleep 10; echo TestJobSubmitRemove"};
options.socket_name = "";
options.socket_key = "";
options.run_at_load = true;
options.auto_launch = false;
ASSERT_TRUE(mac::services::SubmitJob(options));
// The new job should be running. // The new job should be running.
pid = base::mac::PIDForJob(label); pid = base::mac::PIDForJob(label);
EXPECT_GT(pid, 0); EXPECT_GT(pid, 0);
// Remove the job. // Remove the job.
ASSERT_TRUE(GTMSMJobRemove(label_cf, &error)); ASSERT_TRUE(mac::services::RemoveJob(label));
// Wait for the job to be killed. // Wait for the job to be killed.
base::TimeDelta timeout_in_ms = TestTimeouts::action_timeout(); base::TimeDelta timeout_in_ms = TestTimeouts::action_timeout();
...@@ -77,5 +59,5 @@ TEST(ServiceProcessControlMac, TestGTMSMJobSubmitRemove) { ...@@ -77,5 +59,5 @@ TEST(ServiceProcessControlMac, TestGTMSMJobSubmitRemove) {
EXPECT_LT(pid, 0); EXPECT_LT(pid, 0);
// Attempting to remove the job again should fail. // Attempting to remove the job again should fail.
EXPECT_FALSE(GTMSMJobRemove(label_cf, &error)); EXPECT_FALSE(mac::services::RemoveJob(label));
} }
...@@ -146,6 +146,8 @@ static_library("common") { ...@@ -146,6 +146,8 @@ static_library("common") {
"mac/cfbundle_blocker.mm", "mac/cfbundle_blocker.mm",
"mac/launchd.h", "mac/launchd.h",
"mac/launchd.mm", "mac/launchd.mm",
"mac/service_management.h",
"mac/service_management.mm",
"media/media_resource_provider.cc", "media/media_resource_provider.cc",
"media/media_resource_provider.h", "media/media_resource_provider.h",
"media/webrtc_logging_message_data.cc", "media/webrtc_logging_message_data.cc",
...@@ -465,6 +467,8 @@ static_library("common") { ...@@ -465,6 +467,8 @@ static_library("common") {
"//third_party/google_toolbox_for_mac", "//third_party/google_toolbox_for_mac",
"//third_party/mach_override", "//third_party/mach_override",
] ]
libs = [ "ServiceManagement.framework" ]
} }
if (enable_plugins) { if (enable_plugins) {
......
...@@ -40,7 +40,7 @@ class Launchd { ...@@ -40,7 +40,7 @@ class Launchd {
virtual CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error); virtual CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error);
// Remove a launchd process from launchd. // Remove a launchd process from launchd.
virtual bool RemoveJob(CFStringRef label, CFErrorRef* error); virtual bool RemoveJob(const std::string& label);
// Used by a process controlled by launchd to restart itself. // Used by a process controlled by launchd to restart itself.
// |session_type| can be "Aqua", "LoginWindow", "Background", "StandardIO" or // |session_type| can be "Aqua", "LoginWindow", "Background", "StandardIO" or
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "base/process/launch.h" #include "base/process/launch.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "chrome/common/mac/service_management.h"
#include "third_party/google_toolbox_for_mac/src/Foundation/GTMServiceManagement.h" #include "third_party/google_toolbox_for_mac/src/Foundation/GTMServiceManagement.h"
namespace { namespace {
...@@ -95,8 +96,8 @@ CFDictionaryRef Launchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { ...@@ -95,8 +96,8 @@ CFDictionaryRef Launchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
return GTMSMCopyJobCheckInDictionary(error); return GTMSMCopyJobCheckInDictionary(error);
} }
bool Launchd::RemoveJob(CFStringRef label, CFErrorRef* error) { bool Launchd::RemoveJob(const std::string& label) {
return GTMSMJobRemove(label, error); return mac::services::RemoveJob(label);
} }
bool Launchd::RestartJob(Domain domain, bool Launchd::RestartJob(Domain domain,
......
...@@ -39,7 +39,7 @@ class MockLaunchd : public Launchd { ...@@ -39,7 +39,7 @@ class MockLaunchd : public Launchd {
CFDictionaryRef CopyJobDictionary(CFStringRef label) override; CFDictionaryRef CopyJobDictionary(CFStringRef label) override;
CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error) override; CFDictionaryRef CopyDictionaryByCheckingIn(CFErrorRef* error) override;
bool RemoveJob(CFStringRef label, CFErrorRef* error) override; bool RemoveJob(const std::string& label) override;
bool RestartJob(Domain domain, bool RestartJob(Domain domain,
Type type, Type type,
CFStringRef name, CFStringRef name,
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
...@@ -41,7 +42,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst, ...@@ -41,7 +42,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst,
if (!base::CreateDirectory(mac_os)) { if (!base::CreateDirectory(mac_os)) {
return false; return false;
} }
const char *data = "#! testbundle\n"; const char* data = "#! testbundle\n";
int len = strlen(data); int len = strlen(data);
if (base::WriteFile(*executable, data, len) != len) { if (base::WriteFile(*executable, data, len) != len) {
return false; return false;
...@@ -53,7 +54,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst, ...@@ -53,7 +54,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst,
const char info_plist_format[] = const char info_plist_format[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" " "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
"\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n" "<plist version=\"1.0\">\n"
"<dict>\n" "<dict>\n"
" <key>CFBundleDevelopmentRegion</key>\n" " <key>CFBundleDevelopmentRegion</key>\n"
...@@ -71,9 +72,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst, ...@@ -71,9 +72,7 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst,
"</dict>\n" "</dict>\n"
"</plist>\n"; "</plist>\n";
std::string info_plist_data = std::string info_plist_data =
base::StringPrintf(info_plist_format, base::StringPrintf(info_plist_format, name.c_str(), name.c_str(),
name.c_str(),
name.c_str(),
version_info::GetVersionNumber().c_str()); version_info::GetVersionNumber().c_str());
len = info_plist_data.length(); len = info_plist_data.length();
if (base::WriteFile(info_plist, info_plist_data.c_str(), len) != len) { if (base::WriteFile(info_plist, info_plist_data.c_str(), len) != len) {
...@@ -81,11 +80,9 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst, ...@@ -81,11 +80,9 @@ bool MockLaunchd::MakeABundle(const base::FilePath& dst,
} }
const UInt8* bundle_root_path = const UInt8* bundle_root_path =
reinterpret_cast<const UInt8*>(bundle_root->value().c_str()); reinterpret_cast<const UInt8*>(bundle_root->value().c_str());
base::ScopedCFTypeRef<CFURLRef> url( base::ScopedCFTypeRef<CFURLRef> url(CFURLCreateFromFileSystemRepresentation(
CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, kCFAllocatorDefault, bundle_root_path, bundle_root->value().length(),
bundle_root_path, true));
bundle_root->value().length(),
true));
base::ScopedCFTypeRef<CFBundleRef> bundle( base::ScopedCFTypeRef<CFBundleRef> bundle(
CFBundleCreate(kCFAllocatorDefault, url)); CFBundleCreate(kCFAllocatorDefault, url));
return bundle.get(); return bundle.get();
...@@ -109,8 +106,7 @@ MockLaunchd::MockLaunchd( ...@@ -109,8 +106,7 @@ MockLaunchd::MockLaunchd(
write_called_(false), write_called_(false),
delete_called_(false) {} delete_called_(false) {}
MockLaunchd::~MockLaunchd() { MockLaunchd::~MockLaunchd() {}
}
CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) { CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) {
if (!as_service_) { if (!as_service_) {
...@@ -122,19 +118,16 @@ CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) { ...@@ -122,19 +118,16 @@ CFDictionaryRef MockLaunchd::CopyJobDictionary(CFStringRef label) {
CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM); CFStringRef program = CFSTR(LAUNCH_JOBKEY_PROGRAM);
CFStringRef program_pid = CFSTR(LAUNCH_JOBKEY_PID); CFStringRef program_pid = CFSTR(LAUNCH_JOBKEY_PID);
const void *keys[] = { program, program_pid }; const void* keys[] = {program, program_pid};
base::ScopedCFTypeRef<CFStringRef> path( base::ScopedCFTypeRef<CFStringRef> path(
base::SysUTF8ToCFStringRef(file_.value())); base::SysUTF8ToCFStringRef(file_.value()));
int process_id = base::GetCurrentProcId(); int process_id = base::GetCurrentProcId();
base::ScopedCFTypeRef<CFNumberRef> pid( base::ScopedCFTypeRef<CFNumberRef> pid(
CFNumberCreate(NULL, kCFNumberIntType, &process_id)); CFNumberCreate(NULL, kCFNumberIntType, &process_id));
const void *values[] = { path, pid }; const void* values[] = {path, pid};
static_assert(arraysize(keys) == arraysize(values), static_assert(base::size(keys) == base::size(values),
"keys must have the same number of elements as values"); "keys must have the same number of elements as values");
return CFDictionaryCreate(kCFAllocatorDefault, return CFDictionaryCreate(kCFAllocatorDefault, keys, values, base::size(keys),
keys,
values,
arraysize(keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); &kCFTypeDictionaryValueCallBacks);
} }
...@@ -145,20 +138,17 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { ...@@ -145,20 +138,17 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS); CFStringRef program_args = CFSTR(LAUNCH_JOBKEY_PROGRAMARGUMENTS);
base::ScopedCFTypeRef<CFStringRef> path( base::ScopedCFTypeRef<CFStringRef> path(
base::SysUTF8ToCFStringRef(file_.value())); base::SysUTF8ToCFStringRef(file_.value()));
const void *array_values[] = { path.get() }; const void* array_values[] = {path.get()};
base::ScopedCFTypeRef<CFArrayRef> args(CFArrayCreate( base::ScopedCFTypeRef<CFArrayRef> args(CFArrayCreate(
kCFAllocatorDefault, array_values, 1, &kCFTypeArrayCallBacks)); kCFAllocatorDefault, array_values, 1, &kCFTypeArrayCallBacks));
if (!create_socket_) { if (!create_socket_) {
const void *keys[] = { program, program_args }; const void* keys[] = {program, program_args};
const void *values[] = { path, args }; const void* values[] = {path, args};
static_assert(arraysize(keys) == arraysize(values), static_assert(base::size(keys) == base::size(values),
"keys must have the same number of elements as values"); "keys must have the same number of elements as values");
return CFDictionaryCreate(kCFAllocatorDefault, return CFDictionaryCreate(kCFAllocatorDefault, keys, values,
keys, base::size(keys), &kCFTypeDictionaryKeyCallBacks,
values,
arraysize(keys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); &kCFTypeDictionaryValueCallBacks);
} }
...@@ -178,8 +168,7 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { ...@@ -178,8 +168,7 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
signature.protocolFamily = PF_UNIX; signature.protocolFamily = PF_UNIX;
signature.socketType = SOCK_STREAM; signature.socketType = SOCK_STREAM;
signature.protocol = 0; signature.protocol = 0;
size_t unix_addr_len = offsetof(struct sockaddr_un, size_t unix_addr_len = offsetof(struct sockaddr_un, sun_path) + path_len + 1;
sun_path) + path_len + 1;
base::ScopedCFTypeRef<CFDataRef> address( base::ScopedCFTypeRef<CFDataRef> address(
CFDataCreate(NULL, reinterpret_cast<UInt8*>(&unix_addr), unix_addr_len)); CFDataCreate(NULL, reinterpret_cast<UInt8*>(&unix_addr), unix_addr_len));
signature.address = address; signature.address = address;
...@@ -191,43 +180,36 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) { ...@@ -191,43 +180,36 @@ CFDictionaryRef MockLaunchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
EXPECT_NE(-1, local_pipe); EXPECT_NE(-1, local_pipe);
if (local_pipe == -1) { if (local_pipe == -1) {
if (error) { if (error) {
*error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, *error =
errno, NULL); CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, errno, NULL);
} }
return NULL; return NULL;
} }
base::ScopedCFTypeRef<CFNumberRef> socket_fd( base::ScopedCFTypeRef<CFNumberRef> socket_fd(
CFNumberCreate(NULL, kCFNumberIntType, &local_pipe)); CFNumberCreate(NULL, kCFNumberIntType, &local_pipe));
const void *socket_array_values[] = { socket_fd }; const void* socket_array_values[] = {socket_fd};
base::ScopedCFTypeRef<CFArrayRef> sockets(CFArrayCreate( base::ScopedCFTypeRef<CFArrayRef> sockets(CFArrayCreate(
kCFAllocatorDefault, socket_array_values, 1, &kCFTypeArrayCallBacks)); kCFAllocatorDefault, socket_array_values, 1, &kCFTypeArrayCallBacks));
CFStringRef socket_dict_key = CFSTR("ServiceProcessSocket"); CFStringRef socket_dict_key = CFSTR("ServiceProcessSocket");
const void *socket_keys[] = { socket_dict_key }; const void* socket_keys[] = {socket_dict_key};
const void *socket_values[] = { sockets }; const void* socket_values[] = {sockets};
static_assert(arraysize(socket_keys) == arraysize(socket_values), static_assert(base::size(socket_keys) == base::size(socket_values),
"socket_keys must have the same number of elements " "socket_keys must have the same number of elements "
"as socket_values"); "as socket_values");
base::ScopedCFTypeRef<CFDictionaryRef> socket_dict( base::ScopedCFTypeRef<CFDictionaryRef> socket_dict(CFDictionaryCreate(
CFDictionaryCreate(kCFAllocatorDefault, kCFAllocatorDefault, socket_keys, socket_values, base::size(socket_keys),
socket_keys, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
socket_values, const void* keys[] = {program, program_args, socket_key};
arraysize(socket_keys), const void* values[] = {path, args, socket_dict};
&kCFTypeDictionaryKeyCallBacks, static_assert(base::size(keys) == base::size(values),
&kCFTypeDictionaryValueCallBacks));
const void *keys[] = { program, program_args, socket_key };
const void *values[] = { path, args, socket_dict };
static_assert(arraysize(keys) == arraysize(values),
"keys must have the same number of elements as values"); "keys must have the same number of elements as values");
return CFDictionaryCreate(kCFAllocatorDefault, return CFDictionaryCreate(kCFAllocatorDefault, keys, values, base::size(keys),
keys,
values,
arraysize(keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); &kCFTypeDictionaryValueCallBacks);
} }
bool MockLaunchd::RemoveJob(CFStringRef label, CFErrorRef* error) { bool MockLaunchd::RemoveJob(const std::string& label) {
remove_called_ = true; remove_called_ = true;
std::move(quit_closure_).Run(); std::move(quit_closure_).Run();
return true; return true;
...@@ -242,10 +224,9 @@ bool MockLaunchd::RestartJob(Domain domain, ...@@ -242,10 +224,9 @@ bool MockLaunchd::RestartJob(Domain domain,
return true; return true;
} }
CFMutableDictionaryRef MockLaunchd::CreatePlistFromFile( CFMutableDictionaryRef MockLaunchd::CreatePlistFromFile(Domain domain,
Domain domain, Type type,
Type type, CFStringRef name) {
CFStringRef name) {
base::ScopedCFTypeRef<CFDictionaryRef> dict(CopyDictionaryByCheckingIn(NULL)); base::ScopedCFTypeRef<CFDictionaryRef> dict(CopyDictionaryByCheckingIn(NULL));
return CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict); return CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, dict);
} }
...@@ -258,9 +239,7 @@ bool MockLaunchd::WritePlistToFile(Domain domain, ...@@ -258,9 +239,7 @@ bool MockLaunchd::WritePlistToFile(Domain domain,
return true; return true;
} }
bool MockLaunchd::DeletePlist(Domain domain, bool MockLaunchd::DeletePlist(Domain domain, Type type, CFStringRef name) {
Type type,
CFStringRef name) {
delete_called_ = true; delete_called_ = true;
return true; return true;
} }
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_COMMON_MAC_SERVICE_MANAGEMENT_H_
#define CHROME_COMMON_MAC_SERVICE_MANAGEMENT_H_
#include <string>
#include <vector>
namespace mac {
namespace services {
struct JobOptions {
JobOptions();
JobOptions(const JobOptions& other);
~JobOptions();
std::string label;
// See launchd.plist(5) for details about these two fields. In short:
// - executable_path, if non-empty, is the absolute path to the executable
// for this job;
// - arguments[0] is the absolute *or relative* path to the executable if
// executable_path is empty; in either case, all of arguments is passed
// directly as argv to the job, meaning arguments[0] becomes argv[0]
// There are important caveats about the argument vector documented in the
// launchd.plist(5) man page.
std::string executable_path;
std::vector<std::string> arguments;
// See launchd.plist(5) "Sockets" for details about the meaning of these two
// fields. The socket_key field corresponds to a top-level key in the socket
// dictionary; the socket_name field corresponds to a pathname to a Unix
// domain socket (the SockPathName member in the Sockets dictionary).
std::string socket_name;
std::string socket_key;
// Whether to run this job immediately once it is loaded.
bool run_at_load;
// Whether to restart the job whenever it exits successfully. This also
// implicitly limits the job to interactive sessions only (i.e., the job will
// not run in system sessions).
bool auto_launch;
};
bool SubmitJob(const JobOptions& options);
bool RemoveJob(const std::string& label);
} // namespace services
} // namespace mac
#endif // CHROME_COMMON_MAC_SERVICE_MANAGEMENT_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/mac/service_management.h"
#import <CoreServices/CoreServices.h>
#import <Foundation/Foundation.h>
#import <ServiceManagement/ServiceManagement.h>
#include <errno.h>
#include <launch.h>
#include "base/compiler_specific.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/strings/sys_string_conversions.h"
namespace {
class ScopedLaunchData {
public:
explicit ScopedLaunchData(launch_data_type_t type)
: data_(launch_data_alloc(type)) {}
explicit ScopedLaunchData(launch_data_t data) : data_(data) {}
ScopedLaunchData(ScopedLaunchData&& other) : data_(other.release()) {}
~ScopedLaunchData() { reset(); }
void reset() {
if (data_)
launch_data_free(data_);
data_ = nullptr;
}
launch_data_t release() WARN_UNUSED_RESULT {
launch_data_t val = data_;
data_ = nullptr;
return val;
}
launch_data_t get() { return data_; }
operator launch_data_t() const { return data_; }
operator bool() const { return !!data_; }
private:
launch_data_t data_;
DISALLOW_COPY_AND_ASSIGN(ScopedLaunchData);
};
ScopedLaunchData SendLaunchMessage(ScopedLaunchData&& msg) {
return ScopedLaunchData(launch_msg(msg));
}
ScopedLaunchData LaunchDataFromString(const std::string& string) {
ScopedLaunchData result(LAUNCH_DATA_STRING);
// launch_data_set_string() will make a copy of the passed-in string.
launch_data_set_string(result, string.c_str());
return result;
}
int ErrnoFromLaunchData(launch_data_t data) {
if (launch_data_get_type(data) != LAUNCH_DATA_ERRNO)
return EINVAL;
return launch_data_get_errno(data);
}
ScopedLaunchData DoServiceOp(const char* verb,
const std::string& label,
int* error) {
ScopedLaunchData msg(LAUNCH_DATA_DICTIONARY);
launch_data_dict_insert(msg, LaunchDataFromString(label).release(), verb);
ScopedLaunchData result(SendLaunchMessage(std::move(msg)));
if (!result)
*error = errno;
return result;
}
NSArray* NSArrayFromStringVector(const std::vector<std::string>& vec) {
NSMutableArray* args = [NSMutableArray arrayWithCapacity:vec.size()];
for (const auto& item : vec) {
[args addObject:base::SysUTF8ToNSString(item)];
}
return args;
}
base::scoped_nsobject<NSDictionary> DictionaryForJobOptions(
const mac::services::JobOptions& options) {
base::scoped_nsobject<NSMutableDictionary> opts(
[[NSMutableDictionary alloc] init]);
[opts setObject:base::SysUTF8ToNSString(options.label)
forKey:@LAUNCH_JOBKEY_LABEL];
if (!options.executable_path.empty()) {
[opts setObject:base::SysUTF8ToNSString(options.executable_path)
forKey:@LAUNCH_JOBKEY_PROGRAM];
}
if (!options.arguments.empty()) {
[opts setObject:NSArrayFromStringVector(options.arguments)
forKey:@LAUNCH_JOBKEY_PROGRAMARGUMENTS];
}
DCHECK_EQ(options.socket_name.empty(), options.socket_key.empty());
if (!options.socket_name.empty() && !options.socket_key.empty()) {
NSDictionary* inner_dict = [NSDictionary
dictionaryWithObject:base::SysUTF8ToNSString(options.socket_name)
forKey:@LAUNCH_JOBSOCKETKEY_PATHNAME];
NSDictionary* outer_dict = [NSDictionary
dictionaryWithObject:inner_dict
forKey:base::SysUTF8ToNSString(options.socket_key)];
[opts setObject:outer_dict forKey:@LAUNCH_JOBKEY_SOCKETS];
}
if (options.run_at_load || options.auto_launch) {
[opts setObject:@YES forKey:@LAUNCH_JOBKEY_RUNATLOAD];
}
if (options.auto_launch) {
[opts setObject:@{
@LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT : @NO
}
forKey:@LAUNCH_JOBKEY_KEEPALIVE];
[opts setObject:@"Aqua" forKey:@LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE];
}
return base::scoped_nsobject<NSDictionary>(opts.release());
}
} // namespace
namespace mac {
namespace services {
JobOptions::JobOptions() = default;
JobOptions::JobOptions(const JobOptions& other) = default;
JobOptions::~JobOptions() = default;
bool SubmitJob(const JobOptions& options) {
base::scoped_nsobject<NSDictionary> options_dict =
DictionaryForJobOptions(options);
return SMJobSubmit(kSMDomainUserLaunchd,
base::mac::NSToCFCast(options_dict.get()), nullptr,
nullptr);
}
bool RemoveJob(const std::string& label) {
int error = 0;
ScopedLaunchData resp = DoServiceOp(LAUNCH_KEY_REMOVEJOB, label, &error);
if (!error)
error = ErrnoFromLaunchData(resp.get());
// On macOS 10.10+, removing a running job yields EINPROGRESS but the
// operation completes eventually (but not necessarily by the time RemoveJob
// is done). See rdar://18398683 for details.
if (error == EINPROGRESS)
error = 0;
return !error;
}
} // namespace services
} // namespace mac
...@@ -104,14 +104,11 @@ mojo::NamedPlatformChannel::ServerName GetServiceProcessServerName() { ...@@ -104,14 +104,11 @@ mojo::NamedPlatformChannel::ServerName GetServiceProcessServerName() {
bool ForceServiceProcessShutdown(const std::string& /* version */, bool ForceServiceProcessShutdown(const std::string& /* version */,
base::ProcessId /* process_id */) { base::ProcessId /* process_id */) {
base::mac::ScopedNSAutoreleasePool pool; const std::string& label =
CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); base::SysNSStringToUTF8(GetServiceProcessLaunchDLabel());
CFErrorRef err = NULL; bool ret = Launchd::GetInstance()->RemoveJob(label);
bool ret = Launchd::GetInstance()->RemoveJob(label, &err);
if (!ret) { if (!ret) {
DLOG(ERROR) << "ForceServiceProcessShutdown: " << err << " " DLOG(ERROR) << "ForceServiceProcessShutdown: " << label;
<< base::SysCFStringRefToUTF8(label);
CFRelease(err);
} }
return ret; return ret;
} }
...@@ -218,6 +215,24 @@ bool CheckServiceProcessReady() { ...@@ -218,6 +215,24 @@ bool CheckServiceProcessReady() {
return ready; return ready;
} }
mac::services::JobOptions GetServiceProcessJobOptions(
base::CommandLine* cmd_line,
bool for_auto_launch) {
mac::services::JobOptions options;
options.label = base::SysNSStringToUTF8(GetServiceProcessLaunchDLabel());
options.executable_path = cmd_line->GetProgram().value();
options.arguments = cmd_line->argv();
options.socket_name = GetServiceProcessSocketName().value();
options.socket_key =
base::SysNSStringToUTF8(GetServiceProcessLaunchDSocketKey());
options.run_at_load = for_auto_launch;
options.auto_launch = for_auto_launch;
return options;
}
CFDictionaryRef CreateServiceProcessLaunchdPlist(base::CommandLine* cmd_line, CFDictionaryRef CreateServiceProcessLaunchdPlist(base::CommandLine* cmd_line,
bool for_auto_launch) { bool for_auto_launch) {
base::mac::ScopedNSAutoreleasePool pool; base::mac::ScopedNSAutoreleasePool pool;
...@@ -443,12 +458,10 @@ void ExecFilePathWatcherCallback::NotifyPathChanged(const base::FilePath& path, ...@@ -443,12 +458,10 @@ void ExecFilePathWatcherCallback::NotifyPathChanged(const base::FilePath& path,
} }
} }
if (needs_shutdown) { if (needs_shutdown) {
CFStringRef label = const std::string& label =
base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); base::SysNSStringToUTF8(GetServiceProcessLaunchDLabel());
CFErrorRef err = NULL; if (!Launchd::GetInstance()->RemoveJob(label)) {
if (!Launchd::GetInstance()->RemoveJob(label, &err)) { DLOG(ERROR) << "RemoveJob " << label;
base::ScopedCFTypeRef<CFErrorRef> scoped_err(err);
DLOG(ERROR) << "RemoveJob " << err;
// Exiting with zero, so launchd doesn't restart the process. // Exiting with zero, so launchd doesn't restart the process.
exit(0); exit(0);
} }
......
...@@ -25,13 +25,15 @@ MultiProcessLock* TakeServiceRunningLock(bool waiting); ...@@ -25,13 +25,15 @@ MultiProcessLock* TakeServiceRunningLock(bool waiting);
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
#include "base/files/file_path_watcher.h" #include "base/files/file_path_watcher.h"
#include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_cftyperef.h"
#include "chrome/common/mac/service_management.h"
namespace base { namespace base {
class CommandLine; class CommandLine;
} }
CFDictionaryRef CreateServiceProcessLaunchdPlist(base::CommandLine* cmd_line, mac::services::JobOptions GetServiceProcessJobOptions(
bool for_auto_launch); base::CommandLine* cmd_line,
bool for_auto_launch);
#endif // OS_MACOSX #endif // OS_MACOSX
namespace base { namespace base {
......
...@@ -975,8 +975,8 @@ test("browser_tests") { ...@@ -975,8 +975,8 @@ test("browser_tests") {
"../browser/usb/usb_browsertest.cc", "../browser/usb/usb_browsertest.cc",
"../browser/web_bluetooth_browsertest.cc", "../browser/web_bluetooth_browsertest.cc",
"../common/mac/app_mode_chrome_locator_browsertest.mm", "../common/mac/app_mode_chrome_locator_browsertest.mm",
"../common/mac/mock_launchd.cc",
"../common/mac/mock_launchd.h", "../common/mac/mock_launchd.h",
"../common/mac/mock_launchd.mm",
"../common/time_format_browsertest.cc", "../common/time_format_browsertest.cc",
"../renderer/autofill/autofill_renderer_browsertest.cc", "../renderer/autofill/autofill_renderer_browsertest.cc",
"../renderer/autofill/fake_mojo_password_manager_driver.cc", "../renderer/autofill/fake_mojo_password_manager_driver.cc",
...@@ -2762,8 +2762,8 @@ test("unit_tests") { ...@@ -2762,8 +2762,8 @@ test("unit_tests") {
"../common/heap_profiler_controller_unittest.cc", "../common/heap_profiler_controller_unittest.cc",
"../common/ini_parser_unittest.cc", "../common/ini_parser_unittest.cc",
"../common/mac/cfbundle_blocker_unittest.mm", "../common/mac/cfbundle_blocker_unittest.mm",
"../common/mac/mock_launchd.cc",
"../common/mac/mock_launchd.h", "../common/mac/mock_launchd.h",
"../common/mac/mock_launchd.mm",
"../common/media_router/issue_unittest.cc", "../common/media_router/issue_unittest.cc",
"../common/media_router/media_route_unittest.cc", "../common/media_router/media_route_unittest.cc",
"../common/media_router/media_sink_unittest.cc", "../common/media_router/media_sink_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment