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