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