Revamp supported ops. Instead of just being supported or not, we now keep
[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   if (temp.executable()) // 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.setDirectory(std::string(PathStr,Colon))) {
66       FilePath.appendFile(progName);
67       if (FilePath.executable())
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.executable())
111     throw path.toString() + " is not executable"; 
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       // Set up the environment
149       char** env = environ;
150       if (envp != 0)
151         env = (char**) envp;
152
153       // Execute!
154       execve (path.c_str(), (char** const)args, env);
155       // If the execve() failed, we should exit and let the parent pick up
156       // our non-zero exit status.
157       exit (errno);
158     }
159
160     // Parent process: Break out of the switch to do our processing.
161     default:
162       break;
163   }
164
165   // Make sure stderr and stdout have been flushed
166   std::cerr << std::flush;
167   std::cout << std::flush;
168   fsync(1);
169   fsync(2);
170
171   struct sigaction Act, Old;
172
173   // Install a timeout handler.
174   if (secondsToWait) {
175     Timeout = false;
176     Act.sa_sigaction = 0;
177     Act.sa_handler = TimeOutHandler;
178     sigemptyset(&Act.sa_mask);
179     Act.sa_flags = 0;
180     sigaction(SIGALRM, &Act, &Old);
181     alarm(secondsToWait);
182   }
183
184   // Parent process: Wait for the child process to terminate.
185   int status;
186   while (wait(&status) != child)
187     if (secondsToWait && errno == EINTR) {
188       // Kill the child.
189       kill(child, SIGKILL);
190         
191       // Turn off the alarm and restore the signal handler
192       alarm(0);
193       sigaction(SIGALRM, &Old, 0);
194
195       // Wait for child to die
196       if (wait(&status) != child)
197         ThrowErrno("Child timedout but wouldn't die");
198         
199       return -1;   // Timeout detected
200     } else {
201       ThrowErrno("Error waiting for child process");
202     }
203
204   // We exited normally without timeout, so turn off the timer.
205   if (secondsToWait) {
206     alarm(0);
207     sigaction(SIGALRM, &Old, 0);
208   }
209
210   // If the program exited normally with a zero exit status, return success!
211   if (WIFEXITED (status))
212     return WEXITSTATUS(status);
213   else if (WIFSIGNALED(status))
214     return 1;
215     
216 #else
217   throw std::string(
218     "Program::ExecuteAndWait not implemented on this platform!\n");
219 #endif
220   return 0;
221 }
222
223 }
224 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab