Chromium Code Reviews| 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 return @"ServiceProcessSocket"; | |
| 60 } | |
| 61 | |
| 62 bool GetParentFSRef(const FSRef& child, FSRef* parent) { | 59 bool GetParentFSRef(const FSRef& child, FSRef* parent) { |
| 63 return FSGetCatalogInfo(&child, 0, NULL, NULL, NULL, parent) == noErr; | 60 return FSGetCatalogInfo(&child, 0, NULL, NULL, NULL, parent) == noErr; |
| 64 } | 61 } |
| 65 | 62 |
| 66 bool RemoveFromLaunchd() { | 63 bool RemoveFromLaunchd() { |
| 67 // We're killing a file. | 64 // We're killing a file. |
| 68 base::ThreadRestrictions::AssertIOAllowed(); | 65 base::ThreadRestrictions::AssertIOAllowed(); |
| 69 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); | 66 base::ScopedCFTypeRef<CFStringRef> name(CopyServiceProcessLaunchDName()); |
| 70 return Launchd::GetInstance()->DeletePlist(Launchd::User, | 67 return Launchd::GetInstance()->DeletePlist(Launchd::User, |
| 71 Launchd::Agent, | 68 Launchd::Agent, |
| 72 name); | 69 name); |
| 73 } | 70 } |
| 74 | 71 |
| 75 class ExecFilePathWatcherCallback { | 72 class ExecFilePathWatcherCallback { |
| 76 public: | 73 public: |
| 77 ExecFilePathWatcherCallback() {} | 74 ExecFilePathWatcherCallback() {} |
| 78 ~ExecFilePathWatcherCallback() {} | 75 ~ExecFilePathWatcherCallback() {} |
| 79 | 76 |
| 80 bool Init(const base::FilePath& path); | 77 bool Init(const base::FilePath& path); |
| 81 void NotifyPathChanged(const base::FilePath& path, bool error); | 78 void NotifyPathChanged(const base::FilePath& path, bool error); |
| 82 | 79 |
| 83 private: | 80 private: |
| 84 FSRef executable_fsref_; | 81 FSRef executable_fsref_; |
| 85 }; | 82 }; |
| 86 | 83 |
| 84 base::FilePath GetServiceProcessSocketName() { | |
| 85 base::FilePath socket_name; | |
| 86 PathService::Get(base::DIR_TEMP, &socket_name); | |
| 87 std::string pipe_name = GetServiceProcessScopedName("srv"); | |
| 88 socket_name = socket_name.Append(pipe_name); | |
| 89 if (socket_name.value().size() < IPC::kMaxSocketNameLength) | |
| 90 return socket_name; | |
| 91 // Fallback to /tmp if $TMPDIR is too long. | |
|
Robert Sesek
2015/03/05 22:34:02
When would this happen?
Vitaly Buka (NO REVIEWS)
2015/03/05 23:22:13
kMaxSocketNameLength is 104
name generated on my
| |
| 92 return base::FilePath("/tmp").Append(pipe_name); | |
| 93 } | |
| 94 | |
| 87 } // namespace | 95 } // namespace |
| 88 | 96 |
| 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() { | 97 IPC::ChannelHandle GetServiceProcessChannel() { |
| 100 base::mac::ScopedNSAutoreleasePool pool; | 98 base::FilePath socket_name = GetServiceProcessSocketName(); |
| 101 std::string socket_path; | 99 VLOG(1) << "ServiceProcessChannel: " << socket_name.value(); |
| 102 base::scoped_nsobject<NSDictionary> dictionary( | 100 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 } | 101 } |
| 111 | 102 |
| 112 bool ForceServiceProcessShutdown(const std::string& /* version */, | 103 bool ForceServiceProcessShutdown(const std::string& /* version */, |
| 113 base::ProcessId /* process_id */) { | 104 base::ProcessId /* process_id */) { |
| 114 base::mac::ScopedNSAutoreleasePool pool; | 105 base::mac::ScopedNSAutoreleasePool pool; |
| 115 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); | 106 CFStringRef label = base::mac::NSToCFCast(GetServiceProcessLaunchDLabel()); |
| 116 CFErrorRef err = NULL; | 107 CFErrorRef err = NULL; |
| 117 bool ret = Launchd::GetInstance()->RemoveJob(label, &err); | 108 bool ret = Launchd::GetInstance()->RemoveJob(label, &err); |
| 118 if (!ret) { | 109 if (!ret) { |
| 119 DLOG(ERROR) << "ForceServiceProcessShutdown: " << err << " " | 110 DLOG(ERROR) << "ForceServiceProcessShutdown: " << err << " " |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 178 DLOG(ERROR) << "ServiceProcess must be launched by launchd. " | 169 DLOG(ERROR) << "ServiceProcess must be launched by launchd. " |
| 179 << "CopyLaunchdDictionaryByCheckingIn: " << err; | 170 << "CopyLaunchdDictionaryByCheckingIn: " << err; |
| 180 CFRelease(err); | 171 CFRelease(err); |
| 181 return false; | 172 return false; |
| 182 } | 173 } |
| 183 state_->launchd_conf.reset(dict); | 174 state_->launchd_conf.reset(dict); |
| 184 return true; | 175 return true; |
| 185 } | 176 } |
| 186 | 177 |
| 187 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { | 178 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { |
| 188 DCHECK(state_); | 179 return ::GetServiceProcessChannel(); |
| 189 NSDictionary *ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf); | |
| 190 NSDictionary* socket_dict = | |
| 191 [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS]; | |
| 192 NSArray* sockets = | |
| 193 [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()]; | |
| 194 DCHECK_EQ([sockets count], 1U); | |
| 195 int socket = [[sockets objectAtIndex:0] intValue]; | |
| 196 base::FileDescriptor fd(socket, false); | |
| 197 return IPC::ChannelHandle(std::string(), fd); | |
| 198 } | 180 } |
| 199 | 181 |
| 200 bool CheckServiceProcessReady() { | 182 bool CheckServiceProcessReady() { |
| 201 std::string version; | 183 std::string version; |
| 202 pid_t pid; | 184 pid_t pid; |
| 203 if (!GetServiceProcessData(&version, &pid)) { | 185 if (!GetServiceProcessData(&version, &pid) || pid == -1) { |
| 204 return false; | 186 return false; |
| 205 } | 187 } |
| 206 Version service_version(version); | 188 Version service_version(version); |
| 207 bool ready = true; | 189 bool ready = true; |
| 208 if (!service_version.IsValid()) { | 190 if (!service_version.IsValid()) { |
| 209 ready = false; | 191 ready = false; |
| 210 } else { | 192 } else { |
| 211 chrome::VersionInfo version_info; | 193 chrome::VersionInfo version_info; |
| 212 Version running_version(version_info.Version()); | 194 Version running_version(version_info.Version()); |
| 213 if (!running_version.IsValid()) { | 195 if (!running_version.IsValid()) { |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 236 | 218 |
| 237 std::vector<std::string> args = cmd_line->argv(); | 219 std::vector<std::string> args = cmd_line->argv(); |
| 238 NSMutableArray *ns_args = [NSMutableArray arrayWithCapacity:args.size()]; | 220 NSMutableArray *ns_args = [NSMutableArray arrayWithCapacity:args.size()]; |
| 239 | 221 |
| 240 for (std::vector<std::string>::iterator iter = args.begin(); | 222 for (std::vector<std::string>::iterator iter = args.begin(); |
| 241 iter < args.end(); | 223 iter < args.end(); |
| 242 ++iter) { | 224 ++iter) { |
| 243 [ns_args addObject:base::SysUTF8ToNSString(*iter)]; | 225 [ns_args addObject:base::SysUTF8ToNSString(*iter)]; |
| 244 } | 226 } |
| 245 | 227 |
| 246 NSDictionary *socket = | |
| 247 [NSDictionary dictionaryWithObject:GetServiceProcessLaunchDSocketEnvVar() | |
| 248 forKey:@ LAUNCH_JOBSOCKETKEY_SECUREWITHKEY]; | |
| 249 NSDictionary *sockets = | |
| 250 [NSDictionary dictionaryWithObject:socket | |
| 251 forKey:GetServiceProcessLaunchDSocketKey()]; | |
| 252 | |
| 253 // See the man page for launchd.plist. | 228 // See the man page for launchd.plist. |
| 254 NSMutableDictionary *launchd_plist = | 229 NSMutableDictionary *launchd_plist = |
| 255 [[NSMutableDictionary alloc] initWithObjectsAndKeys: | 230 [[NSMutableDictionary alloc] initWithObjectsAndKeys: |
| 256 GetServiceProcessLaunchDLabel(), @ LAUNCH_JOBKEY_LABEL, | 231 GetServiceProcessLaunchDLabel(), @ LAUNCH_JOBKEY_LABEL, |
| 257 program, @ LAUNCH_JOBKEY_PROGRAM, | 232 program, @ LAUNCH_JOBKEY_PROGRAM, |
| 258 ns_args, @ LAUNCH_JOBKEY_PROGRAMARGUMENTS, | 233 ns_args, @ LAUNCH_JOBKEY_PROGRAMARGUMENTS, |
| 259 sockets, @ LAUNCH_JOBKEY_SOCKETS, | 234 [NSNumber numberWithBool:YES], @ LAUNCH_JOBKEY_RUNATLOAD, |
|
Robert Sesek
2015/03/05 22:34:02
This is changing the RunAtLoad to be from only whe
Robert Sesek
2015/03/05 22:34:02
You can use @YES
Vitaly Buka (NO REVIEWS)
2015/03/05 23:22:13
Done.
Vitaly Buka (NO REVIEWS)
2015/03/05 23:22:13
for_auto_launch means that this file is going to b
| |
| 260 nil]; | 235 nil]; |
| 261 | 236 |
| 262 if (for_auto_launch) { | 237 if (for_auto_launch) { |
| 263 // We want the service process to be able to exit if there are no services | 238 // 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 | 239 // enabled. With a value of NO in the SuccessfulExit key, launchd will |
| 265 // relaunch the service automatically in any other case than exiting | 240 // relaunch the service automatically in any other case than exiting |
| 266 // cleanly with a 0 return code. | 241 // cleanly with a 0 return code. |
| 267 NSDictionary *keep_alive = | 242 NSDictionary *keep_alive = |
| 268 [NSDictionary | 243 [NSDictionary |
| 269 dictionaryWithObject:[NSNumber numberWithBool:NO] | 244 dictionaryWithObject:[NSNumber numberWithBool:NO] |
| 270 forKey:@ LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT]; | 245 forKey:@ LAUNCH_JOBKEY_KEEPALIVE_SUCCESSFULEXIT]; |
| 271 NSDictionary *auto_launchd_plist = | 246 NSDictionary *auto_launchd_plist = |
| 272 [[NSDictionary alloc] initWithObjectsAndKeys: | 247 [[NSDictionary alloc] initWithObjectsAndKeys: |
| 273 [NSNumber numberWithBool:YES], @ LAUNCH_JOBKEY_RUNATLOAD, | |
| 274 keep_alive, @ LAUNCH_JOBKEY_KEEPALIVE, | 248 keep_alive, @ LAUNCH_JOBKEY_KEEPALIVE, |
| 275 @ kServiceProcessSessionType, @ LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE, | 249 @ kServiceProcessSessionType, @ LAUNCH_JOBKEY_LIMITLOADTOSESSIONTYPE, |
| 276 nil]; | 250 nil]; |
| 277 [launchd_plist addEntriesFromDictionary:auto_launchd_plist]; | 251 [launchd_plist addEntriesFromDictionary:auto_launchd_plist]; |
| 278 } | 252 } |
| 279 return reinterpret_cast<CFDictionaryRef>(launchd_plist); | 253 return reinterpret_cast<CFDictionaryRef>(launchd_plist); |
| 280 } | 254 } |
| 281 | 255 |
| 282 // Writes the launchd property list into the user's LaunchAgents directory, | 256 // 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 | 257 // creating that directory if needed. This will cause the service process to be |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 434 CFErrorRef err = NULL; | 408 CFErrorRef err = NULL; |
| 435 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { | 409 if (!Launchd::GetInstance()->RemoveJob(label, &err)) { |
| 436 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); | 410 base::ScopedCFTypeRef<CFErrorRef> scoped_err(err); |
| 437 DLOG(ERROR) << "RemoveJob " << err; | 411 DLOG(ERROR) << "RemoveJob " << err; |
| 438 // Exiting with zero, so launchd doesn't restart the process. | 412 // Exiting with zero, so launchd doesn't restart the process. |
| 439 exit(0); | 413 exit(0); |
| 440 } | 414 } |
| 441 } | 415 } |
| 442 } | 416 } |
| 443 } | 417 } |
| OLD | NEW |