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