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. 4 5#include "chrome/common/mac/launchd.h" 6 7#import <Foundation/Foundation.h> 8#include <launch.h> 9 10#include "base/mac/foundation_util.h" 11#include "base/mac/scoped_cftyperef.h" 12#include "base/process/launch.h" 13#include "base/strings/stringprintf.h" 14#include "base/strings/sys_string_conversions.h" 15#include "chrome/common/mac/service_management.h" 16 17namespace { 18 19NSString* SanitizeShellArgument(NSString* arg) { 20 if (!arg) { 21 return nil; 22 } 23 NSString *sanitize = [arg stringByReplacingOccurrencesOfString:@"'" 24 withString:@"'\''"]; 25 return [NSString stringWithFormat:@"'%@'", sanitize]; 26} 27 28NSURL* GetPlistURL(Launchd::Domain domain, 29 Launchd::Type type, 30 CFStringRef name) { 31 NSArray* library_paths = 32 NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, domain, YES); 33 DCHECK_EQ([library_paths count], 1U); 34 NSString* library_path = [library_paths objectAtIndex:0]; 35 36 NSString *launch_dir_name = (type == Launchd::Daemon) ? @"LaunchDaemons" 37 : @"LaunchAgents"; 38 NSString* launch_dir = 39 [library_path stringByAppendingPathComponent:launch_dir_name]; 40 41 NSError* err; 42 if (![[NSFileManager defaultManager] createDirectoryAtPath:launch_dir 43 withIntermediateDirectories:YES 44 attributes:nil 45 error:&err]) { 46 DLOG(ERROR) << "GetPlistURL " << base::mac::NSToCFCast(err); 47 return nil; 48 } 49 50 NSString* plist_file_path = 51 [launch_dir stringByAppendingPathComponent:base::mac::CFToNSCast(name)]; 52 plist_file_path = [plist_file_path stringByAppendingPathExtension:@"plist"]; 53 return [NSURL fileURLWithPath:plist_file_path isDirectory:NO]; 54} 55 56} // namespace 57 58static_assert(static_cast<int>(Launchd::User) == 59 static_cast<int>(NSUserDomainMask), 60 "NSUserDomainMask value changed"); 61static_assert(static_cast<int>(Launchd::Local) == 62 static_cast<int>(NSLocalDomainMask), 63 "NSLocalDomainMask value changed"); 64static_assert(static_cast<int>(Launchd::Network) == 65 static_cast<int>(NSNetworkDomainMask), 66 "NSNetworkDomainMask value changed"); 67static_assert(static_cast<int>(Launchd::System) == 68 static_cast<int>(NSSystemDomainMask), 69 "NSSystemDomainMask value changed"); 70 71Launchd* Launchd::g_instance_ = NULL; 72 73Launchd* Launchd::GetInstance() { 74 if (!g_instance_) { 75 g_instance_ = base::Singleton<Launchd>::get(); 76 } 77 return g_instance_; 78} 79 80void Launchd::SetInstance(Launchd* instance) { 81 if (instance) { 82 CHECK(!g_instance_); 83 } 84 g_instance_ = instance; 85} 86 87Launchd::~Launchd() { } 88 89bool Launchd::GetJobInfo(const std::string& label, 90 mac::services::JobInfo* info) { 91 return mac::services::GetJobInfo(label, info); 92} 93 94bool Launchd::RemoveJob(const std::string& label) { 95 return mac::services::RemoveJob(label); 96} 97 98bool Launchd::RestartJob(Domain domain, 99 Type type, 100 CFStringRef name, 101 CFStringRef cf_session_type) { 102 @autoreleasepool { 103 NSURL* url = GetPlistURL(domain, type, name); 104 NSString* ns_path = [url path]; 105 ns_path = SanitizeShellArgument(ns_path); 106 const char* file_path = [ns_path fileSystemRepresentation]; 107 108 NSString* ns_session_type = 109 SanitizeShellArgument(base::mac::CFToNSCast(cf_session_type)); 110 if (!file_path || !ns_session_type) { 111 return false; 112 } 113 114 std::vector<std::string> argv; 115 argv.push_back("/bin/bash"); 116 argv.push_back("--noprofile"); 117 argv.push_back("-c"); 118 std::string command = 119 base::StringPrintf("/bin/launchctl unload -S %s %s;" 120 "/bin/launchctl load -S %s %s;", 121 [ns_session_type UTF8String], file_path, 122 [ns_session_type UTF8String], file_path); 123 argv.push_back(command); 124 125 base::LaunchOptions options; 126 options.new_process_group = true; 127 return base::LaunchProcess(argv, options).IsValid(); 128 } 129} 130 131CFMutableDictionaryRef Launchd::CreatePlistFromFile(Domain domain, 132 Type type, 133 CFStringRef name) { 134 @autoreleasepool { 135 NSURL* ns_url = GetPlistURL(domain, type, name); 136 NSMutableDictionary* plist = 137 [[NSMutableDictionary alloc] initWithContentsOfURL:ns_url]; 138 return base::mac::NSToCFCast(plist); 139 } 140} 141 142bool Launchd::WritePlistToFile(Domain domain, 143 Type type, 144 CFStringRef name, 145 CFDictionaryRef dict) { 146 @autoreleasepool { 147 NSURL* ns_url = GetPlistURL(domain, type, name); 148 return [base::mac::CFToNSCast(dict) writeToURL:ns_url atomically:YES]; 149 } 150} 151 152bool Launchd::PlistExists(Domain domain, Type type, CFStringRef name) { 153 @autoreleasepool { 154 NSURL* ns_url = GetPlistURL(domain, type, name); 155 BOOL is_dir = false; 156 return [[NSFileManager defaultManager] fileExistsAtPath:[ns_url path] 157 isDirectory:&is_dir] && 158 !is_dir; 159 } 160} 161 162bool Launchd::DeletePlist(Domain domain, Type type, CFStringRef name) { 163 @autoreleasepool { 164 NSURL* ns_url = GetPlistURL(domain, type, name); 165 NSError* err = nil; 166 if (![[NSFileManager defaultManager] removeItemAtPath:[ns_url path] 167 error:&err]) { 168 if ([err code] != NSFileNoSuchFileError) { 169 DLOG(ERROR) << "DeletePlist: " << base::mac::NSToCFCast(err); 170 return false; 171 } 172 } 173 return true; 174 } 175} 176