1 // natPosixProcess.cc - Native side of POSIX process code.
2
3 /* Copyright (C) 1998, 1999, 2000, 2002, 2003, 2004 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
destroy(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
waitFor(void)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 *
new_string(jstring string)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, string->length(), buf);
86 buf[s] = '\0';
87 return buf;
88 }
89
90 static void
cleanup(char ** args,char ** env,char * path)91 cleanup (char **args, char **env, char *path)
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 if (path != NULL)
106 _Jv_Free (path);
107 }
108
109 // This makes our error handling a bit simpler and it lets us avoid
110 // thread bugs where we close a possibly-reopened file descriptor for
111 // a second time.
112 static void
myclose(int & fd)113 myclose (int &fd)
114 {
115 if (fd != -1)
116 close (fd);
117 fd = -1;
118 }
119
120 void
startProcess(jstringArray progarray,jstringArray envp,java::io::File * dir)121 java::lang::ConcreteProcess::startProcess (jstringArray progarray,
122 jstringArray envp,
123 java::io::File *dir)
124 {
125 using namespace java::io;
126
127 hasExited = false;
128
129 // Initialize all locals here to make cleanup simpler.
130 char **args = NULL;
131 char **env = NULL;
132 char *path = NULL;
133 int inp[2], outp[2], errp[2], msgp[2];
134 inp[0] = -1;
135 inp[1] = -1;
136 outp[0] = -1;
137 outp[1] = -1;
138 errp[0] = -1;
139 errp[1] = -1;
140 msgp[0] = -1;
141 msgp[1] = -1;
142 java::lang::Throwable *exc = NULL;
143 errorStream = NULL;
144 inputStream = NULL;
145 outputStream = NULL;
146
147 try
148 {
149 // Transform arrays to native form.
150 args = (char **) _Jv_Malloc ((progarray->length + 1)
151 * sizeof (char *));
152
153 // Initialize so we can gracefully recover.
154 jstring *elts = elements (progarray);
155 for (int i = 0; i <= progarray->length; ++i)
156 args[i] = NULL;
157
158 for (int i = 0; i < progarray->length; ++i)
159 args[i] = new_string (elts[i]);
160 args[progarray->length] = NULL;
161
162 if (envp)
163 {
164 env = (char **) _Jv_Malloc ((envp->length + 1) * sizeof (char *));
165 elts = elements (envp);
166
167 // Initialize so we can gracefully recover.
168 for (int i = 0; i <= envp->length; ++i)
169 env[i] = NULL;
170
171 for (int i = 0; i < envp->length; ++i)
172 env[i] = new_string (elts[i]);
173 env[envp->length] = NULL;
174 }
175
176 // We allocate this here because we can't call malloc() after
177 // the fork.
178 if (dir != NULL)
179 path = new_string (dir->getPath ());
180
181 // Create pipes for I/O. MSGP is for communicating exec()
182 // status.
183 if (pipe (inp) || pipe (outp) || pipe (errp) || pipe (msgp)
184 || fcntl (msgp[1], F_SETFD, FD_CLOEXEC))
185 throw new IOException (JvNewStringLatin1 (strerror (errno)));
186
187 // We create the streams before forking. Otherwise if we had an
188 // error while creating the streams we would have run the child
189 // with no way to communicate with it.
190 errorStream = new FileInputStream (new FileDescriptor (errp[0]));
191 inputStream = new FileInputStream (new FileDescriptor (inp[0]));
192 outputStream = new FileOutputStream (new FileDescriptor (outp[1]));
193
194 // We don't use vfork() because that would cause the local
195 // environment to be set by the child.
196 if ((pid = (jlong) fork ()) == -1)
197 throw new IOException (JvNewStringLatin1 (strerror (errno)));
198
199 if (pid == 0)
200 {
201 // Child process, so remap descriptors, chdir and exec.
202
203 if (envp)
204 {
205 // Preserve PATH and LD_LIBRARY_PATH unless specified
206 // explicitly.
207 char *path_val = getenv ("PATH");
208 char *ld_path_val = getenv ("LD_LIBRARY_PATH");
209 environ = env;
210 if (path_val && getenv ("PATH") == NULL)
211 {
212 char *path_env = (char *) _Jv_Malloc (strlen (path_val)
213 + 5 + 1);
214 strcpy (path_env, "PATH=");
215 strcat (path_env, path_val);
216 putenv (path_env);
217 }
218 if (ld_path_val && getenv ("LD_LIBRARY_PATH") == NULL)
219 {
220 char *ld_path_env
221 = (char *) _Jv_Malloc (strlen (ld_path_val) + 16 + 1);
222 strcpy (ld_path_env, "LD_LIBRARY_PATH=");
223 strcat (ld_path_env, ld_path_val);
224 putenv (ld_path_env);
225 }
226 }
227
228 // We ignore errors from dup2 because they should never occur.
229 dup2 (outp[0], 0);
230 dup2 (inp[1], 1);
231 dup2 (errp[1], 2);
232
233 // Use close and not myclose -- we're in the child, and we
234 // aren't worried about the possible race condition.
235 close (inp[0]);
236 close (inp[1]);
237 close (errp[0]);
238 close (errp[1]);
239 close (outp[0]);
240 close (outp[1]);
241 close (msgp[0]);
242
243 // Change directory.
244 if (path != NULL)
245 {
246 if (chdir (path) != 0)
247 {
248 char c = errno;
249 write (msgp[1], &c, 1);
250 _exit (127);
251 }
252 }
253
254 execvp (args[0], args);
255
256 // Send the parent notification that the exec failed.
257 char c = errno;
258 write (msgp[1], &c, 1);
259 _exit (127);
260 }
261
262 // Parent. Close extra file descriptors and mark ours as
263 // close-on-exec.
264 myclose (outp[0]);
265 myclose (inp[1]);
266 myclose (errp[1]);
267 myclose (msgp[1]);
268
269 char c;
270 int r = read (msgp[0], &c, 1);
271 if (r == -1)
272 throw new IOException (JvNewStringLatin1 (strerror (errno)));
273 else if (r != 0)
274 throw new IOException (JvNewStringLatin1 (strerror (c)));
275 }
276 catch (java::lang::Throwable *thrown)
277 {
278 // Do some cleanup we only do on failure. If a stream object
279 // has been created, we must close the stream itself (to avoid
280 // duplicate closes when the stream object is collected).
281 // Otherwise we simply close the underlying file descriptor.
282 // We ignore errors here as they are uninteresting.
283
284 try
285 {
286 if (inputStream != NULL)
287 inputStream->close ();
288 else
289 myclose (inp[0]);
290 }
291 catch (java::lang::Throwable *ignore)
292 {
293 }
294
295 try
296 {
297 if (outputStream != NULL)
298 outputStream->close ();
299 else
300 myclose (outp[1]);
301 }
302 catch (java::lang::Throwable *ignore)
303 {
304 }
305
306 try
307 {
308 if (errorStream != NULL)
309 errorStream->close ();
310 else
311 myclose (errp[0]);
312 }
313 catch (java::lang::Throwable *ignore)
314 {
315 }
316
317 // These are potentially duplicate, but it doesn't matter due to
318 // the use of myclose.
319 myclose (outp[0]);
320 myclose (inp[1]);
321 myclose (errp[1]);
322 myclose (msgp[1]);
323
324 exc = thrown;
325 }
326
327 myclose (msgp[0]);
328 cleanup (args, env, path);
329
330 if (exc != NULL)
331 throw exc;
332 else
333 {
334 fcntl (outp[1], F_SETFD, FD_CLOEXEC);
335 fcntl (inp[0], F_SETFD, FD_CLOEXEC);
336 fcntl (errp[0], F_SETFD, FD_CLOEXEC);
337 }
338 }
339