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