1 #include "config.h"
2 #include "gskstreamexternal.h"
3 #include "gskmacros.h"
4 #include "gskerror.h"
5 #include "gskerrno.h"
6 #include "gskghelpers.h"
7 #include <fcntl.h>
8 #include <sys/types.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <errno.h>
14 
15 static GObjectClass *parent_class = NULL;
16 
17 #define DEFAULT_MAX_WRITE_BUFFER		4096
18 #define DEFAULT_MAX_READ_BUFFER			4096
19 #define DEFAULT_MAX_ERROR_LINE_LEN		2048
20 
21 static guint
gsk_stream_external_raw_read(GskStream * stream,gpointer data,guint length,GError ** error)22 gsk_stream_external_raw_read  (GskStream     *stream,
23 			       gpointer       data,
24 			       guint          length,
25 			       GError       **error)
26 {
27   GskStreamExternal *external = GSK_STREAM_EXTERNAL (stream);
28   guint rv = gsk_buffer_read (&external->read_buffer, data, length);
29   if (external->read_buffer.size == 0)
30     gsk_io_clear_idle_notify_read (external);
31   return rv;
32 }
33 
34 static guint
gsk_stream_external_raw_write(GskStream * stream,gconstpointer data,guint length,GError ** error)35 gsk_stream_external_raw_write (GskStream     *stream,
36 			       gconstpointer  data,
37 			       guint          length,
38 			       GError       **error)
39 {
40   GskStreamExternal *external = GSK_STREAM_EXTERNAL (stream);
41   gsize immediate_write = 0;
42   gsize to_buffer;
43 
44   if (external->write_buffer.size == 0)
45     {
46       /* Try to do a write immediately. */
47       int rv = write (external->write_fd, data, length);
48       if (rv < 0)
49 	{
50 	  if (gsk_errno_is_ignorable (errno))
51 	    {
52 	      to_buffer = length;
53 	    }
54 	  else
55 	    {
56 	      g_set_error (error, GSK_G_ERROR_DOMAIN,
57 			   gsk_error_code_from_errno (errno),
58 			   "error writing to external process: %s",
59 			   g_strerror (errno));
60 	      gsk_io_notify_shutdown (GSK_IO (stream));
61 	      return 0;
62 	    }
63 	}
64       else
65 	{
66 	  data = (char*)data + rv;
67 	  to_buffer = length - rv;
68 	  immediate_write = rv;
69 	}
70     }
71   else
72     to_buffer = length;
73 
74   if (to_buffer + external->write_buffer.size > external->max_write_buffer)
75     {
76       if (external->write_buffer.size < external->max_write_buffer)
77 	to_buffer = external->max_write_buffer - external->write_buffer.size;
78       else
79 	to_buffer = 0;
80       gsk_io_clear_idle_notify_write (external);
81     }
82 
83   /* If the buffer is going from empty -> nonempty,
84      request writable notification from the main-loop. */
85   if (external->write_buffer.size == 0
86    && to_buffer > 0)
87     gsk_source_adjust_io (external->write_source, G_IO_OUT);
88 
89   gsk_buffer_append (&external->write_buffer, data, to_buffer);
90 
91   return immediate_write + to_buffer;
92 }
93 
94 static guint
gsk_stream_external_raw_read_buffer(GskStream * stream,GskBuffer * buffer,GError ** error)95 gsk_stream_external_raw_read_buffer (GskStream     *stream,
96 			             GskBuffer     *buffer,
97 			             GError       **error)
98 {
99   GskStreamExternal *external = GSK_STREAM_EXTERNAL (stream);
100   guint rv;
101   (void) error;
102   rv = gsk_buffer_drain (buffer, &external->read_buffer);
103   gsk_io_clear_idle_notify_read (external);
104   return rv;
105 }
106 
107 static guint
gsk_stream_external_raw_write_buffer(GskStream * stream,GskBuffer * buffer,GError ** error)108 gsk_stream_external_raw_write_buffer (GskStream    *stream,
109 			              GskBuffer     *buffer,
110 			              GError       **error)
111 {
112   GskStreamExternal *external = GSK_STREAM_EXTERNAL (stream);
113   guint sys_written = 0;
114   if (external->write_buffer.size == 0)
115     {
116       /* try using writev immediately */
117       int rv = gsk_buffer_writev (buffer, external->write_fd);
118       if (rv < 0)
119 	{
120 	  if (gsk_errno_is_ignorable (errno))
121 	    return 0;
122 	  g_set_error (error, GSK_G_ERROR_DOMAIN,
123 		       gsk_error_code_from_errno (errno),
124 		       "error writing to external process: %s",
125 		       g_strerror (errno));
126 	  gsk_io_notify_shutdown (GSK_IO (stream));
127 	  return 0;
128 	}
129 
130       if (buffer->size > 0)
131 	gsk_source_adjust_io (external->write_source, G_IO_OUT);
132     }
133   if (external->write_buffer.size >= external->max_write_buffer)
134     return sys_written;
135   return sys_written + gsk_buffer_transfer (&external->write_buffer, buffer,
136 					    external->max_write_buffer - external->write_buffer.size);
137 }
138 
139 static void
gsk_stream_external_set_poll_read(GskIO * io,gboolean do_poll)140 gsk_stream_external_set_poll_read   (GskIO      *io,
141 			             gboolean    do_poll)
142 {
143   GskStreamExternal *external = GSK_STREAM_EXTERNAL (io);
144   if (do_poll)
145     {
146       if (external->read_buffer.size < external->max_read_buffer
147        && external->read_source != NULL)
148 	gsk_source_add_io_events (external->read_source, G_IO_IN);
149     }
150   else
151     {
152       if (external->read_source != NULL)
153 	gsk_source_remove_io_events (external->read_source, G_IO_IN);
154     }
155 }
156 
157 static void
gsk_stream_external_set_poll_write(GskIO * io,gboolean do_poll)158 gsk_stream_external_set_poll_write  (GskIO      *io,
159 			             gboolean    do_poll)
160 {
161 #if 0
162   GskStreamExternal *external = GSK_STREAM_EXTERNAL (io);
163   if (do_poll)
164     {
165       ???
166     }
167   else
168     {
169       ???
170     }
171 #endif
172 }
173 
174 static gboolean
gsk_stream_external_shutdown_read(GskIO * io,GError ** error)175 gsk_stream_external_shutdown_read   (GskIO      *io,
176 			             GError    **error)
177 {
178   GskStreamExternal *external = GSK_STREAM_EXTERNAL (io);
179   if (external->read_source != NULL)
180     {
181       gsk_source_remove (external->read_source);
182       external->read_source = NULL;
183     }
184   if (external->read_fd >= 0)
185     {
186       close (external->read_fd);
187       external->read_fd = -1;
188     }
189   return TRUE;
190 }
191 
192 static gboolean
gsk_stream_external_shutdown_write(GskIO * io,GError ** error)193 gsk_stream_external_shutdown_write  (GskIO      *io,
194 			             GError    **error)
195 {
196   GskStreamExternal *external = GSK_STREAM_EXTERNAL (io);
197   if (external->write_source != NULL)
198     {
199       gsk_source_remove (external->write_source);
200       external->write_source = NULL;
201     }
202   if (external->write_fd >= 0)
203     {
204       close (external->write_fd);
205       external->write_fd = -1;
206     }
207   return TRUE;
208 }
209 
210 
211 static void
gsk_stream_external_finalize(GObject * object)212 gsk_stream_external_finalize (GObject *object)
213 {
214   GskStreamExternal *external = GSK_STREAM_EXTERNAL (object);
215   g_assert (external->process_source == NULL);
216   if (external->read_source != NULL)
217     {
218       gsk_source_remove (external->read_source);
219       external->read_source = NULL;
220     }
221   if (external->write_source != NULL)
222     {
223       gsk_source_remove (external->write_source);
224       external->write_source = NULL;
225     }
226   if (external->read_fd >= 0)
227     {
228       close (external->read_fd);
229       external->read_fd = -1;
230     }
231   if (external->write_fd >= 0)
232     {
233       close (external->write_fd);
234       external->write_fd = -1;
235     }
236   if (external->read_err_source != NULL)
237     {
238       gsk_source_remove (external->read_err_source);
239       external->read_err_source = NULL;
240     }
241   if (external->read_err_fd >= 0)
242     {
243       close (external->read_err_fd);
244       external->read_err_fd = -1;
245     }
246   gsk_buffer_destruct (&external->read_buffer);
247   gsk_buffer_destruct (&external->read_err_buffer);
248   gsk_buffer_destruct (&external->write_buffer);
249 
250   (*parent_class->finalize) (object);
251 }
252 
253 static gboolean
handle_read_fd_ready(int fd,GIOCondition condition,gpointer user_data)254 handle_read_fd_ready (int                   fd,
255 		      GIOCondition          condition,
256 		      gpointer              user_data)
257 {
258   GskStreamExternal *external = GSK_STREAM_EXTERNAL (user_data);
259   int rv;
260   gboolean had_data = external->read_buffer.size > 0;
261   g_assert (external->read_fd == fd);
262   if ((condition & G_IO_ERR) == G_IO_ERR)
263     {
264       int err = gsk_errno_from_fd (fd);
265       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_READ,
266 			GSK_ERROR_IO, "error flag on %d: %s", fd,
267 			g_strerror (err));
268       gsk_source_remove (external->read_source);
269       close (fd);
270       external->read_fd = -1;
271       external->read_source = NULL;
272       gsk_io_notify_read_shutdown (external);
273       return FALSE;
274     }
275 
276   if ((condition & G_IO_HUP) == G_IO_HUP)
277     {
278       gsk_source_remove (external->read_source);
279       close (fd);
280       external->read_fd = -1;
281       external->read_source = NULL;
282       gsk_io_notify_read_shutdown (external);
283       return FALSE;
284     }
285 
286   if ((condition & G_IO_IN) != G_IO_IN)
287     return TRUE;
288 
289   rv = gsk_buffer_read_in_fd (&external->read_buffer, fd);
290   if (rv < 0)
291     {
292       if (gsk_errno_is_ignorable (errno))
293 	{
294 	  return TRUE;
295 	}
296       gsk_source_remove (external->read_source);
297       close (fd);
298       external->read_fd = -1;
299       external->read_source = NULL;
300       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_READ,
301 			gsk_error_code_from_errno (errno),
302 			"error reading to low-level stream: %s",
303 			g_strerror (errno));
304       gsk_io_notify_read_shutdown (external);
305       return FALSE;
306     }
307   if (rv == 0)
308     {
309       gsk_source_remove (external->read_source);
310       close (fd);
311       external->read_fd = -1;
312       external->read_source = NULL;
313       gsk_io_notify_read_shutdown (external);
314       return FALSE;
315     }
316   if (!had_data)
317     gsk_io_mark_idle_notify_read (external);
318   return TRUE;
319 }
320 
321 static gboolean
handle_write_fd_ready(int fd,GIOCondition condition,gpointer user_data)322 handle_write_fd_ready (int                   fd,
323 		       GIOCondition          condition,
324 		       gpointer              user_data)
325 {
326   GskStreamExternal *external = GSK_STREAM_EXTERNAL (user_data);
327   int rv;
328 
329   if ((condition & G_IO_ERR) == G_IO_ERR)
330     {
331       int err = gsk_errno_from_fd (fd);
332       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_WRITE,
333 			GSK_ERROR_IO, "error flagged writingon %d: %s", fd,
334 			g_strerror (err));
335       gsk_source_remove (external->write_source);
336       close (fd);
337       external->write_fd = -1;
338       external->write_source = NULL;
339       gsk_io_notify_write_shutdown (external);
340       return FALSE;
341     }
342 
343   if ((condition & G_IO_OUT) != G_IO_OUT)
344     return TRUE;
345 
346   rv = gsk_buffer_writev (&external->write_buffer, fd);
347   if (rv < 0)
348     {
349       if (gsk_errno_is_ignorable (errno))
350 	{
351 	  return TRUE;
352 	}
353       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_WRITE,
354 			gsk_error_code_from_errno (errno),
355 			"error writing to low-level stream: %s",
356 			g_strerror (errno));
357       gsk_source_remove (external->write_source);
358       external->write_fd = -1;
359       external->write_source = NULL;
360       close (fd);
361       gsk_io_notify_write_shutdown (external);
362       return FALSE;
363     }
364 
365   if (external->write_buffer.size == 0)
366     gsk_source_adjust_io (external->write_source, 0);
367   if (external->write_buffer.size < external->max_write_buffer)
368     gsk_io_mark_idle_notify_write (GSK_IO (external));
369   return TRUE;
370 }
371 
372 static gboolean
handle_read_err_fd_ready(int fd,GIOCondition condition,gpointer user_data)373 handle_read_err_fd_ready (int                   fd,
374 		          GIOCondition          condition,
375 		          gpointer              user_data)
376 {
377   GskStreamExternal *external = GSK_STREAM_EXTERNAL (user_data);
378   int rv;
379   char *line;
380 
381   g_assert (external->read_err_fd == fd);
382 
383   if ((condition & G_IO_ERR) == G_IO_ERR)
384     {
385       int err = gsk_errno_from_fd (fd);
386       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_READ,
387 			GSK_ERROR_IO, "error flag reading error from process %ld: %s",
388 			external->pid, g_strerror (err));
389       gsk_source_remove (external->read_err_source);
390       close (fd);
391       external->read_err_fd = -1;
392       external->read_err_source = NULL;
393       return FALSE;
394     }
395 
396   if ((condition & G_IO_HUP) == G_IO_HUP)
397     {
398       gsk_source_remove (external->read_err_source);
399       close (fd);
400       external->read_err_fd = -1;
401       external->read_err_source = NULL;
402       return FALSE;
403     }
404 
405   if ((condition & G_IO_IN) != G_IO_IN)
406     return TRUE;
407 
408   rv = gsk_buffer_read_in_fd (&external->read_err_buffer, fd);
409   if (rv < 0)
410     {
411       if (gsk_errno_is_ignorable (errno))
412 	{
413 	  return TRUE;
414 	}
415       gsk_io_set_error (GSK_IO (external), GSK_IO_ERROR_WRITE,
416 			gsk_error_code_from_errno (errno),
417 			"error reading error messages low-level stream: %s",
418 			g_strerror (errno));
419       gsk_source_remove (external->read_err_source);
420       external->read_err_fd = -1;
421       external->read_err_source = NULL;
422       close (fd);
423       return FALSE;
424     }
425 
426   while ((line = gsk_buffer_read_line (&external->read_err_buffer)) != NULL)
427     {
428       (*external->err_func) (external, line, external->user_data);
429       g_free (line);
430     }
431 
432   return TRUE;
433 }
434 
435 static void
handle_process_terminated(GskMainLoopWaitInfo * info,gpointer user_data)436 handle_process_terminated (GskMainLoopWaitInfo  *info,
437 			   gpointer              user_data)
438 {
439   GskStreamExternal *external = GSK_STREAM_EXTERNAL (user_data);
440   if (external->term_func != NULL)
441     (*external->term_func) (external, info, external->user_data);
442   external->process_source = NULL;
443 }
444 
445 /* --- functions --- */
446 static void
gsk_stream_external_init(GskStreamExternal * stream_external)447 gsk_stream_external_init (GskStreamExternal *stream_external)
448 {
449   stream_external->max_write_buffer = DEFAULT_MAX_WRITE_BUFFER;
450   stream_external->max_read_buffer = DEFAULT_MAX_READ_BUFFER;
451   stream_external->max_err_line_length = DEFAULT_MAX_ERROR_LINE_LEN;
452 }
453 
454 static void
gsk_stream_external_class_init(GskStreamExternalClass * class)455 gsk_stream_external_class_init (GskStreamExternalClass *class)
456 {
457   GObjectClass *object_class = G_OBJECT_CLASS (class);
458   GskStreamClass *stream_class = GSK_STREAM_CLASS (class);
459   GskIOClass *io_class = GSK_IO_CLASS (class);
460   parent_class = g_type_class_peek_parent (class);
461 
462   stream_class->raw_read = gsk_stream_external_raw_read;
463   stream_class->raw_read_buffer = gsk_stream_external_raw_read_buffer;
464   stream_class->raw_write = gsk_stream_external_raw_write;
465   stream_class->raw_write_buffer = gsk_stream_external_raw_write_buffer;
466   io_class->shutdown_read = gsk_stream_external_shutdown_read;
467   io_class->shutdown_write = gsk_stream_external_shutdown_write;
468   io_class->set_poll_read = gsk_stream_external_set_poll_read;
469   io_class->set_poll_write = gsk_stream_external_set_poll_write;
470   object_class->finalize = gsk_stream_external_finalize;
471 }
472 
gsk_stream_external_get_type()473 GType gsk_stream_external_get_type()
474 {
475   static GType stream_external_type = 0;
476   if (!stream_external_type)
477     {
478       static const GTypeInfo stream_external_info =
479       {
480 	sizeof(GskStreamExternalClass),
481 	(GBaseInitFunc) NULL,
482 	(GBaseFinalizeFunc) NULL,
483 	(GClassInitFunc) gsk_stream_external_class_init,
484 	NULL,		/* class_finalize */
485 	NULL,		/* class_data */
486 	sizeof (GskStreamExternal),
487 	0,		/* n_preallocs */
488 	(GInstanceInitFunc) gsk_stream_external_init,
489 	NULL		/* value_table */
490       };
491       stream_external_type = g_type_register_static (GSK_TYPE_STREAM,
492                                                   "GskStreamExternal",
493 						  &stream_external_info, 0);
494     }
495   return stream_external_type;
496 }
497 
498 static inline int
nb_pipe(int pipe_fds[2])499 nb_pipe (int pipe_fds[2])
500 {
501 retry:
502   if (pipe (pipe_fds) < 0)
503     {
504       if (gsk_errno_is_ignorable (errno))
505         goto retry;
506       gsk_errno_fd_creation_failed ();
507       return -1;
508     }
509   gsk_fd_set_nonblocking (pipe_fds[0]);
510   gsk_fd_set_nonblocking (pipe_fds[1]);
511   return 0;
512 }
513 
514 /**
515  * gsk_stream_external_new:
516  * @flags: whether to allocate a pseudo-tty and/or use $PATH.
517  * @stdin_filename: file to redirect as standard input into the process.
518  * If NULL, then the returned stream will be writable, and data
519  * written in will appear as standard-input to the process.
520  * @stdout_filename: file to redirect output from the process's standard-input.
521  * If NULL, then the returned stream will be readable; the data read
522  * will be the process's standard-output.
523  * @term_func: function to call with the process's exit status.
524  * @err_func: function to call with standard error output from the process,
525  * line-by-line.
526  * @user_data: data to pass to term_func and err_func.
527  * @path: name of the executable.
528  * @argv: arguments to pass to the executable.
529  * @env: environment variables, as a NULL-terminated key=value list of strings.
530  * @error: optional error return location.
531  *
532  * Allocates a stream which points to standard input and/or output
533  * of a newly forked process.
534  *
535  * This forks, redirects the standard input from a file if @stdin_filename
536  * is non-NULL, otherwise it uses a pipe to communicate
537  * the data to the main-loop.
538  * It redirects the standard output to a file if @stdout_filename
539  * is non-NULL, otherwise it uses a pipe to communicate
540  * the data from the main-loop.
541  *
542  * When the process terminates, @term_func will be called.
543  * Until @term_func is called, @err_func may be called with lines
544  * of data from the standard-error of the process.
545  *
546  * If @env is non-NULL, then the environment for the subprocess
547  * will consist of nothing but the list given as @env.
548  * If @env is NULL, then the environment will be the same as
549  * the parent process's environment.
550  *
551  * If GSK_STREAM_EXTERNAL_SEARCH_PATH is set, then the executable
552  * will be saught in the colon-separated list of paths
553  * in the $PATH environment-variable.  Otherwise, @path must be
554  * the exact path to the executable.
555  *
556  * If the executable is not found, or exec otherwise fails,
557  * then @term_func will be called with an exit status of 127.
558  *
559  * returns: the new stream.
560  */
561 GskStream *
gsk_stream_external_new(GskStreamExternalFlags flags,const char * stdin_filename,const char * stdout_filename,GskStreamExternalTerminated term_func,GskStreamExternalStderr err_func,gpointer user_data,const char * path,const char * argv[],const char * env[],GError ** error)562 gsk_stream_external_new       (GskStreamExternalFlags      flags,
563 			       const char                 *stdin_filename,
564 			       const char                 *stdout_filename,
565                                GskStreamExternalTerminated term_func,
566                                GskStreamExternalStderr     err_func,
567                                gpointer                    user_data,
568                                const char                 *path,
569                                const char                 *argv[],
570                                const char                 *env[],
571 			       GError                    **error)
572 {
573   GskStreamExternal *external = g_object_new (GSK_TYPE_STREAM_EXTERNAL, NULL);
574   GskMainLoop *main_loop = gsk_main_loop_default ();
575   int rpipe[2], wpipe[2], epipe[2];
576   int fork_rv;
577   if (stdout_filename == NULL)
578     nb_pipe (rpipe);
579   if (stdin_filename == NULL)
580     nb_pipe (wpipe);
581   if (err_func != NULL)
582     nb_pipe (epipe);
583 
584   for (;;)
585     {
586       fork_rv = fork ();
587       if (fork_rv < 0)
588 	{
589 	  int e = errno;
590 	  if (gsk_errno_is_ignorable (e))
591 	    continue;
592 
593 	  /* error: cannot fork, set error, clean up, and return. */
594 	  g_set_error (error, GSK_G_ERROR_DOMAIN,
595 		       gsk_error_code_from_errno (e),
596 		       "fork: %s",
597 		       g_strerror (errno));
598 
599 	  if (stdout_filename == NULL)
600 	    {
601 	      close (rpipe[0]);
602 	      close (rpipe[1]);
603 	    }
604 
605 	  if (stdin_filename == NULL)
606 	    {
607 	      close (wpipe[0]);
608 	      close (wpipe[1]);
609 	    }
610 
611 	  if (err_func != NULL)
612 	    {
613 	      close (epipe[0]);
614 	      close (epipe[1]);
615 	    }
616 	  return NULL;
617 	}
618       else		/* fork >= 0 */
619 	{
620 	  break;	/* success: child and parent get out of this loop */
621 	}
622     }
623 
624   /* Child process */
625   if (fork_rv == 0)
626     {
627       const char *search_path;
628       /* Deal with standard output. */
629       if (stdout_filename == NULL)
630 	{
631 	  dup2 (rpipe[1], STDOUT_FILENO);
632 	  close (rpipe[0]);
633 	  close (rpipe[1]);
634 	}
635       else
636 	{
637 	   int fd = open (stdout_filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
638 	   if (fd < 0)
639 	     {
640                gsk_errno_fd_creation_failed ();
641 	       g_warning ("error opening %s", stdout_filename);
642 	       _exit (126);
643 	     }
644 	   dup2 (fd, STDOUT_FILENO);
645 	   close (fd);
646 	}
647 
648       /* Deal with standard input. */
649       if (stdin_filename == NULL)
650 	{
651 	  dup2 (wpipe[0], STDIN_FILENO);
652 	  close (wpipe[0]);
653 	  close (wpipe[1]);
654 	}
655       else
656 	{
657 	   int fd = open (stdin_filename, O_RDONLY);
658 	   if (fd < 0)
659 	     {
660                gsk_errno_fd_creation_failed ();
661 	       g_warning ("error opening %s", stdin_filename);
662 	       _exit (126);
663 	     }
664 	   dup2 (fd, STDIN_FILENO);
665 	   close (fd);
666 	}
667 
668       /* deal with standard error. */
669       if (err_func != NULL)
670 	{
671 	  dup2 (epipe[1], STDERR_FILENO);
672 	  close (epipe[0]);
673 	  close (epipe[1]);
674 	}
675       else
676 	{
677 	   int fd = open ("/dev/null", O_WRONLY);
678 	   if (fd < 0)
679 	     {
680                gsk_errno_fd_creation_failed ();
681 	       g_warning ("error opening %s", "/dev/null");
682 	       _exit (126);
683 	     }
684 	   dup2 (fd, STDERR_FILENO);
685 	   close (fd);
686 	}
687 
688 
689 #define PATH_SEPARATOR			':'
690 #define IS_PATH_SEPARATOR(ch)		((ch) == PATH_SEPARATOR)
691 #define IS_NOT_PATH_SEPARATOR(ch)	((ch) != PATH_SEPARATOR)
692 
693       gsk_fd_clear_nonblocking (STDIN_FILENO);
694       gsk_fd_clear_nonblocking (STDOUT_FILENO);
695       gsk_fd_clear_nonblocking (STDERR_FILENO);
696 
697       if (strchr (path, '/') == NULL
698        && (flags & GSK_STREAM_EXTERNAL_SEARCH_PATH) == GSK_STREAM_EXTERNAL_SEARCH_PATH
699        && (search_path = g_getenv("PATH")) != NULL)
700 	{
701 	  const char *start, *end;
702 	  int sp_len = strlen (search_path) + 1 + strlen (path) + 1;
703 	  char *scratch = sp_len > 4096 ? g_malloc (sp_len) : g_alloca (sp_len);
704 	  /* search through each component of $PATH. */
705 	  GSK_SKIP_CHAR_TYPE (search_path, IS_PATH_SEPARATOR);
706 	  start = search_path;
707 	  while (*start)
708 	    {
709 	      end = start;
710 	      GSK_SKIP_CHAR_TYPE (end, IS_NOT_PATH_SEPARATOR);
711 	      if (end > start)
712 		{
713 		  memcpy (scratch, start, end - start);
714 		  scratch[end - start] = G_DIR_SEPARATOR;
715 		  strcpy (scratch + (end - start) + 1, path);
716 
717 		  if (env)
718 		    execve (scratch, (char **) argv, (char **) env);
719 		  else
720 		    execv (scratch, (char **) argv);
721 
722 		  /* if not found, continue to the next path component. */
723 		}
724 	      start = end;
725 	      GSK_SKIP_CHAR_TYPE (start, IS_PATH_SEPARATOR);
726 	    }
727 	}
728       else
729 	{
730 	  /* Just exec, no path searching required. */
731 	  if (env)
732 	    execve (path, (char **) argv, (char **) env);
733 	  else
734 	    execv (path, (char **) argv);
735 	}
736       _exit (127);
737     }
738 
739   /* Parent process */
740   if (stdout_filename == NULL)
741     {
742       external->read_fd = rpipe[0];
743       close (rpipe[1]);
744       external->read_source = gsk_main_loop_add_io (main_loop, external->read_fd, G_IO_IN,
745 						    handle_read_fd_ready, external, NULL);
746       gsk_io_mark_is_readable (external);
747     }
748   else
749     {
750       external->read_fd = -1;
751     }
752   if (stdin_filename == NULL)
753     {
754       external->write_fd = wpipe[1];
755       close (wpipe[0]);
756       external->write_source = gsk_main_loop_add_io (main_loop, external->write_fd, 0,
757 						     handle_write_fd_ready, external, NULL);
758       gsk_io_mark_is_writable (external);
759       gsk_io_mark_idle_notify_write (GSK_IO (external));
760     }
761   else
762     {
763       external->write_fd = -1;
764     }
765   if (err_func != NULL)
766     {
767       external->read_err_fd = epipe[0];
768       close (epipe[1]);
769       external->read_err_source = gsk_main_loop_add_io (main_loop, external->read_err_fd, G_IO_IN,
770 						        handle_read_err_fd_ready, external, NULL);
771     }
772   else
773     external->read_err_fd = -1;
774 
775   external->pid = fork_rv;
776   external->process_source = gsk_main_loop_add_waitpid (main_loop, external->pid,
777 							handle_process_terminated,
778 							g_object_ref (external),
779 							g_object_unref);
780 
781   external->term_func = term_func;
782   external->err_func = err_func;
783   external->user_data = user_data;
784 
785   return GSK_STREAM (external);
786 }
787