• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/mac_util.h"
11#include "base/mac/scoped_cftyperef.h"
12#include "base/mac/scoped_nsautorelease_pool.h"
13#include "base/process/launch.h"
14#include "base/strings/stringprintf.h"
15#include "base/strings/sys_string_conversions.h"
16#include "third_party/google_toolbox_for_mac/src/Foundation/GTMServiceManagement.h"
17
18namespace {
19
20NSString* SanitizeShellArgument(NSString* arg) {
21  if (!arg) {
22    return nil;
23  }
24  NSString *sanitize = [arg stringByReplacingOccurrencesOfString:@"'"
25                                                      withString:@"'\''"];
26  return [NSString stringWithFormat:@"'%@'", sanitize];
27}
28
29NSURL* 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    DLOG(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
59COMPILE_ASSERT(static_cast<int>(Launchd::User) ==
60               static_cast<int>(NSUserDomainMask),
61               NSUserDomainMask_value_changed);
62COMPILE_ASSERT(static_cast<int>(Launchd::Local) ==
63               static_cast<int>(NSLocalDomainMask),
64               NSLocalDomainMask_value_changed);
65COMPILE_ASSERT(static_cast<int>(Launchd::Network) ==
66               static_cast<int>(NSNetworkDomainMask),
67               NSNetworkDomainMask_value_changed);
68COMPILE_ASSERT(static_cast<int>(Launchd::System) ==
69               static_cast<int>(NSSystemDomainMask),
70               NSSystemDomainMask_value_changed);
71
72Launchd* Launchd::g_instance_ = NULL;
73
74Launchd* Launchd::GetInstance() {
75  if (!g_instance_) {
76    g_instance_ = Singleton<Launchd>::get();
77  }
78  return g_instance_;
79}
80
81void Launchd::SetInstance(Launchd* instance) {
82  if (instance) {
83    CHECK(!g_instance_);
84  }
85  g_instance_ = instance;
86}
87
88Launchd::~Launchd() { }
89
90CFDictionaryRef Launchd::CopyExports() {
91  return GTMCopyLaunchdExports();
92}
93
94CFDictionaryRef Launchd::CopyJobDictionary(CFStringRef label) {
95  return GTMSMJobCopyDictionary(label);
96}
97
98CFDictionaryRef Launchd::CopyDictionaryByCheckingIn(CFErrorRef* error) {
99  return GTMSMCopyJobCheckInDictionary(error);
100}
101
102bool Launchd::RemoveJob(CFStringRef label, CFErrorRef* error) {
103  return GTMSMJobRemove(label, error);
104}
105
106bool 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
133  base::LaunchOptions options;
134  options.new_process_group = true;
135  return base::LaunchProcess(argv, options, NULL);
136}
137
138CFMutableDictionaryRef Launchd::CreatePlistFromFile(Domain domain,
139                                                    Type type,
140                                                    CFStringRef name) {
141  base::mac::ScopedNSAutoreleasePool pool;
142  NSURL* ns_url = GetPlistURL(domain, type, name);
143  NSMutableDictionary* plist =
144      [[NSMutableDictionary alloc] initWithContentsOfURL:ns_url];
145  return base::mac::NSToCFCast(plist);
146}
147
148bool Launchd::WritePlistToFile(Domain domain,
149                               Type type,
150                               CFStringRef name,
151                               CFDictionaryRef dict) {
152  base::mac::ScopedNSAutoreleasePool pool;
153  NSURL* ns_url = GetPlistURL(domain, type, name);
154  return [base::mac::CFToNSCast(dict) writeToURL:ns_url atomically:YES];
155}
156
157bool Launchd::DeletePlist(Domain domain, Type type, CFStringRef name) {
158  base::mac::ScopedNSAutoreleasePool pool;
159  NSURL* ns_url = GetPlistURL(domain, type, name);
160  NSError* err = nil;
161  if (![[NSFileManager defaultManager] removeItemAtPath:[ns_url path]
162                                                  error:&err]) {
163    if ([err code] != NSFileNoSuchFileError) {
164      DLOG(ERROR) << "DeletePlist: " << base::mac::NSToCFCast(err);
165    }
166    return false;
167  }
168  return true;
169}
170