1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/common/mac/launchd.h"
7 #import <Foundation/Foundation.h>
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_cftyperef.h"
12 #include "base/mac/scoped_nsautorelease_pool.h"
13 #include "base/process/launch.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "third_party/google_toolbox_for_mac/src/Foundation/GTMServiceManagement.h"
20 NSString* SanitizeShellArgument(NSString* arg) {
24 NSString *sanitize = [arg stringByReplacingOccurrencesOfString:@"'"
26 return [NSString stringWithFormat:@"'%@'", sanitize];
29 NSURL* GetPlistURL(Launchd::Domain domain,
32 NSArray* library_paths =
33 NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, domain, YES);
34 DCHECK_EQ([library_paths count], 1U);
35 NSString* library_path = [library_paths objectAtIndex:0];
37 NSString *launch_dir_name = (type == Launchd::Daemon) ? @"LaunchDaemons"
39 NSString* launch_dir =
40 [library_path stringByAppendingPathComponent:launch_dir_name];
43 if (![[NSFileManager defaultManager] createDirectoryAtPath:launch_dir
44 withIntermediateDirectories:YES
47 DLOG(ERROR) << "GetPlistURL " << base::mac::NSToCFCast(err);
51 NSString* plist_file_path =
52 [launch_dir stringByAppendingPathComponent:base::mac::CFToNSCast(name)];
53 plist_file_path = [plist_file_path stringByAppendingPathExtension:@"plist"];
54 return [NSURL fileURLWithPath:plist_file_path isDirectory:NO];
59 static_assert(static_cast<int>(Launchd::User) ==
60 static_cast<int>(NSUserDomainMask),
61 "NSUserDomainMask value changed");
62 static_assert(static_cast<int>(Launchd::Local) ==
63 static_cast<int>(NSLocalDomainMask),
64 "NSLocalDomainMask value changed");
65 static_assert(static_cast<int>(Launchd::Network) ==
66 static_cast<int>(NSNetworkDomainMask),
67 "NSNetworkDomainMask value changed");
68 static_assert(static_cast<int>(Launchd::System) ==
69 static_cast<int>(NSSystemDomainMask),
70 "NSSystemDomainMask value changed");
72 Launchd* Launchd::g_instance_ = NULL;
74 Launchd* Launchd::GetInstance() {
76 g_instance_ = Singleton<Launchd>::get();
81 void Launchd::SetInstance(Launchd* instance) {
85 g_instance_ = instance;
88 Launchd::~Launchd() { }
90 CFDictionaryRef Launchd::CopyJobDictionary(CFStringRef label) {
91 return GTMSMJobCopyDictionary(label);
94 CFDictionaryRef Launchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
95 return GTMSMCopyJobCheckInDictionary(error);
98 bool Launchd::RemoveJob(CFStringRef label, CFErrorRef* error) {
99 return GTMSMJobRemove(label, error);
102 bool Launchd::RestartJob(Domain domain,
105 CFStringRef cf_session_type) {
106 base::mac::ScopedNSAutoreleasePool pool;
107 NSURL* url = GetPlistURL(domain, type, name);
108 NSString* ns_path = [url path];
109 ns_path = SanitizeShellArgument(ns_path);
110 const char* file_path = [ns_path fileSystemRepresentation];
112 NSString* ns_session_type =
113 SanitizeShellArgument(base::mac::CFToNSCast(cf_session_type));
114 if (!file_path || !ns_session_type) {
118 std::vector<std::string> argv;
119 argv.push_back("/bin/bash");
120 argv.push_back("--noprofile");
121 argv.push_back("-c");
122 std::string command = base::StringPrintf(
123 "/bin/launchctl unload -S %s %s;"
124 "/bin/launchctl load -S %s %s;",
125 [ns_session_type UTF8String], file_path,
126 [ns_session_type UTF8String], file_path);
127 argv.push_back(command);
129 base::LaunchOptions options;
130 options.new_process_group = true;
131 return base::LaunchProcess(argv, options).IsValid();
134 CFMutableDictionaryRef Launchd::CreatePlistFromFile(Domain domain,
137 base::mac::ScopedNSAutoreleasePool pool;
138 NSURL* ns_url = GetPlistURL(domain, type, name);
139 NSMutableDictionary* plist =
140 [[NSMutableDictionary alloc] initWithContentsOfURL:ns_url];
141 return base::mac::NSToCFCast(plist);
144 bool Launchd::WritePlistToFile(Domain domain,
147 CFDictionaryRef dict) {
148 base::mac::ScopedNSAutoreleasePool pool;
149 NSURL* ns_url = GetPlistURL(domain, type, name);
150 return [base::mac::CFToNSCast(dict) writeToURL:ns_url atomically:YES];
153 bool Launchd::DeletePlist(Domain domain, Type type, CFStringRef name) {
154 base::mac::ScopedNSAutoreleasePool pool;
155 NSURL* ns_url = GetPlistURL(domain, type, name);
157 if (![[NSFileManager defaultManager] removeItemAtPath:[ns_url path]
159 if ([err code] != NSFileNoSuchFileError) {
160 DLOG(ERROR) << "DeletePlist: " << base::mac::NSToCFCast(err);