summaryrefslogtreecommitdiff
path: root/chromium/chrome/common/mac/service_management.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/common/mac/service_management.mm')
-rw-r--r--chromium/chrome/common/mac/service_management.mm217
1 files changed, 217 insertions, 0 deletions
diff --git a/chromium/chrome/common/mac/service_management.mm b/chromium/chrome/common/mac/service_management.mm
new file mode 100644
index 00000000000..20fa32749f7
--- /dev/null
+++ b/chromium/chrome/common/mac/service_management.mm
@@ -0,0 +1,217 @@
+// 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"
+
+// This entire file is written in terms of the launch_data_t API, which is
+// deprecated with no replacement, so just ignore the warnings for now.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+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);
+}
+
+bool StringFromLaunchDataDictEntry(launch_data_t dict,
+ const char* key,
+ std::string* value) {
+ launch_data_t entry = launch_data_dict_lookup(dict, key);
+ if (!entry || launch_data_get_type(entry) != LAUNCH_DATA_STRING)
+ return false;
+ *value = std::string(launch_data_get_string(entry));
+ return true;
+}
+
+bool IntFromLaunchDataDictEntry(launch_data_t dict,
+ const char* key,
+ int* value) {
+ launch_data_t entry = launch_data_dict_lookup(dict, key);
+ if (!entry || launch_data_get_type(entry) != LAUNCH_DATA_INTEGER)
+ return false;
+ *value = launch_data_get_integer(entry);
+ return true;
+}
+
+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];
+ }
+
+ if (!options.mach_service_name.empty()) {
+ NSDictionary* service_entry =
+ @{base::SysUTF8ToNSString(options.mach_service_name) : @YES};
+ [opts setObject:service_entry forKey:@LAUNCH_JOBKEY_MACHSERVICES];
+ }
+
+ 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 {
+
+JobInfo::JobInfo() = default;
+JobInfo::JobInfo(const JobInfo& other) = default;
+JobInfo::~JobInfo() = default;
+
+JobCheckinInfo::JobCheckinInfo() = default;
+JobCheckinInfo::JobCheckinInfo(const JobCheckinInfo& other) = default;
+JobCheckinInfo::~JobCheckinInfo() = default;
+
+JobOptions::JobOptions() = default;
+JobOptions::JobOptions(const JobOptions& other) = default;
+JobOptions::~JobOptions() = default;
+
+bool GetJobInfo(const std::string& label, JobInfo* info) {
+ int error = 0;
+ ScopedLaunchData resp = DoServiceOp(LAUNCH_KEY_GETJOB, label, &error);
+
+ if (error)
+ return false;
+
+ std::string program;
+ if (!StringFromLaunchDataDictEntry(resp.get(), LAUNCH_JOBKEY_PROGRAM,
+ &program))
+ return false;
+
+ info->program = program;
+ int pid;
+ if (IntFromLaunchDataDictEntry(resp.get(), LAUNCH_JOBKEY_PID, &pid))
+ info->pid = pid;
+
+ return true;
+}
+
+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
+
+#pragma clang diagnostic pop