/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. #import "KSCommandRunner.h"
  15. #import <unistd.h>
  16. // Following Unix conventions, a failure code is any non-zero value
  17. static const int kFailure = 1;
  18. @interface NSTask (KSTaskTimeout)
  19. // Returns YES if the task completed before the |timeout|, NO otherwise. If
  20. // NO is returned, that means your timeout expired but the task is still
  21. // running. Typically, callers will want to -terminate the task or clean it up
  22. // in some way.
  23. //
  24. // Args:
  25. // timeout - the number of seconds to wait for task to complete
  26. //
  27. // Returns:
  28. // YES if the task completed within the timeout, NO otherwise.
  29. - (BOOL)waitUntilExitWithTimeout:(NSTimeInterval)timeout;
  30. @end
  31. @implementation KSTaskCommandRunner
  32. + (id)commandRunner {
  33. return [[[self alloc] init] autorelease];
  34. }
  35. - (int)runCommand:(NSString *)path
  36. withArgs:(NSArray *)args
  37. environment:(NSDictionary *)env
  38. output:(NSString **)output
  39. stdError:(NSString **)stderror {
  40. if (path == nil)
  41. return kFailure;
  42. NSPipe *outPipe = [NSPipe pipe];
  43. NSPipe *errorPipe = [NSPipe pipe];
  44. NSTask *task = [[[NSTask alloc] init] autorelease];
  45. [task setLaunchPath:path];
  46. if (args) [task setArguments:args];
  47. if (env) [task setEnvironment:env];
  48. [task setStandardOutput:outPipe];
  49. [task setStandardError:errorPipe];
  50. // If EUID and UID are not the same, you can run into problems where
  51. // privileges are lost when shell scripts are executed without a
  52. // (BASH) -p flag. We save and restore the current UID around the task launch.
  53. // http://www.delorie.com/gnu/docs/bash/bashref_58.html for details.
  54. int savedUID = getuid();
  55. int eUID = geteuid();
  56. if (eUID == 0) {
  57. if (setuid(eUID)) { // COV_NF_LINE
  58. return kFailure; // COV_NF_LINE
  59. } // COV_NF_LINE
  60. }
  61. @try {
  62. // -launch will throw if it can't find |path|
  63. [task launch];
  64. }
  65. @catch (id ex) {
  66. GTMLoggerInfo(@"Caught exception while trying to launch task for "
  67. @"%@, args=%@, env=%@: ex=%@", path, args, env, ex);
  68. return kFailure;
  69. }
  70. if (output) {
  71. NSData *outData = [[outPipe fileHandleForReading] readDataToEndOfFile];
  72. NSString *outString =
  73. [[[NSString alloc] initWithData:outData
  74. encoding:NSUTF8StringEncoding] autorelease];
  75. *output = outString;
  76. }
  77. if (stderror) {
  78. NSData *errorData = [[errorPipe fileHandleForReading] readDataToEndOfFile];
  79. NSString *errorString =
  80. [[[NSString alloc] initWithData:errorData
  81. encoding:NSUTF8StringEncoding] autorelease];
  82. *stderror = errorString;
  83. }
  84. // Wait up to 1 hour for the task to complete
  85. BOOL ok = [task waitUntilExitWithTimeout:3600];
  86. // Restore our saved UID.
  87. if (eUID == 0) {
  88. setuid(savedUID); // COV_NF_LINE
  89. }
  90. if (!ok) {
  91. [task terminate]; // COV_NF_LINE
  92. return kFailure; // COV_NF_LINE
  93. }
  94. return [task terminationStatus];
  95. }
  96. - (int)runCommand:(NSString *)path
  97. withArgs:(NSArray *)args
  98. environment:(NSDictionary *)env
  99. output:(NSString **)output {
  100. NSString *discard;
  101. int result = [self runCommand:path
  102. withArgs:args
  103. environment:env
  104. output:output
  105. stdError:&discard];
  106. return result;
  107. }
  108. @end
  109. @implementation NSTask (KSTaskTimeout)
  110. // Spins the runloop for one second at a time up to a maximum of |timeout|
  111. // seconds, waiting for the task to finish running.
  112. - (BOOL)waitUntilExitWithTimeout:(NSTimeInterval)timeout {
  113. static const NSTimeInterval step = 1;
  114. NSTimeInterval waitTime = 0;
  115. while (waitTime < timeout && [self isRunning]) {
  116. waitTime += step;
  117. NSDate *stepSec = [NSDate dateWithTimeIntervalSinceNow:step];
  118. [[NSRunLoop currentRunLoop] runUntilDate:stepSec];
  119. }
  120. return ([self isRunning] == NO);
  121. }
  122. @end