Program.inc revision 53ca1f3190680f3e86aebe0f72f7918d63f71e0d
1//===- llvm/Support/Unix/Program.cpp -----------------------------*- C++ -*-===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the Unix specific portion of the Program class. 11// 12//===----------------------------------------------------------------------===// 13 14//===----------------------------------------------------------------------===// 15//=== WARNING: Implementation here must contain only generic UNIX code that 16//=== is guaranteed to work on *all* UNIX variants. 17//===----------------------------------------------------------------------===// 18 19#include <llvm/Config/config.h> 20#include "Unix.h" 21#if HAVE_SYS_STAT_H 22#include <sys/stat.h> 23#endif 24#if HAVE_SYS_RESOURCE_H 25#include <sys/resource.h> 26#endif 27#if HAVE_SIGNAL_H 28#include <signal.h> 29#endif 30#if HAVE_FCNTL_H 31#include <fcntl.h> 32#endif 33#ifdef HAVE_POSIX_SPAWN 34#include <spawn.h> 35#if !defined(__APPLE__) 36 extern char **environ; 37#else 38#include <crt_externs.h> // _NSGetEnviron 39#endif 40#endif 41 42namespace llvm { 43using namespace sys; 44 45Program::Program() : Data_(0) {} 46 47Program::~Program() {} 48 49unsigned Program::GetPid() const { 50 uint64_t pid = reinterpret_cast<uint64_t>(Data_); 51 return static_cast<unsigned>(pid); 52} 53 54// This function just uses the PATH environment variable to find the program. 55Path 56Program::FindProgramByName(const std::string& progName) { 57 58 // Check some degenerate cases 59 if (progName.length() == 0) // no program 60 return Path(); 61 Path temp; 62 if (!temp.set(progName)) // invalid name 63 return Path(); 64 // Use the given path verbatim if it contains any slashes; this matches 65 // the behavior of sh(1) and friends. 66 if (progName.find('/') != std::string::npos) 67 return temp; 68 69 // At this point, the file name is valid and does not contain slashes. Search 70 // for it through the directories specified in the PATH environment variable. 71 72 // Get the path. If its empty, we can't do anything to find it. 73 const char *PathStr = getenv("PATH"); 74 if (PathStr == 0) 75 return Path(); 76 77 // Now we have a colon separated list of directories to search; try them. 78 size_t PathLen = strlen(PathStr); 79 while (PathLen) { 80 // Find the first colon... 81 const char *Colon = std::find(PathStr, PathStr+PathLen, ':'); 82 83 // Check to see if this first directory contains the executable... 84 Path FilePath; 85 if (FilePath.set(std::string(PathStr,Colon))) { 86 FilePath.appendComponent(progName); 87 if (FilePath.canExecute()) 88 return FilePath; // Found the executable! 89 } 90 91 // Nope it wasn't in this directory, check the next path in the list! 92 PathLen -= Colon-PathStr; 93 PathStr = Colon; 94 95 // Advance past duplicate colons 96 while (*PathStr == ':') { 97 PathStr++; 98 PathLen--; 99 } 100 } 101 return Path(); 102} 103 104static bool RedirectIO(const Path *Path, int FD, std::string* ErrMsg) { 105 if (Path == 0) // Noop 106 return false; 107 const char *File; 108 if (Path->isEmpty()) 109 // Redirect empty paths to /dev/null 110 File = "/dev/null"; 111 else 112 File = Path->c_str(); 113 114 // Open the file 115 int InFD = open(File, FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); 116 if (InFD == -1) { 117 MakeErrMsg(ErrMsg, "Cannot open file '" + std::string(File) + "' for " 118 + (FD == 0 ? "input" : "output")); 119 return true; 120 } 121 122 // Install it as the requested FD 123 if (dup2(InFD, FD) == -1) { 124 MakeErrMsg(ErrMsg, "Cannot dup2"); 125 close(InFD); 126 return true; 127 } 128 close(InFD); // Close the original FD 129 return false; 130} 131 132#ifdef HAVE_POSIX_SPAWN 133static bool RedirectIO_PS(const Path *Path, int FD, std::string *ErrMsg, 134 posix_spawn_file_actions_t &FileActions) { 135 if (Path == 0) // Noop 136 return false; 137 const char *File; 138 if (Path->isEmpty()) 139 // Redirect empty paths to /dev/null 140 File = "/dev/null"; 141 else 142 File = Path->c_str(); 143 144 if (int Err = posix_spawn_file_actions_addopen(&FileActions, FD, 145 File, FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666)) 146 return MakeErrMsg(ErrMsg, "Cannot dup2", Err); 147 return false; 148} 149#endif 150 151static void TimeOutHandler(int Sig) { 152} 153 154static void SetMemoryLimits (unsigned size) 155{ 156#if HAVE_SYS_RESOURCE_H && HAVE_GETRLIMIT && HAVE_SETRLIMIT 157 struct rlimit r; 158 __typeof__ (r.rlim_cur) limit = (__typeof__ (r.rlim_cur)) (size) * 1048576; 159 160 // Heap size 161 getrlimit (RLIMIT_DATA, &r); 162 r.rlim_cur = limit; 163 setrlimit (RLIMIT_DATA, &r); 164#ifdef RLIMIT_RSS 165 // Resident set size. 166 getrlimit (RLIMIT_RSS, &r); 167 r.rlim_cur = limit; 168 setrlimit (RLIMIT_RSS, &r); 169#endif 170#ifdef RLIMIT_AS // e.g. NetBSD doesn't have it. 171 // Virtual memory. 172 getrlimit (RLIMIT_AS, &r); 173 r.rlim_cur = limit; 174 setrlimit (RLIMIT_AS, &r); 175#endif 176#endif 177} 178 179bool 180Program::Execute(const Path &path, const char **args, const char **envp, 181 const Path **redirects, unsigned memoryLimit, 182 std::string *ErrMsg) { 183 // If this OS has posix_spawn and there is no memory limit being implied, use 184 // posix_spawn. It is more efficient than fork/exec. 185#ifdef HAVE_POSIX_SPAWN 186 if (memoryLimit == 0) { 187 posix_spawn_file_actions_t FileActions; 188 posix_spawn_file_actions_init(&FileActions); 189 190 if (redirects) { 191 // Redirect stdin/stdout. 192 if (RedirectIO_PS(redirects[0], 0, ErrMsg, FileActions) || 193 RedirectIO_PS(redirects[1], 1, ErrMsg, FileActions)) 194 return false; 195 if (redirects[1] == 0 || redirects[2] == 0 || 196 *redirects[1] != *redirects[2]) { 197 // Just redirect stderr 198 if (RedirectIO_PS(redirects[2], 2, ErrMsg, FileActions)) return false; 199 } else { 200 // If stdout and stderr should go to the same place, redirect stderr 201 // to the FD already open for stdout. 202 if (int Err = posix_spawn_file_actions_adddup2(&FileActions, 1, 2)) 203 return !MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout", Err); 204 } 205 } 206 207 if (!envp) 208#if !defined(__APPLE__) 209 envp = const_cast<const char **>(environ); 210#else 211 // environ is missing in dylibs. 212 envp = const_cast<const char **>(*_NSGetEnviron()); 213#endif 214 215 // Explicitly initialized to prevent what appears to be a valgrind false 216 // positive. 217 pid_t PID = 0; 218 int Err = posix_spawn(&PID, path.c_str(), &FileActions, /*attrp*/0, 219 const_cast<char **>(args), const_cast<char **>(envp)); 220 221 posix_spawn_file_actions_destroy(&FileActions); 222 223 if (Err) 224 return !MakeErrMsg(ErrMsg, "posix_spawn failed", Err); 225 226 Data_ = reinterpret_cast<void*>(PID); 227 return true; 228 } 229#endif 230 231 // Create a child process. 232 int child = fork(); 233 switch (child) { 234 // An error occured: Return to the caller. 235 case -1: 236 MakeErrMsg(ErrMsg, "Couldn't fork"); 237 return false; 238 239 // Child process: Execute the program. 240 case 0: { 241 // Redirect file descriptors... 242 if (redirects) { 243 // Redirect stdin 244 if (RedirectIO(redirects[0], 0, ErrMsg)) { return false; } 245 // Redirect stdout 246 if (RedirectIO(redirects[1], 1, ErrMsg)) { return false; } 247 if (redirects[1] && redirects[2] && 248 *(redirects[1]) == *(redirects[2])) { 249 // If stdout and stderr should go to the same place, redirect stderr 250 // to the FD already open for stdout. 251 if (-1 == dup2(1,2)) { 252 MakeErrMsg(ErrMsg, "Can't redirect stderr to stdout"); 253 return false; 254 } 255 } else { 256 // Just redirect stderr 257 if (RedirectIO(redirects[2], 2, ErrMsg)) { return false; } 258 } 259 } 260 261 // Set memory limits 262 if (memoryLimit!=0) { 263 SetMemoryLimits(memoryLimit); 264 } 265 266 // Execute! 267 if (envp != 0) 268 execve(path.c_str(), 269 const_cast<char **>(args), 270 const_cast<char **>(envp)); 271 else 272 execv(path.c_str(), 273 const_cast<char **>(args)); 274 // If the execve() failed, we should exit. Follow Unix protocol and 275 // return 127 if the executable was not found, and 126 otherwise. 276 // Use _exit rather than exit so that atexit functions and static 277 // object destructors cloned from the parent process aren't 278 // redundantly run, and so that any data buffered in stdio buffers 279 // cloned from the parent aren't redundantly written out. 280 _exit(errno == ENOENT ? 127 : 126); 281 } 282 283 // Parent process: Break out of the switch to do our processing. 284 default: 285 break; 286 } 287 288 Data_ = reinterpret_cast<void*>(child); 289 290 return true; 291} 292 293int 294Program::Wait(const sys::Path &path, 295 unsigned secondsToWait, 296 std::string* ErrMsg) 297{ 298#ifdef HAVE_SYS_WAIT_H 299 struct sigaction Act, Old; 300 301 if (Data_ == 0) { 302 MakeErrMsg(ErrMsg, "Process not started!"); 303 return -1; 304 } 305 306 // Install a timeout handler. The handler itself does nothing, but the simple 307 // fact of having a handler at all causes the wait below to return with EINTR, 308 // unlike if we used SIG_IGN. 309 if (secondsToWait) { 310 memset(&Act, 0, sizeof(Act)); 311 Act.sa_handler = TimeOutHandler; 312 sigemptyset(&Act.sa_mask); 313 sigaction(SIGALRM, &Act, &Old); 314 alarm(secondsToWait); 315 } 316 317 // Parent process: Wait for the child process to terminate. 318 int status; 319 uint64_t pid = reinterpret_cast<uint64_t>(Data_); 320 pid_t child = static_cast<pid_t>(pid); 321 while (waitpid(pid, &status, 0) != child) 322 if (secondsToWait && errno == EINTR) { 323 // Kill the child. 324 kill(child, SIGKILL); 325 326 // Turn off the alarm and restore the signal handler 327 alarm(0); 328 sigaction(SIGALRM, &Old, 0); 329 330 // Wait for child to die 331 if (wait(&status) != child) 332 MakeErrMsg(ErrMsg, "Child timed out but wouldn't die"); 333 else 334 MakeErrMsg(ErrMsg, "Child timed out", 0); 335 336 return -1; // Timeout detected 337 } else if (errno != EINTR) { 338 MakeErrMsg(ErrMsg, "Error waiting for child process"); 339 return -1; 340 } 341 342 // We exited normally without timeout, so turn off the timer. 343 if (secondsToWait) { 344 alarm(0); 345 sigaction(SIGALRM, &Old, 0); 346 } 347 348 // Return the proper exit status. Detect error conditions 349 // so we can return -1 for them and set ErrMsg informatively. 350 int result = 0; 351 if (WIFEXITED(status)) { 352 result = WEXITSTATUS(status); 353#ifdef HAVE_POSIX_SPAWN 354 // The posix_spawn child process returns 127 on any kind of error. 355 // Following the POSIX convention for command-line tools (which posix_spawn 356 // itself apparently does not), check to see if the failure was due to some 357 // reason other than the file not existing, and return 126 in this case. 358 if (result == 127 && path.exists()) 359 result = 126; 360#endif 361 if (result == 127) { 362 if (ErrMsg) 363 *ErrMsg = llvm::sys::StrError(ENOENT); 364 return -1; 365 } 366 if (result == 126) { 367 if (ErrMsg) 368 *ErrMsg = "Program could not be executed"; 369 return -1; 370 } 371 } else if (WIFSIGNALED(status)) { 372 if (ErrMsg) { 373 *ErrMsg = strsignal(WTERMSIG(status)); 374#ifdef WCOREDUMP 375 if (WCOREDUMP(status)) 376 *ErrMsg += " (core dumped)"; 377#endif 378 } 379 return -1; 380 } 381 return result; 382#else 383 if (ErrMsg) 384 *ErrMsg = "Program::Wait is not implemented on this platform yet!"; 385 return -1; 386#endif 387} 388 389bool 390Program::Kill(std::string* ErrMsg) { 391 if (Data_ == 0) { 392 MakeErrMsg(ErrMsg, "Process not started!"); 393 return true; 394 } 395 396 uint64_t pid64 = reinterpret_cast<uint64_t>(Data_); 397 pid_t pid = static_cast<pid_t>(pid64); 398 399 if (kill(pid, SIGKILL) != 0) { 400 MakeErrMsg(ErrMsg, "The process couldn't be killed!"); 401 return true; 402 } 403 404 return false; 405} 406 407bool Program::ChangeStdinToBinary(){ 408 // Do nothing, as Unix doesn't differentiate between text and binary. 409 return false; 410} 411 412bool Program::ChangeStdoutToBinary(){ 413 // Do nothing, as Unix doesn't differentiate between text and binary. 414 return false; 415} 416 417bool Program::ChangeStderrToBinary(){ 418 // Do nothing, as Unix doesn't differentiate between text and binary. 419 return false; 420} 421 422} 423