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/service_process_util_posix.h" | 5 #include "chrome/common/service_process_util_posix.h" |
6 | 6 |
7 #import <Foundation/Foundation.h> | 7 #import <Foundation/Foundation.h> |
8 #include <launch.h> | 8 #include <launch.h> |
9 | 9 |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/bind.h" | 12 #include "base/bind.h" |
13 #include "base/command_line.h" | 13 #include "base/command_line.h" |
14 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
15 #include "base/mac/bundle_locations.h" | 15 #include "base/mac/bundle_locations.h" |
16 #include "base/mac/foundation_util.h" | 16 #include "base/mac/foundation_util.h" |
17 #include "base/mac/mac_util.h" | 17 #include "base/mac/mac_util.h" |
18 #include "base/mac/scoped_nsautorelease_pool.h" | 18 #include "base/mac/scoped_nsautorelease_pool.h" |
19 #include "base/mac/scoped_nsobject.h" | 19 #include "base/mac/scoped_nsobject.h" |
20 #include "base/path_service.h" | 20 #include "base/path_service.h" |
21 #include "base/strings/string_util.h" | 21 #include "base/strings/string_util.h" |
22 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
23 #include "base/strings/sys_string_conversions.h" | 23 #include "base/strings/sys_string_conversions.h" |
24 #include "base/threading/thread_restrictions.h" | 24 #include "base/threading/thread_restrictions.h" |
25 #include "base/version.h" | 25 #include "base/version.h" |
26 #include "chrome/common/chrome_paths.h" | 26 #include "chrome/common/chrome_paths.h" |
27 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
28 #include "chrome/common/chrome_version_info.h" | 28 #include "chrome/common/chrome_version_info.h" |
29 #include "chrome/common/mac/launchd.h" | 29 #include "chrome/common/mac/launchd.h" |
30 #include "ipc/unix_domain_socket_util.h" | |
30 | 31 |
31 using ::base::FilePathWatcher; | 32 using ::base::FilePathWatcher; |
32 | 33 |
33 namespace { | 34 namespace { |
34 | 35 |
35 #define kServiceProcessSessionType "Aqua" | 36 #define kServiceProcessSessionType "Aqua" |
36 | 37 |
37 CFStringRef CopyServiceProcessLaunchDName() { | 38 CFStringRef CopyServiceProcessLaunchDName() { |
38 base::mac::ScopedNSAutoreleasePool pool; | 39 base::mac::ScopedNSAutoreleasePool pool; |
39 NSBundle* bundle = base::mac::FrameworkBundle(); | 40 NSBundle* bundle = base::mac::FrameworkBundle(); |
40 return CFStringCreateCopy(kCFAllocatorDefault, | 41 return CFStringCreateCopy(kCFAllocatorDefault, |
41 base::mac::NSToCFCast([bundle bundleIdentifier])); | 42 base::mac::NSToCFCast([bundle bundleIdentifier])); |
42 } | 43 } |
43 | 44 |
44 NSString* GetServiceProcessLaunchDLabel() { | 45 NSString* GetServiceProcessLaunchDLabel() { |
45 base::scoped_nsobject<NSString> name( | 46 base::scoped_nsobject<NSString> name( |
46 base::mac::CFToNSCast(CopyServiceProcessLaunchDName())); | 47 base::mac::CFToNSCast(CopyServiceProcessLaunchDName())); |
47 NSString *label = [name stringByAppendingString:@".service_process"]; | 48 NSString* label = [name stringByAppendingString:@".service_process"]; |
48 base::FilePath user_data_dir; | 49 base::FilePath user_data_dir; |
49 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); | 50 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); |
50 std::string user_data_dir_path = user_data_dir.value(); | 51 std::string user_data_dir_path = user_data_dir.value(); |
51 NSString *ns_path = base::SysUTF8ToNSString(user_data_dir_path); | 52 NSString* ns_path = base::SysUTF8ToNSString(user_data_dir_path); |
52 ns_path = [ns_path stringByReplacingOccurrencesOfString:@" " | 53 ns_path = [ns_path stringByReplacingOccurrencesOfString:@" " |
53 withString:@"_"]; | 54 withString:@"_"]; |
54 label = [label stringByAppendingString:ns_path]; | 55 label = [label stringByAppendingString:ns_path]; |
55 return label; | 56 return label; |
56 } | 57 } |
57 | 58 |
58 NSString* GetServiceProcessLaunchDSocketKey() { | 59 NSString* GetServiceProcessLaunchDSocketKey() { |
59 return @"ServiceProcessSocket"; | 60 return @"ServiceProcessSocket"; |
60 } | 61 } |
61 | 62 |
(...skipping 15 matching lines...) Expand all Loading... | |
77 ExecFilePathWatcherCallback() {} | 78 ExecFilePathWatcherCallback() {} |
78 ~ExecFilePathWatcherCallback() {} | 79 ~ExecFilePathWatcherCallback() {} |
79 | 80 |
80 bool Init(const base::FilePath& path); | 81 bool Init(const base::FilePath& path); |
81 void NotifyPathChanged(const base::FilePath& path, bool error); | 82 void NotifyPathChanged(const base::FilePath& path, bool error); |
82 | 83 |
83 private: | 84 private: |
84 FSRef executable_fsref_; | 85 FSRef executable_fsref_; |
85 }; | 86 }; |
86 | 87 |
88 base::FilePath GetServiceProcessSocketName() { | |
89 base::FilePath socket_name; | |
90 PathService::Get(base::DIR_TEMP, &socket_name); | |
91 std::string pipe_name = GetServiceProcessScopedName("srv"); | |
92 socket_name = socket_name.Append(pipe_name); | |
93 if (socket_name.value().size() < IPC::kMaxSocketNameLength) | |
94 return socket_name; | |
95 // Fallback to /tmp if $TMPDIR is too long. | |
Robert Sesek
2015/03/11 22:20:29
I'd suggest using an UMA histogram to track how of
| |
96 return base::FilePath("/tmp").Append(pipe_name); | |
97 } | |
98 | |
87 } // namespace | 99 } // namespace |
88 | 100 |
89 NSString* GetServiceProcessLaunchDSocketEnvVar() { | |
90 NSString *label = GetServiceProcessLaunchDLabel(); | |
91 NSString *env_var = [label stringByReplacingOccurrencesOfString:@"." | |
92 withString:@"_"]; | |
93 env_var = [env_var stringByAppendingString:@"_SOCKET"]; | |
94 env_var = [env_var uppercaseString]; | |
95 return env_var; | |
96 } | |
97 | |
98 // Gets the name of the service process IPC channel. | |
99 IPC::ChannelHandle GetServiceProcessChannel() { | 101 IPC::ChannelHandle GetServiceProcessChannel() { |
100 base::mac::ScopedNSAutoreleasePool pool; | 102 base::FilePath socket_name = GetServiceProcessSocketName(); |
101 std::string socket_path; | 103 VLOG(1) << "ServiceProcessChannel: " << socket_name.value(); |
102 base::scoped_nsobject<NSDictionary> dictionary( | 104 return IPC::ChannelHandle(socket_name.value()); |
103 base::mac::CFToNSCast(Launchd::GetInstance()->CopyExports())); | |
104 NSString *ns_socket_path = | |
105 [dictionary objectForKey:GetServiceProcessLaunchDSocketEnvVar()]; | |
106 if (ns_socket_path) { | |
107 socket_path = base::SysNSStringToUTF8(ns_socket_path); | |
108 } | |
109 return IPC::ChannelHandle(socket_path); | |
110 } | 105 } |
111 | 106 |
112 bool ForceServiceProcessShutdown(const std::string& /* version */, | 107 bool ForceServiceProcessShutdown(const std::string& /* version */, |
113 base::ProcessId /* process_id */) { | 108 base::ProcessId /* process_id */) { |
114 base::mac::ScopedNSAutoreleasePool pool; | 109 base::mac::ScopedNSAutoreleasePool pool; |
115 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); | 110 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); |
116 CFErrorRef err = NULL; | 111 CFErrorRef err = NULL; |
117 bool ret = Launchd::GetInstance()->RemoveJob(label, &err); | 112 bool ret = Launchd::GetInstance()->RemoveJob(label, &err); |
118 if (!ret) { | 113 if (!ret) { |
119 DLOG(ERROR) << "ForceServiceProcessShutdown: " << err << " " | 114 DLOG(ERROR) << "ForceServiceProcessShutdown: " << err << " " |
120 << base::SysCFStringRefToUTF8(label); | 115 << base::SysCFStringRefToUTF8(label); |
121 CFRelease(err); | 116 CFRelease(err); |
122 } | 117 } |
123 return ret; | 118 return ret; |
124 } | 119 } |
125 | 120 |
126 bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { | 121 bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { |
127 base::mac::ScopedNSAutoreleasePool pool; | 122 base::mac::ScopedNSAutoreleasePool pool; |
128 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); | 123 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); |
129 base::scoped_nsobject<NSDictionary> launchd_conf( | 124 base::scoped_nsobject<NSDictionary> launchd_conf( |
130 base::mac::CFToNSCast(Launchd::GetInstance()->CopyJobDictionary(label))); | 125 base::mac::CFToNSCast(Launchd::GetInstance()->CopyJobDictionary(label))); |
131 if (!launchd_conf.get()) { | 126 if (!launchd_conf.get()) { |
132 return false; | 127 return false; |
133 } | 128 } |
134 // Anything past here will return true in that there does appear | 129 // Anything past here will return true in that there does appear |
135 // to be a service process of some sort registered with launchd. | 130 // to be a service process of some sort registered with launchd. |
136 if (version) { | 131 if (version) { |
137 *version = "0"; | 132 *version = "0"; |
138 NSString *exe_path = [launchd_conf objectForKey:@ LAUNCH_JOBKEY_PROGRAM]; | 133 NSString* exe_path = [launchd_conf objectForKey:@ LAUNCH_JOBKEY_PROGRAM]; |
139 if (exe_path) { | 134 if (exe_path) { |
140 NSString *bundle_path = [[[exe_path stringByDeletingLastPathComponent] | 135 NSString* bundle_path = [[[exe_path stringByDeletingLastPathComponent] |
141 stringByDeletingLastPathComponent] | 136 stringByDeletingLastPathComponent] |
142 stringByDeletingLastPathComponent]; | 137 stringByDeletingLastPathComponent]; |
143 NSBundle *bundle = [NSBundle bundleWithPath:bundle_path]; | 138 NSBundle* bundle = [NSBundle bundleWithPath:bundle_path]; |
144 if (bundle) { | 139 if (bundle) { |
145 NSString *ns_version = | 140 NSString* ns_version = |
146 [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; | 141 [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; |
147 if (ns_version) { | 142 if (ns_version) { |
148 *version = base::SysNSStringToUTF8(ns_version); | 143 *version = base::SysNSStringToUTF8(ns_version); |
149 } else { | 144 } else { |
150 DLOG(ERROR) << "Unable to get version at: " | 145 DLOG(ERROR) << "Unable to get version at: " |
151 << reinterpret_cast<CFStringRef>(bundle_path); | 146 << reinterpret_cast<CFStringRef>(bundle_path); |
152 } | 147 } |
153 } else { | 148 } else { |
154 // The bundle has been deleted out from underneath the registered | 149 // The bundle has been deleted out from underneath the registered |
155 // job. | 150 // job. |
(...skipping 23 matching lines...) Expand all Loading... | |
179 << "CopyLaunchdDictionaryByCheckingIn: " << err; | 174 << "CopyLaunchdDictionaryByCheckingIn: " << err; |
180 CFRelease(err); | 175 CFRelease(err); |
181 return false; | 176 return false; |
182 } | 177 } |
183 state_->launchd_conf.reset(dict); | 178 state_->launchd_conf.reset(dict); |
184 return true; | 179 return true; |
185 } | 180 } |
186 | 181 |
187 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { | 182 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { |
188 DCHECK(state_); | 183 DCHECK(state_); |
189 NSDictionary *ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf); | 184 NSDictionary* ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf); |
190 NSDictionary* socket_dict = | 185 NSDictionary* socket_dict = |
191 [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS]; | 186 [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS]; |
192 NSArray* sockets = | 187 NSArray* sockets = |
193 [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()]; | 188 [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()]; |
194 DCHECK_EQ([sockets count], 1U); | 189 DCHECK_EQ([sockets count], 1U); |
195 int socket = [[sockets objectAtIndex:0] intValue]; | 190 int socket = [[sockets objectAtIndex:0] intValue]; |
196 base::FileDescriptor fd(socket, false); | 191 base::FileDescriptor fd(socket, false); |
197 return IPC::ChannelHandle(std::string(), fd); | 192 return IPC::ChannelHandle(std::string(), fd); |
198 } | 193 } |
199 | 194 |
(...skipping 24 matching lines...) Expand all Loading... | |
224 if (!ready) { | 219 if (!ready) { |
225 ForceServiceProcessShutdown(version, pid); | 220 ForceServiceProcessShutdown(version, pid); |
226 } | 221 } |
227 return ready; | 222 return ready; |
228 } | 223 } |
229 | 224 |
230 CFDictionaryRef CreateServiceProcessLaunchdPlist(base::CommandLine* cmd_line, | 225 CFDictionaryRef CreateServiceProcessLaunchdPlist(base::CommandLine* cmd_line, |
231 bool for_auto_launch) { | 226 bool for_auto_launch) { |
232 base::mac::ScopedNSAutoreleasePool pool; | 227 base::mac::ScopedNSAutoreleasePool pool; |
233 | 228 |
234 NSString *program = | 229 NSString* program = |
235 base::SysUTF8ToNSString(cmd_line->GetProgram().value()); | 230 base::SysUTF8ToNSString(cmd_line->GetProgram().value()); |
236 | 231 |
237 std::vector<std::string> args = cmd_line->argv(); | 232 std::vector<std::string> args = cmd_line->argv(); |
238 NSMutableArray *ns_args = [NSMutableArray arrayWithCapacity:args.size()]; | 233 NSMutableArray* ns_args = [NSMutableArray arrayWithCapacity:args.size()]; |
239 | 234 |
240 for (std::vector<std::string>::iterator iter = args.begin(); | 235 for (std::vector<std::string>::iterator iter = args.begin(); |
241 iter < args.end(); | 236 iter < args.end(); |
242 ++iter) { | 237 ++iter) { |
243 [ns_args addObject:base::SysUTF8ToNSString(*iter)]; | 238 [ns_args addObject:base::SysUTF8ToNSString(*iter)]; |
244 } | 239 } |
245 | 240 |
246 NSDictionary *socket = | 241 NSString* soket_name = |
247 [NSDictionary dictionaryWithObject:GetServiceProcessLaunchDSocketEnvVar() | 242 base::SysUTF8ToNSString(GetServiceProcessSocketName().value()); |
248 forKey:@ LAUNCH_JOBSOCKETKEY_SECUREWITHKEY]; | 243 |
249 NSDictionary *sockets = | 244 NSDictionary* socket = |
245 [NSDictionary dictionaryWithObject:soket_name | |
246 forKey:@LAUNCH_JOBSOCKETKEY_PATHNAME]; | |
247 NSDictionary* sockets = | |
250 [NSDictionary dictionaryWithObject:socket | 248 [NSDictionary dictionaryWithObject:socket |
251 forKey:GetServiceProcessLaunchDSocketKey()]; | 249 forKey:GetServiceProcessLaunchDSocketKey()]; |
252 | 250 |
253 // See the man page for launchd.plist. | 251 // See the man page for launchd.plist. |
254 NSMutableDictionary *launchd_plist = | 252 NSMutableDictionary* launchd_plist = |
255 [[NSMutableDictionary alloc] initWithObjectsAndKeys: | 253 [[NSMutableDictionary alloc] initWithObjectsAndKeys: |
256 GetServiceProcessLaunchDLabel(), @ LAUNCH_JOBKEY_LABEL, | 254 GetServiceProcessLaunchDLabel(), @LAUNCH_JOBKEY_LABEL, |
257 program, @ LAUNCH_JOBKEY_PROGRAM, | 255 program, @LAUNCH_JOBKEY_PROGRAM, |
258 ns_args, @ LAUNCH_JOBKEY_PROGRAMARGUMENTS, | 256 ns_args, @LAUNCH_JOBKEY_PROGRAMARGUMENTS, |
259 sockets, @ LAUNCH_JOBKEY_SOCKETS, | 257 sockets, @LAUNCH_JOBKEY_SOCKETS, |
260 nil]; | 258 nil]; |
261 | 259 |
262 if (for_auto_launch) { | 260 if (for_auto_launch) { |
263 // We want the service process to be able to exit if there are no services | 261 // We want the service process to be able to exit if there are no services |
264 // enabled. With a value of NO in the SuccessfulExit key, launchd will | 262 // enabled. With a value of NO in the SuccessfulExit key, launchd will |
265 // relaunch the service automatically in any other case than exiting | 263 // relaunch the service automatically in any other case than exiting |
266 // cleanly with a 0 return code. | 264 // cleanly with a 0 return code. |
267 NSDictionary *keep_alive = | 265 NSDictionary* keep_alive = |
268 [NSDictionary | 266 [NSDictionary |
269 dictionaryWithObject:[NSNumber numberWithBool:NO] | 267 dictionaryWithObject:[NSNumber numberWithBool:NO] |
270 forKey:@ LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT]; | 268 forKey:@LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT]; |
271 NSDictionary *auto_launchd_plist = | 269 NSDictionary* auto_launchd_plist = |
272 [[NSDictionary alloc] initWithObjectsAndKeys: | 270 [[NSDictionary alloc] initWithObjectsAndKeys: |
273 [NSNumber numberWithBool:YES], @ LAUNCH_JOBKEY_RUNATLOAD, | 271 [NSNumber numberWithBool:YES], @LAUNCH_JOBKEY_RUNATLOAD, |
274 keep_alive, @ LAUNCH_JOBKEY_KEEPALIVE, | 272 keep_alive, @LAUNCH_JOBKEY_KEEPALIVE, |
275 @ kServiceProcessSessionType, @ LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE, | 273 @kServiceProcessSessionType, @LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE, |
276 nil]; | 274 nil]; |
277 [launchd_plist addEntriesFromDictionary:auto_launchd_plist]; | 275 [launchd_plist addEntriesFromDictionary:auto_launchd_plist]; |
278 } | 276 } |
279 return reinterpret_cast<CFDictionaryRef>(launchd_plist); | 277 return reinterpret_cast<CFDictionaryRef>(launchd_plist); |
280 } | 278 } |
281 | 279 |
282 // Writes the launchd property list into the user's LaunchAgents directory, | 280 // Writes the launchd property list into the user's LaunchAgents directory, |
283 // creating that directory if needed. This will cause the service process to be | 281 // creating that directory if needed. This will cause the service process to be |
284 // auto launched on the next user login. | 282 // auto launched on the next user login. |
285 bool ServiceProcessState::AddToAutoRun() { | 283 bool ServiceProcessState::AddToAutoRun() { |
286 // We're creating directories and writing a file. | 284 // We're creating directories and writing a file. |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
434 CFErrorRef err = NULL; | 432 CFErrorRef err = NULL; |
435 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { | 433 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { |
436 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); | 434 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); |
437 DLOG(ERROR) << "RemoveJob " << err; | 435 DLOG(ERROR) << "RemoveJob " << err; |
438 // Exiting with zero, so launchd doesn't restart the process. | 436 // Exiting with zero, so launchd doesn't restart the process. |
439 exit(0); | 437 exit(0); |
440 } | 438 } |
441 } | 439 } |
442 } | 440 } |
443 } | 441 } |
OLD | NEW |