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