PageRenderTime 36ms CodeModel.GetById 10ms app.highlight 23ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/Core/KSCommandRunner.m

http://macfuse.googlecode.com/
Objective C | 148 lines | 89 code | 26 blank | 33 comment | 15 complexity | c3a0fabfd0817584439ded56c3a41386 MD5 | raw file
  1// Copyright 2008 Google Inc.
  2//
  3// Licensed under the Apache License, Version 2.0 (the "License");
  4// you may not use this file except in compliance with the License.
  5// You may obtain a copy of the License at
  6//
  7//     http://www.apache.org/licenses/LICENSE-2.0
  8//
  9// Unless required by applicable law or agreed to in writing, software
 10// distributed under the License is distributed on an "AS IS" BASIS,
 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12// See the License for the specific language governing permissions and
 13// limitations under the License.
 14
 15#import "KSCommandRunner.h"
 16#import <unistd.h>
 17
 18// Following Unix conventions, a failure code is any non-zero value
 19static const int kFailure = 1;
 20
 21
 22@interface NSTask (KSTaskTimeout)
 23
 24// Returns YES if the task completed before the |timeout|, NO otherwise. If 
 25// NO is returned, that means your timeout expired but the task is still
 26// running. Typically, callers will want to -terminate the task or clean it up
 27// in some way.
 28// 
 29// Args:
 30//   timeout - the number of seconds to wait for task to complete
 31//
 32// Returns:
 33//   YES if the task completed within the timeout, NO otherwise.
 34- (BOOL)waitUntilExitWithTimeout:(NSTimeInterval)timeout;
 35
 36@end
 37
 38
 39@implementation KSTaskCommandRunner
 40
 41+ (id)commandRunner {
 42  return [[[self alloc] init] autorelease];
 43}
 44
 45- (int)runCommand:(NSString *)path
 46         withArgs:(NSArray *)args
 47      environment:(NSDictionary *)env
 48           output:(NSString **)output
 49         stdError:(NSString **)stderror {
 50  if (path == nil)
 51    return kFailure;
 52  
 53  NSPipe *outPipe = [NSPipe pipe];
 54  NSPipe *errorPipe = [NSPipe pipe];
 55  NSTask *task = [[[NSTask alloc] init] autorelease];
 56  [task setLaunchPath:path];
 57  if (args) [task setArguments:args];
 58  if (env) [task setEnvironment:env];
 59  [task setStandardOutput:outPipe];
 60  [task setStandardError:errorPipe];
 61
 62  // If EUID and UID are not the same, you can run into problems where
 63  // privileges are lost when shell scripts are executed without a
 64  // (BASH) -p flag. We save and restore the current UID around the task launch.
 65  // http://www.delorie.com/gnu/docs/bash/bashref_58.html for details.
 66  int savedUID = getuid();
 67  int eUID = geteuid();
 68  if (eUID == 0) {
 69    if (setuid(eUID)) {  // COV_NF_LINE
 70      return kFailure;  // COV_NF_LINE
 71    }  // COV_NF_LINE
 72  }
 73  
 74  @try {
 75    // -launch will throw if it can't find |path|
 76    [task launch];
 77  }
 78  @catch (id ex) {
 79    GTMLoggerInfo(@"Caught exception while trying to launch task for "
 80                  @"%@, args=%@, env=%@: ex=%@", path, args, env, ex);
 81    return kFailure;
 82  }
 83  
 84  if (output) {
 85    NSData *outData = [[outPipe fileHandleForReading] readDataToEndOfFile];
 86    NSString *outString = 
 87    [[[NSString alloc] initWithData:outData
 88                           encoding:NSUTF8StringEncoding] autorelease];
 89    *output = outString;
 90  }
 91  if (stderror) {
 92    NSData *errorData = [[errorPipe fileHandleForReading] readDataToEndOfFile];
 93    NSString *errorString =
 94    [[[NSString alloc] initWithData:errorData
 95                           encoding:NSUTF8StringEncoding] autorelease];
 96    *stderror = errorString;
 97  }
 98  
 99  // Wait up to 1 hour for the task to complete
100  BOOL ok = [task waitUntilExitWithTimeout:3600];
101
102  // Restore our saved UID.
103  if (eUID == 0) {
104    setuid(savedUID);  // COV_NF_LINE
105  }
106  
107  if (!ok) {
108    [task terminate];  // COV_NF_LINE
109    return kFailure;   // COV_NF_LINE
110  }
111
112  return [task terminationStatus];
113}
114
115- (int)runCommand:(NSString *)path
116         withArgs:(NSArray *)args
117      environment:(NSDictionary *)env
118           output:(NSString **)output {
119  NSString *discard;
120  int result = [self runCommand:path
121                       withArgs:args
122                    environment:env
123                         output:output
124                       stdError:&discard];
125  return result;
126}
127
128@end
129
130
131@implementation NSTask (KSTaskTimeout)
132
133// Spins the runloop for one second at a time up to a maximum of |timeout|
134// seconds, waiting for the task to finish running.
135- (BOOL)waitUntilExitWithTimeout:(NSTimeInterval)timeout {
136  static const NSTimeInterval step = 1;
137  NSTimeInterval waitTime = 0;
138  while (waitTime < timeout && [self isRunning]) {
139    waitTime += step;
140    NSDate *stepSec = [NSDate dateWithTimeIntervalSinceNow:step];
141    [[NSRunLoop currentRunLoop] runUntilDate:stepSec];
142  }
143  return ([self isRunning] == NO);
144}
145
146@end
147
148