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