a6c049b54b1506e2f0eb32fb0475205d9f2c9a80
[gcc.git] / libjava / java / lang / natPosixProcess.cc
1 // natPosixProcess.cc - Native side of POSIX process code.
2
3 /* Copyright (C) 1998, 1999, 2000, 2002 Free Software Foundation
4
5 This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
10
11 #include <config.h>
12
13 #ifdef HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <signal.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24
25 #include <gcj/cni.h>
26 #include <jvm.h>
27
28 #include <java/lang/ConcreteProcess.h>
29 #include <java/lang/IllegalThreadStateException.h>
30 #include <java/lang/InterruptedException.h>
31 #include <java/lang/NullPointerException.h>
32 #include <java/lang/Thread.h>
33 #include <java/io/File.h>
34 #include <java/io/FileDescriptor.h>
35 #include <java/io/FileInputStream.h>
36 #include <java/io/FileOutputStream.h>
37 #include <java/io/IOException.h>
38 #include <java/lang/OutOfMemoryError.h>
39
40 extern char **environ;
41
42 void
43 java::lang::ConcreteProcess::destroy (void)
44 {
45 if (! hasExited)
46 {
47 // Really kill it.
48 kill ((pid_t) pid, SIGKILL);
49 }
50 }
51
52 jint
53 java::lang::ConcreteProcess::waitFor (void)
54 {
55 if (! hasExited)
56 {
57 int wstat;
58 int r = waitpid ((pid_t) pid, &wstat, 0);
59
60 if (r == -1)
61 {
62 if (java::lang::Thread::interrupted())
63 throw new InterruptedException (JvNewStringLatin1 (strerror
64 (errno)));
65 }
66 else
67 {
68 hasExited = true;
69
70 if (WIFEXITED (wstat))
71 status = WEXITSTATUS (wstat);
72 else
73 status = -1;
74 }
75 }
76
77 return status;
78 }
79
80 static char *
81 new_string (jstring string)
82 {
83 jsize s = _Jv_GetStringUTFLength (string);
84 char *buf = (char *) _Jv_Malloc (s + 1);
85 _Jv_GetStringUTFRegion (string, 0, s, buf);
86 buf[s] = '\0';
87 return buf;
88 }
89
90 static void
91 cleanup (char **args, char **env)
92 {
93 if (args != NULL)
94 {
95 for (int i = 0; args[i] != NULL; ++i)
96 _Jv_Free (args[i]);
97 _Jv_Free (args);
98 }
99 if (env != NULL)
100 {
101 for (int i = 0; env[i] != NULL; ++i)
102 _Jv_Free (env[i]);
103 _Jv_Free (env);
104 }
105 }
106
107 // This makes our error handling a bit simpler and it lets us avoid
108 // thread bugs where we close a possibly-reopened file descriptor for
109 // a second time.
110 static void
111 myclose (int &fd)
112 {
113 if (fd != -1)
114 close (fd);
115 fd = -1;
116 }
117
118 void
119 java::lang::ConcreteProcess::startProcess (jstringArray progarray,
120 jstringArray envp,
121 java::io::File *dir)
122 {
123 using namespace java::io;
124
125 hasExited = false;
126
127 // Initialize all locals here to make cleanup simpler.
128 char **args = NULL;
129 char **env = NULL;
130 int inp[2], outp[2], errp[2], msgp[2];
131 inp[0] = -1;
132 inp[1] = -1;
133 outp[0] = -1;
134 outp[1] = -1;
135 errp[0] = -1;
136 errp[1] = -1;
137 msgp[0] = -1;
138 msgp[1] = -1;
139 java::lang::Throwable *exc = NULL;
140 errorStream = NULL;
141 inputStream = NULL;
142 outputStream = NULL;
143
144 try
145 {
146 // Transform arrays to native form.
147 args = (char **) _Jv_Malloc ((progarray->length + 1)
148 * sizeof (char *));
149
150 // Initialize so we can gracefully recover.
151 jstring *elts = elements (progarray);
152 for (int i = 0; i <= progarray->length; ++i)
153 args[i] = NULL;
154
155 for (int i = 0; i < progarray->length; ++i)
156 args[i] = new_string (elts[i]);
157 args[progarray->length] = NULL;
158
159 if (envp)
160 {
161 env = (char **) _Jv_Malloc ((envp->length + 1) * sizeof (char *));
162 elts = elements (envp);
163
164 // Initialize so we can gracefully recover.
165 for (int i = 0; i <= envp->length; ++i)
166 env[i] = NULL;
167
168 for (int i = 0; i < envp->length; ++i)
169 env[i] = new_string (elts[i]);
170 env[envp->length] = NULL;
171 }
172
173 // Create pipes for I/O. MSGP is for communicating exec()
174 // status.
175 if (pipe (inp) || pipe (outp) || pipe (errp) || pipe (msgp)
176 || fcntl (msgp[1], F_SETFD, FD_CLOEXEC))
177 throw new IOException (JvNewStringLatin1 (strerror (errno)));
178
179 // We create the streams before forking. Otherwise if we had an
180 // error while creating the streams we would have run the child
181 // with no way to communicate with it.
182 errorStream = new FileInputStream (new FileDescriptor (errp[0]));
183 inputStream = new FileInputStream (new FileDescriptor (inp[0]));
184 outputStream = new FileOutputStream (new FileDescriptor (outp[1]));
185
186 // We don't use vfork() because that would cause the local
187 // environment to be set by the child.
188 if ((pid = (jlong) fork ()) == -1)
189 throw new IOException (JvNewStringLatin1 (strerror (errno)));
190
191 if (pid == 0)
192 {
193 // Child process, so remap descriptors, chdir and exec.
194
195 if (envp)
196 {
197 // Preserve PATH and LD_LIBRARY_PATH unless specified
198 // explicitly.
199 char *path_val = getenv ("PATH");
200 char *ld_path_val = getenv ("LD_LIBRARY_PATH");
201 environ = env;
202 if (getenv ("PATH") == NULL)
203 {
204 char *path_env = (char *) _Jv_Malloc (strlen (path_val)
205 + 5 + 1);
206 strcpy (path_env, "PATH=");
207 strcat (path_env, path_val);
208 putenv (path_env);
209 }
210 if (getenv ("LD_LIBRARY_PATH") == NULL)
211 {
212 char *ld_path_env
213 = (char *) _Jv_Malloc (strlen (ld_path_val) + 16 + 1);
214 strcpy (ld_path_env, "LD_LIBRARY_PATH=");
215 strcat (ld_path_env, ld_path_val);
216 putenv (ld_path_env);
217 }
218 }
219
220 // We ignore errors from dup2 because they should never occur.
221 dup2 (outp[0], 0);
222 dup2 (inp[1], 1);
223 dup2 (errp[1], 2);
224
225 // Use close and not myclose -- we're in the child, and we
226 // aren't worried about the possible race condition.
227 close (inp[0]);
228 close (inp[1]);
229 close (errp[0]);
230 close (errp[1]);
231 close (outp[0]);
232 close (outp[1]);
233 close (msgp[0]);
234
235 // Change directory.
236 if (dir != NULL)
237 {
238 // We don't care about leaking memory here; this process
239 // is about to terminate one way or another.
240 if (chdir (new_string (dir->getPath ())) != 0)
241 {
242 char c = errno;
243 write (msgp[1], &c, 1);
244 _exit (127);
245 }
246 }
247
248 execvp (args[0], args);
249
250 // Send the parent notification that the exec failed.
251 char c = errno;
252 write (msgp[1], &c, 1);
253 _exit (127);
254 }
255
256 // Parent. Close extra file descriptors and mark ours as
257 // close-on-exec.
258 myclose (outp[0]);
259 myclose (inp[1]);
260 myclose (errp[1]);
261 myclose (msgp[1]);
262
263 char c;
264 int r = read (msgp[0], &c, 1);
265 if (r == -1)
266 throw new IOException (JvNewStringLatin1 (strerror (errno)));
267 else if (r != 0)
268 throw new IOException (JvNewStringLatin1 (strerror (c)));
269 }
270 catch (java::lang::Throwable *thrown)
271 {
272 // Do some cleanup we only do on failure. If a stream object
273 // has been created, we must close the stream itself (to avoid
274 // duplicate closes when the stream object is collected).
275 // Otherwise we simply close the underlying file descriptor.
276 // We ignore errors here as they are uninteresting.
277
278 try
279 {
280 if (inputStream != NULL)
281 inputStream->close ();
282 else
283 myclose (inp[0]);
284 }
285 catch (java::lang::Throwable *ignore)
286 {
287 }
288
289 try
290 {
291 if (outputStream != NULL)
292 outputStream->close ();
293 else
294 myclose (outp[1]);
295 }
296 catch (java::lang::Throwable *ignore)
297 {
298 }
299
300 try
301 {
302 if (errorStream != NULL)
303 errorStream->close ();
304 else
305 myclose (errp[0]);
306 }
307 catch (java::lang::Throwable *ignore)
308 {
309 }
310
311 // These are potentially duplicate, but it doesn't matter due to
312 // the use of myclose.
313 myclose (outp[0]);
314 myclose (inp[1]);
315 myclose (errp[1]);
316 myclose (msgp[1]);
317
318 exc = thrown;
319 }
320
321 myclose (msgp[0]);
322 cleanup (args, env);
323
324 if (exc != NULL)
325 throw exc;
326 else
327 {
328 fcntl (outp[1], F_SETFD, FD_CLOEXEC);
329 fcntl (inp[0], F_SETFD, FD_CLOEXEC);
330 fcntl (errp[0], F_SETFD, FD_CLOEXEC);
331 }
332 }