1248590Smm/*-
2248590Smm * Copyright (c) 2007 Joerg Sonnenberger
3248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA
4248590Smm * All rights reserved.
5248590Smm *
6248590Smm * Redistribution and use in source and binary forms, with or without
7248590Smm * modification, are permitted provided that the following conditions
8248590Smm * are met:
9248590Smm * 1. Redistributions of source code must retain the above copyright
10248590Smm *    notice, this list of conditions and the following disclaimer.
11248590Smm * 2. Redistributions in binary form must reproduce the above copyright
12248590Smm *    notice, this list of conditions and the following disclaimer in the
13248590Smm *    documentation and/or other materials provided with the distribution.
14248590Smm *
15248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25248590Smm */
26248590Smm
27248590Smm#include "archive_platform.h"
28248590Smm
29248590Smm/* This capability is only available on POSIX systems. */
30248590Smm#if defined(HAVE_PIPE) && defined(HAVE_FCNTL) && \
31248590Smm    (defined(HAVE_FORK) || defined(HAVE_VFORK) || defined(HAVE_POSIX_SPAWNP))
32248590Smm
33248590Smm__FBSDID("$FreeBSD: head/lib/libarchive/filter_fork.c 182958 2008-09-12 05:33:00Z kientzle $");
34248590Smm
35248590Smm#if defined(HAVE_SYS_TYPES_H)
36248590Smm#  include <sys/types.h>
37248590Smm#endif
38248590Smm#ifdef HAVE_ERRNO_H
39248590Smm#  include <errno.h>
40248590Smm#endif
41248590Smm#ifdef HAVE_STRING_H
42248590Smm#  include <string.h>
43248590Smm#endif
44248590Smm#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
45248590Smm#  if defined(HAVE_POLL_H)
46248590Smm#    include <poll.h>
47248590Smm#  elif defined(HAVE_SYS_POLL_H)
48248590Smm#    include <sys/poll.h>
49248590Smm#  endif
50248590Smm#elif defined(HAVE_SELECT)
51248590Smm#  if defined(HAVE_SYS_SELECT_H)
52248590Smm#    include <sys/select.h>
53248590Smm#  elif defined(HAVE_UNISTD_H)
54248590Smm#    include <unistd.h>
55248590Smm#  endif
56248590Smm#endif
57248590Smm#ifdef HAVE_FCNTL_H
58248590Smm#  include <fcntl.h>
59248590Smm#endif
60248590Smm#ifdef HAVE_SPAWN_H
61248590Smm#  include <spawn.h>
62248590Smm#endif
63248590Smm#ifdef HAVE_STDLIB_H
64248590Smm#  include <stdlib.h>
65248590Smm#endif
66248590Smm#ifdef HAVE_UNISTD_H
67248590Smm#  include <unistd.h>
68248590Smm#endif
69248590Smm
70248590Smm#include "archive.h"
71248590Smm#include "archive_cmdline_private.h"
72248590Smm
73248590Smm#include "filter_fork.h"
74248590Smm
75248590Smmpid_t
76248590Smm__archive_create_child(const char *cmd, int *child_stdin, int *child_stdout)
77248590Smm{
78248590Smm	pid_t child;
79248590Smm	int stdin_pipe[2], stdout_pipe[2], tmp;
80248590Smm#if HAVE_POSIX_SPAWNP
81248590Smm	posix_spawn_file_actions_t actions;
82248590Smm	int r;
83248590Smm#endif
84248590Smm	struct archive_cmdline *cmdline;
85248590Smm
86248590Smm	cmdline = __archive_cmdline_allocate();
87248590Smm	if (cmdline == NULL)
88248590Smm		goto state_allocated;
89248590Smm	if (__archive_cmdline_parse(cmdline, cmd) != ARCHIVE_OK)
90248590Smm		goto state_allocated;
91248590Smm
92248590Smm	if (pipe(stdin_pipe) == -1)
93248590Smm		goto state_allocated;
94248590Smm	if (stdin_pipe[0] == 1 /* stdout */) {
95248590Smm		if ((tmp = dup(stdin_pipe[0])) == -1)
96248590Smm			goto stdin_opened;
97248590Smm		close(stdin_pipe[0]);
98248590Smm		stdin_pipe[0] = tmp;
99248590Smm	}
100248590Smm	if (pipe(stdout_pipe) == -1)
101248590Smm		goto stdin_opened;
102248590Smm	if (stdout_pipe[1] == 0 /* stdin */) {
103248590Smm		if ((tmp = dup(stdout_pipe[1])) == -1)
104248590Smm			goto stdout_opened;
105248590Smm		close(stdout_pipe[1]);
106248590Smm		stdout_pipe[1] = tmp;
107248590Smm	}
108248590Smm
109248590Smm#if HAVE_POSIX_SPAWNP
110248590Smm
111248590Smm	r = posix_spawn_file_actions_init(&actions);
112248590Smm	if (r != 0) {
113248590Smm		errno = r;
114248590Smm		goto stdout_opened;
115248590Smm	}
116248590Smm	r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[1]);
117248590Smm	if (r != 0)
118248590Smm		goto actions_inited;
119248590Smm	r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[0]);
120248590Smm	if (r != 0)
121248590Smm		goto actions_inited;
122248590Smm	/* Setup for stdin. */
123248590Smm	r = posix_spawn_file_actions_adddup2(&actions, stdin_pipe[0], 0);
124248590Smm	if (r != 0)
125248590Smm		goto actions_inited;
126248590Smm	if (stdin_pipe[0] != 0 /* stdin */) {
127248590Smm		r = posix_spawn_file_actions_addclose(&actions, stdin_pipe[0]);
128248590Smm		if (r != 0)
129248590Smm			goto actions_inited;
130248590Smm	}
131248590Smm	/* Setup for stdout. */
132248590Smm	r = posix_spawn_file_actions_adddup2(&actions, stdout_pipe[1], 1);
133248590Smm	if (r != 0)
134248590Smm		goto actions_inited;
135248590Smm	if (stdout_pipe[1] != 1 /* stdout */) {
136248590Smm		r = posix_spawn_file_actions_addclose(&actions, stdout_pipe[1]);
137248590Smm		if (r != 0)
138248590Smm			goto actions_inited;
139248590Smm	}
140248590Smm	r = posix_spawnp(&child, cmdline->path, &actions, NULL,
141248590Smm		cmdline->argv, NULL);
142248590Smm	if (r != 0)
143248590Smm		goto actions_inited;
144248590Smm	posix_spawn_file_actions_destroy(&actions);
145248590Smm
146248590Smm#else /* HAVE_POSIX_SPAWNP */
147248590Smm
148248590Smm#if HAVE_VFORK
149248590Smm	child = vfork();
150248590Smm#else
151248590Smm	child = fork();
152248590Smm#endif
153248590Smm	if (child == -1)
154248590Smm		goto stdout_opened;
155248590Smm	if (child == 0) {
156248590Smm		close(stdin_pipe[1]);
157248590Smm		close(stdout_pipe[0]);
158248590Smm		if (dup2(stdin_pipe[0], 0 /* stdin */) == -1)
159248590Smm			_exit(254);
160248590Smm		if (stdin_pipe[0] != 0 /* stdin */)
161248590Smm			close(stdin_pipe[0]);
162248590Smm		if (dup2(stdout_pipe[1], 1 /* stdout */) == -1)
163248590Smm			_exit(254);
164248590Smm		if (stdout_pipe[1] != 1 /* stdout */)
165248590Smm			close(stdout_pipe[1]);
166248590Smm		execvp(cmdline->path, cmdline->argv);
167248590Smm		_exit(254);
168248590Smm	}
169248590Smm#endif /* HAVE_POSIX_SPAWNP */
170248590Smm
171248590Smm	close(stdin_pipe[0]);
172248590Smm	close(stdout_pipe[1]);
173248590Smm
174248590Smm	*child_stdin = stdin_pipe[1];
175248590Smm	fcntl(*child_stdin, F_SETFL, O_NONBLOCK);
176248590Smm	*child_stdout = stdout_pipe[0];
177248590Smm	fcntl(*child_stdout, F_SETFL, O_NONBLOCK);
178248590Smm	__archive_cmdline_free(cmdline);
179248590Smm
180248590Smm	return child;
181248590Smm
182248590Smm#if HAVE_POSIX_SPAWNP
183248590Smmactions_inited:
184248590Smm	errno = r;
185248590Smm	posix_spawn_file_actions_destroy(&actions);
186248590Smm#endif
187248590Smmstdout_opened:
188248590Smm	close(stdout_pipe[0]);
189248590Smm	close(stdout_pipe[1]);
190248590Smmstdin_opened:
191248590Smm	close(stdin_pipe[0]);
192248590Smm	close(stdin_pipe[1]);
193248590Smmstate_allocated:
194248590Smm	__archive_cmdline_free(cmdline);
195248590Smm	return -1;
196248590Smm}
197248590Smm
198248590Smmvoid
199248590Smm__archive_check_child(int in, int out)
200248590Smm{
201248590Smm#if defined(HAVE_POLL) && (defined(HAVE_POLL_H) || defined(HAVE_SYS_POLL_H))
202248590Smm	struct pollfd fds[2];
203248590Smm	int idx;
204248590Smm
205248590Smm	idx = 0;
206248590Smm	if (in != -1) {
207248590Smm		fds[idx].fd = in;
208248590Smm		fds[idx].events = POLLOUT;
209248590Smm		++idx;
210248590Smm	}
211248590Smm	if (out != -1) {
212248590Smm		fds[idx].fd = out;
213248590Smm		fds[idx].events = POLLIN;
214248590Smm		++idx;
215248590Smm	}
216248590Smm
217248590Smm	poll(fds, idx, -1); /* -1 == INFTIM, wait forever */
218248590Smm#elif defined(HAVE_SELECT)
219248590Smm	fd_set fds_in, fds_out, fds_error;
220248590Smm
221248590Smm	FD_ZERO(&fds_in);
222248590Smm	FD_ZERO(&fds_out);
223248590Smm	FD_ZERO(&fds_error);
224248590Smm	if (out != -1) {
225248590Smm		FD_SET(out, &fds_in);
226248590Smm		FD_SET(out, &fds_error);
227248590Smm	}
228248590Smm	if (in != -1) {
229248590Smm		FD_SET(in, &fds_out);
230248590Smm		FD_SET(in, &fds_error);
231248590Smm	}
232248590Smm	select(in < out ? out + 1 : in + 1, &fds_in, &fds_out, &fds_error, NULL);
233248590Smm#else
234248590Smm	sleep(1);
235248590Smm#endif
236248590Smm}
237248590Smm
238248590Smm#endif /* defined(HAVE_PIPE) && defined(HAVE_VFORK) && defined(HAVE_FCNTL) */
239