For PR351:
[oota-llvm.git] / lib / System / Unix / Program.inc
1 //===- llvm/System/Unix/Program.cpp -----------------------------*- C++ -*-===//
2 // 
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Reid Spencer and is distributed under the 
6 // University of Illinois Open Source 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 #include <sys/stat.h>
22 #include <fcntl.h>
23 #ifdef HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26 #include <iostream>
27
28 extern char** environ;
29
30 namespace llvm {
31 using namespace sys;
32
33 // This function just uses the PATH environment variable to find the program.
34 Path
35 Program::FindProgramByName(const std::string& progName) {
36
37   // Check some degenerate cases
38   if (progName.length() == 0) // no program
39     return Path();
40   Path temp;
41   if (!temp.setFile(progName)) // invalid name
42     return Path();
43   if (temp.executable()) // already executable as is
44     return temp;
45
46   // At this point, the file name is valid and its not executable
47  
48   // Get the path. If its empty, we can't do anything to find it.
49   const char *PathStr = getenv("PATH");
50   if (PathStr == 0) 
51     return Path();
52
53   // Now we have a colon separated list of directories to search; try them.
54   unsigned PathLen = strlen(PathStr);
55   while (PathLen) {
56     // Find the first colon...
57     const char *Colon = std::find(PathStr, PathStr+PathLen, ':');
58
59     // Check to see if this first directory contains the executable...
60     Path FilePath;
61     if (FilePath.setDirectory(std::string(PathStr,Colon))) {
62       FilePath.appendFile(progName);
63       if (FilePath.executable())
64         return FilePath;                    // Found the executable!
65     }
66
67     // Nope it wasn't in this directory, check the next path in the list!
68     PathLen -= Colon-PathStr;
69     PathStr = Colon;
70
71     // Advance past duplicate colons
72     while (*PathStr == ':') {
73       PathStr++;
74       PathLen--;
75     }
76   }
77   return Path();
78 }
79
80 static void RedirectFD(const std::string &File, int FD) {
81   if (File.empty()) return;  // Noop
82
83   // Open the file
84   int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
85   if (InFD == -1) {
86     ThrowErrno("Cannot open file '" + File + "' for "
87               + (FD == 0 ? "input" : "output") + "!\n");
88   }
89
90   dup2(InFD, FD);   // Install it as the requested FD
91   close(InFD);      // Close the original FD
92 }
93
94 static bool Timeout = false;
95 static void TimeOutHandler(int Sig) {
96   Timeout = true;
97 }
98
99 int 
100 Program::ExecuteAndWait(const Path& path, 
101                         const char** args,
102                         const char** envp,
103                         const Path** redirects,
104                         unsigned secondsToWait
105 ) {
106   if (!path.executable())
107     throw path.toString() + " is not executable"; 
108
109 #ifdef HAVE_SYS_WAIT_H
110   // Create a child process.
111   int child = fork();
112   switch (child) {
113     // An error occured:  Return to the caller.
114     case -1:
115       ThrowErrno(std::string("Couldn't execute program '") + path.toString() + 
116                  "'");
117       break;
118
119     // Child process: Execute the program.
120     case 0: {
121       // Redirect file descriptors...
122       if (redirects) {
123         if (redirects[0])
124           if (redirects[0]->isEmpty())
125             RedirectFD("/dev/null",0);
126           else
127             RedirectFD(redirects[0]->toString(), 0);
128         if (redirects[1])
129           if (redirects[1]->isEmpty())
130             RedirectFD("/dev/null",1);
131           else
132             RedirectFD(redirects[1]->toString(), 1);
133         if (redirects[1] && redirects[2] && 
134             *(redirects[1]) != *(redirects[2])) {
135           if (redirects[2]->isEmpty())
136             RedirectFD("/dev/null",2);
137           else
138             RedirectFD(redirects[2]->toString(), 2);
139         } else {
140           dup2(1, 2);
141         }
142       }
143
144       // Set up the environment
145       char** env = environ;
146       if (envp != 0)
147         env = (char**) envp;
148
149       // Execute!
150       execve (path.c_str(), (char** const)args, env);
151       // If the execve() failed, we should exit and let the parent pick up
152       // our non-zero exit status.
153       exit (errno);
154     }
155
156     // Parent process: Break out of the switch to do our processing.
157     default:
158       break;
159   }
160
161   // Make sure stderr and stdout have been flushed
162   std::cerr << std::flush;
163   std::cout << std::flush;
164   fsync(1);
165   fsync(2);
166
167   struct sigaction Act, Old;
168
169   // Install a timeout handler.
170   if (secondsToWait) {
171     Timeout = false;
172     Act.sa_sigaction = 0;
173     Act.sa_handler = TimeOutHandler;
174     sigemptyset(&Act.sa_mask);
175     Act.sa_flags = 0;
176     sigaction(SIGALRM, &Act, &Old);
177     alarm(secondsToWait);
178   }
179
180   // Parent process: Wait for the child process to terminate.
181   int status;
182   while (wait(&status) != child)
183     if (secondsToWait && errno == EINTR) {
184       // Kill the child.
185       kill(child, SIGKILL);
186         
187       // Turn off the alarm and restore the signal handler
188       alarm(0);
189       sigaction(SIGALRM, &Old, 0);
190
191       // Wait for child to die
192       if (wait(&status) != child)
193         ThrowErrno("Child timedout but wouldn't die");
194         
195       return -1;   // Timeout detected
196     } else {
197       ThrowErrno("Error waiting for child process");
198     }
199
200   // We exited normally without timeout, so turn off the timer.
201   if (secondsToWait) {
202     alarm(0);
203     sigaction(SIGALRM, &Old, 0);
204   }
205
206   // If the program exited normally with a zero exit status, return success!
207   if (WIFEXITED (status))
208     return WEXITSTATUS(status);
209   else if (WIFSIGNALED(status))
210     throw std::string("Program '") + path.toString() + 
211           "' received terminating signal.";
212     
213 #else
214   throw std::string(
215     "Program::ExecuteAndWait not implemented on this platform!\n");
216 #endif
217   return 0;
218 }
219
220 }
221 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab