1 /*-
2  * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4  * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer
12  *    in this position and unchanged.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "pkg_config.h"
30 
31 #include <sys/wait.h>
32 #ifdef HAVE_SYS_PROCCTL_H
33 #include <sys/procctl.h>
34 #endif
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <paths.h>
40 #include <poll.h>
41 #include <spawn.h>
42 #include <stdlib.h>
43 #include <limits.h>
44 #include <string.h>
45 #include <xstring.h>
46 
47 #include "pkg.h"
48 #include "private/pkg.h"
49 #include "private/event.h"
50 
51 extern char **environ;
52 
53 int
pkg_script_run(struct pkg * const pkg,pkg_script type,bool upgrade)54 pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
55 {
56 	xstring *script_cmd = NULL;
57 	size_t i, j, script_len;
58 	int error, pstat;
59 	pid_t pid;
60 	const char *script_cmd_p;
61 	const char *argv[4];
62 	char **ep;
63 	int ret = EPKG_OK;
64 	int fd = -1;
65 	int stdin_pipe[2] = {-1, -1};
66 	posix_spawn_file_actions_t action;
67 	bool use_pipe = 0;
68 	bool debug = false;
69 	ssize_t bytes_written;
70 	long argmax;
71 	int cur_pipe[2] = {-1, -1};
72 #ifdef PROC_REAP_KILL
73 	bool do_reap;
74 	pid_t mypid;
75 	struct procctl_reaper_status info;
76 	struct procctl_reaper_kill killemall;
77 #endif
78 	struct {
79 		const char * const arg;
80 		const pkg_script b;
81 		const pkg_script a;
82 	} const map[] = {
83 		/* a implies b with argument arg */
84 		{"PRE-INSTALL",    PKG_SCRIPT_INSTALL,   PKG_SCRIPT_PRE_INSTALL},
85 		{"POST-INSTALL",   PKG_SCRIPT_INSTALL,   PKG_SCRIPT_POST_INSTALL},
86 		{"DEINSTALL",      PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_PRE_DEINSTALL},
87 		{"POST-DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_POST_DEINSTALL},
88 	};
89 
90 	if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS"))) {
91 		return (EPKG_OK);
92 	}
93 
94 	for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
95 		if (map[i].a == type)
96 			break;
97 	}
98 
99 	assert(i < sizeof(map) / sizeof(map[0]));
100 
101 #ifdef PROC_REAP_KILL
102 	mypid = getpid();
103 	do_reap = procctl(P_PID, mypid, PROC_REAP_ACQUIRE, NULL) == 0;
104 #endif
105 	for (j = 0; j < PKG_NUM_SCRIPTS; j++) {
106 		if (pkg_script_get(pkg, j) == NULL)
107 			continue;
108 		if (j == map[i].a || j == map[i].b) {
109 			xstring_renew(script_cmd);
110 			if (upgrade) {
111 				setenv("PKG_UPGRADE", "true", 1);
112 			}
113 			setenv("PKG_NAME", pkg->name, 1);
114 			setenv("PKG_PREFIX", pkg->prefix, 1);
115 			if (ctx.pkg_rootdir == NULL)
116 				ctx.pkg_rootdir = "/";
117 			setenv("PKG_ROOTDIR", ctx.pkg_rootdir, 1);
118 			debug = pkg_object_bool(pkg_config_get("DEBUG_SCRIPTS"));
119 			if (debug)
120 				fprintf(script_cmd->fp, "set -x\n");
121 			pkg_fprintf(script_cmd->fp, "set -- %n-%v", pkg, pkg);
122 
123 			if (j == map[i].b) {
124 				/* add arg **/
125 				fprintf(script_cmd->fp, " %s", map[i].arg);
126 			}
127 
128 			fprintf(script_cmd->fp, "\n%s", pkg->scripts[j]->buf);
129 
130 			/* Determine the maximum argument length for the given
131 			   script to determine if /bin/sh -c can be used, or
132 			   if a pipe is required to /bin/sh -s. Similar to
133 			   find(1) determination */
134 			if ((argmax = sysconf(_SC_ARG_MAX)) == -1)
135 				argmax = _POSIX_ARG_MAX;
136 			argmax -= 1024;
137 			for (ep = environ; *ep != NULL; ep++)
138 				argmax -= strlen(*ep) + 1 + sizeof(*ep);
139 			argmax -= 1 + sizeof(*ep);
140 
141 			fflush(script_cmd->fp);
142 			script_len = strlen(script_cmd->buf);
143 			pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", script_cmd->buf);
144 			posix_spawn_file_actions_init(&action);
145 			if (get_socketpair(cur_pipe) == -1) {
146 				pkg_emit_errno("pkg_script_run", "socketpair");
147 				goto cleanup;
148 			}
149 
150 			if (fcntl(cur_pipe[0], F_SETFL, O_NONBLOCK) == -1) {
151 				pkg_emit_errno("pkg_script_run", "fcntl");
152 				goto cleanup;
153 			}
154 
155 			setenv("PKG_MSGFD", "4", 1);
156 
157 			posix_spawn_file_actions_adddup2(&action, cur_pipe[1], 4);
158 			posix_spawn_file_actions_addclose(&action, cur_pipe[0]);
159 			/*
160 			 * consider cur_pipe[1] to probably be the lastest
161 			 * opened fd close all unuseful fd up to there
162 			 */
163 			for (int i = 5; i <= cur_pipe[1]; i++) {
164 				if (i != cur_pipe[0])
165 					posix_spawn_file_actions_addclose(&action, i);
166 			}
167 			if (script_len > argmax) {
168 				if (pipe(stdin_pipe) < 0) {
169 					ret = EPKG_FATAL;
170 					posix_spawn_file_actions_destroy(&action);
171 					goto cleanup;
172 				}
173 
174 				posix_spawn_file_actions_adddup2(&action, stdin_pipe[0],
175 				    STDIN_FILENO);
176 				posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
177 
178 				argv[0] = _PATH_BSHELL;
179 				argv[1] = "-s";
180 				argv[2] = NULL;
181 
182 				use_pipe = 1;
183 			} else {
184 				fd = open("/dev/null", O_RDWR);
185 				if (fd < 0) {
186 					pkg_errno("Cannot open %s", "/dev/null");
187 					ret = EPKG_FATAL;
188 					posix_spawn_file_actions_destroy(&action);
189 					goto cleanup;
190 				}
191 				posix_spawn_file_actions_adddup2(&action,
192 				    fd, STDIN_FILENO);
193 
194 				argv[0] = _PATH_BSHELL;
195 				argv[1] = "-c";
196 				argv[2] = script_cmd->buf;
197 				argv[3] = NULL;
198 
199 				use_pipe = 0;
200 			}
201 
202 			if ((error = posix_spawn(&pid, _PATH_BSHELL, &action,
203 			    NULL, __DECONST(char **, argv),
204 			    environ)) != 0) {
205 				errno = error;
206 				pkg_errno("Cannot runscript %s", map[i].arg);
207 				posix_spawn_file_actions_destroy(&action);
208 				goto cleanup;
209 			}
210 			posix_spawn_file_actions_destroy(&action);
211 
212 			if (fd != -1)
213 				close(fd);
214 			if (use_pipe) {
215 				script_cmd_p = script_cmd->buf;
216 				while (script_len > 0) {
217 					if ((bytes_written = write(stdin_pipe[1], script_cmd_p,
218 					    script_len)) == -1) {
219 						if (errno == EINTR)
220 							continue;
221 						ret = EPKG_FATAL;
222 						goto cleanup;
223 					}
224 					script_cmd_p += bytes_written;
225 					script_len -= bytes_written;
226 				}
227 				close(stdin_pipe[1]);
228 			}
229 
230 			unsetenv("PKG_PREFIX");
231 
232 			close(cur_pipe[1]);
233 			cur_pipe[1] = -1;
234 
235 			ret = pkg_script_run_child(pid, &pstat, cur_pipe[0], map[i].arg);
236 
237 			close(cur_pipe[0]);
238 			cur_pipe[0] = -1;
239 		}
240 	}
241 
242 cleanup:
243 
244 	xstring_free(script_cmd);
245 	if (stdin_pipe[0] != -1)
246 		close(stdin_pipe[0]);
247 	if (stdin_pipe[1] != -1)
248 		close(stdin_pipe[1]);
249 	if (cur_pipe[0] != -1)
250 		close(cur_pipe[0]);
251 	if (cur_pipe[1] != -1)
252 		close(cur_pipe[1]);
253 
254 #ifdef PROC_REAP_KILL
255 	/*
256 	 * If the prior PROCCTL_REAP_ACQUIRE call failed, the kernel
257 	 * probably doesn't support this, so don't try.
258 	 */
259 	if (!do_reap)
260 		return (ret);
261 
262 	procctl(P_PID, mypid, PROC_REAP_STATUS, &info);
263 	if (info.rs_children != 0) {
264 		killemall.rk_sig = SIGKILL;
265 		killemall.rk_flags = 0;
266 		if (procctl(P_PID, mypid, PROC_REAP_KILL, &killemall) != 0) {
267 			pkg_errno("%s", "Fail to kill all processes");
268 		}
269 	}
270 	procctl(P_PID, mypid, PROC_REAP_RELEASE, NULL);
271 #endif
272 
273 	return (ret);
274 }
275 
276 
277 int
pkg_script_run_child(int pid,int * pstat,int inputfd,const char * script_name)278 pkg_script_run_child(int pid, int *pstat, int inputfd, const char* script_name) {
279 	struct pollfd pfd;
280 	bool wait_for_child;
281 	char msgbuf[16384+1];
282 
283 
284 	memset(&pfd, 0, sizeof(pfd));
285 	pfd.events = POLLIN | POLLERR | POLLHUP;
286 	pfd.fd = inputfd;
287 
288 	// Wait for child to exit, and read input, including all queued input on child exit.
289 	wait_for_child = true;
290 	do {
291 		pfd.revents = 0;
292 		errno = 0;
293 		// Check if child is running, get exitstatus if newly terminated.
294 		pid_t p = 0;
295 		while (wait_for_child && (p = waitpid(pid, pstat, WNOHANG)) == -1) {
296 			if (errno != EINTR) {
297 				pkg_emit_error("waitpid() failed: %s",
298 				    strerror(errno));
299 				return (EPKG_FATAL);
300 			}
301 		}
302 		if (p > 0) {
303 			wait_for_child = false;
304 		}
305 		// Check for input from child, but only wait for more if child is still running.
306 		// Read/print all available input.
307 		ssize_t readsize;
308 		do {
309 			readsize = 0;
310 			int pres;
311 			while ((pres = poll(&pfd, 1, wait_for_child ? 1000 : 0)) == -1) {
312 				if (errno != EINTR) {
313 					pkg_emit_error("poll() failed: %s",
314 					    strerror(errno));
315 					return (EPKG_FATAL);
316 				}
317 			}
318 			if (pres > 0 && pfd.revents & POLLIN) {
319 				while ((readsize = read(inputfd, msgbuf, sizeof msgbuf - 1)) < 0) {
320 					// MacOS gives us ECONNRESET on child exit
321 					if (errno == EAGAIN || errno == ECONNRESET) {
322 						break;
323 					}
324 					if (errno != EINTR) {
325 						pkg_emit_errno(__func__, "read");
326 						return (EPKG_FATAL);
327 					}
328 				}
329 				if (readsize > 0) {
330 					msgbuf[readsize] = '\0';
331 					pkg_emit_message(msgbuf);
332 				}
333 			}
334 		} while (readsize > 0);
335 	} while (wait_for_child);
336 
337 	if (WEXITSTATUS(*pstat) != 0) {
338 		if (WEXITSTATUS(*pstat) == 3)
339 			exit(0);
340 
341 		pkg_emit_error("%s script failed", script_name);
342 		return (EPKG_FATAL);
343 	}
344 	return (EPKG_OK);
345 }
346