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