source: trunk/src/gcc/libjava/java/lang/natPosixProcess.cc@ 1389

Last change on this file since 1389 was 2, checked in by bird, 23 years ago

Initial revision

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