Branch data Line data Source code
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
54 : 564 : pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
55 : : {
56 : 564 : 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 : 564 : int ret = EPKG_OK;
64 : 564 : int stdin_pipe[2] = {-1, -1};
65 : : posix_spawn_file_actions_t action;
66 : 564 : bool use_pipe = 0;
67 : 564 : bool debug = false;
68 : : ssize_t bytes_written;
69 : : long argmax;
70 : 564 : int cur_pipe[2] = {-1, -1};
71 : : #ifdef PROC_REAP_KILL
72 : : bool do_reap;
73 : : pid_t mypid;
74 : : struct procctl_reaper_status info;
75 : : struct procctl_reaper_kill killemall;
76 : : #endif
77 : : struct {
78 : : const char * const arg;
79 : : const pkg_script b;
80 : : const pkg_script a;
81 : 564 : } const map[] = {
82 : : /* a implies b with argument arg */
83 : : {"PRE-INSTALL", PKG_SCRIPT_INSTALL, PKG_SCRIPT_PRE_INSTALL},
84 : : {"POST-INSTALL", PKG_SCRIPT_INSTALL, PKG_SCRIPT_POST_INSTALL},
85 : : {"DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_PRE_DEINSTALL},
86 : : {"POST-DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_POST_DEINSTALL},
87 : : };
88 : :
89 [ - + ]: 564 : if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS"))) {
90 : 0 : return (EPKG_OK);
91 : : }
92 : :
93 [ - + ]: 1035 : for (i = 0; i < NELEM(map); i++) {
94 [ + + ]: 1035 : if (map[i].a == type)
95 : 564 : break;
96 : 471 : }
97 : :
98 [ + - ]: 564 : assert(i < NELEM(map));
99 : :
100 : : #ifdef PROC_REAP_KILL
101 : 564 : mypid = getpid();
102 : 564 : do_reap = procctl(P_PID, mypid, PROC_REAP_ACQUIRE, NULL) == 0;
103 : : #endif
104 [ + + ]: 5640 : for (j = 0; j < PKG_NUM_SCRIPTS; j++) {
105 [ + + ]: 5076 : if (pkg_script_get(pkg, j) == NULL)
106 : 5028 : continue;
107 [ + + - + ]: 48 : if (j == map[i].a || j == map[i].b) {
108 [ - + ]: 21 : xstring_renew(script_cmd);
109 [ + + ]: 21 : if (upgrade) {
110 : 1 : setenv("PKG_UPGRADE", "true", 1);
111 : 1 : }
112 : 21 : setenv("PKG_NAME", pkg->name, 1);
113 : 21 : setenv("PKG_PREFIX", pkg->prefix, 1);
114 [ + + ]: 21 : if (ctx.pkg_rootdir == NULL)
115 : 10 : ctx.pkg_rootdir = "/";
116 : 21 : setenv("PKG_ROOTDIR", ctx.pkg_rootdir, 1);
117 [ + - ]: 21 : if (ctx.ischrooted)
118 : 0 : setenv("PKG_CHROOTED", "true", 1);
119 : 21 : debug = pkg_object_bool(pkg_config_get("DEBUG_SCRIPTS"));
120 [ + - ]: 21 : if (debug)
121 : 0 : fprintf(script_cmd->fp, "set -x\n");
122 : 21 : pkg_fprintf(script_cmd->fp, "set -- %n-%v", pkg, pkg);
123 : :
124 [ + - ]: 21 : if (j == map[i].b) {
125 : : /* add arg **/
126 : 0 : fprintf(script_cmd->fp, " %s", map[i].arg);
127 : 0 : }
128 : :
129 : 21 : fprintf(script_cmd->fp, "\n%s", pkg->scripts[j]->buf);
130 : :
131 : : /* Determine the maximum argument length for the given
132 : : script to determine if /bin/sh -c can be used, or
133 : : if a pipe is required to /bin/sh -s. Similar to
134 : : find(1) determination */
135 [ + - ]: 21 : if ((argmax = sysconf(_SC_ARG_MAX)) == -1)
136 : 0 : argmax = _POSIX_ARG_MAX;
137 : 21 : argmax -= 1024;
138 [ + + ]: 2303 : for (ep = environ; *ep != NULL; ep++)
139 : 2282 : argmax -= strlen(*ep) + 1 + sizeof(*ep);
140 : 21 : argmax -= 1 + sizeof(*ep);
141 : :
142 : 21 : fflush(script_cmd->fp);
143 : 21 : script_len = strlen(script_cmd->buf);
144 : 21 : pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", script_cmd->buf);
145 : 21 : posix_spawn_file_actions_init(&action);
146 [ + - ]: 21 : if (get_socketpair(cur_pipe) == -1) {
147 : 0 : pkg_emit_errno("pkg_script_run", "socketpair");
148 : 0 : goto cleanup;
149 : : }
150 : :
151 [ + - ]: 21 : if (fcntl(cur_pipe[0], F_SETFL, O_NONBLOCK) == -1) {
152 : 0 : pkg_emit_errno("pkg_script_run", "fcntl");
153 : 0 : goto cleanup;
154 : : }
155 : :
156 : 21 : setenv("PKG_MSGFD", "4", 1);
157 : :
158 : 21 : posix_spawn_file_actions_adddup2(&action, cur_pipe[1], 4);
159 : 21 : posix_spawn_file_actions_addclose(&action, cur_pipe[0]);
160 : : /*
161 : : * consider cur_pipe[1] to probably be the lastest
162 : : * opened fd close all unuseful fd up to there
163 : : */
164 [ + + ]: 160 : for (int i = 5; i <= cur_pipe[1]; i++) {
165 [ + + - + ]: 139 : if (i != cur_pipe[0] && i != ctx.devnullfd)
166 : 118 : posix_spawn_file_actions_addclose(&action, i);
167 : 139 : }
168 [ - + ]: 21 : if (script_len > argmax) {
169 [ # # ]: 0 : if (pipe(stdin_pipe) < 0) {
170 : 0 : ret = EPKG_FATAL;
171 : 0 : posix_spawn_file_actions_destroy(&action);
172 : 0 : goto cleanup;
173 : : }
174 : :
175 : 0 : posix_spawn_file_actions_adddup2(&action, stdin_pipe[0],
176 : : STDIN_FILENO);
177 : 0 : posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
178 : :
179 : 0 : argv[0] = _PATH_BSHELL;
180 : 0 : argv[1] = "-s";
181 : 0 : argv[2] = NULL;
182 : :
183 : 0 : use_pipe = 1;
184 : 0 : } else {
185 : 21 : posix_spawn_file_actions_adddup2(&action,
186 : 21 : ctx.devnullfd, STDIN_FILENO);
187 : :
188 : 21 : argv[0] = _PATH_BSHELL;
189 : 21 : argv[1] = "-c";
190 : 21 : argv[2] = script_cmd->buf;
191 : 21 : argv[3] = NULL;
192 : :
193 : 21 : use_pipe = 0;
194 : : }
195 : :
196 [ - + - + ]: 42 : if ((error = posix_spawn(&pid, _PATH_BSHELL, &action,
197 : 21 : NULL, __DECONST(char **, argv),
198 : 42 : environ)) != 0) {
199 : 0 : errno = error;
200 : 0 : pkg_errno("Cannot runscript %s", map[i].arg);
201 : 0 : posix_spawn_file_actions_destroy(&action);
202 : 0 : goto cleanup;
203 : : }
204 : 21 : posix_spawn_file_actions_destroy(&action);
205 : :
206 [ + - ]: 21 : if (use_pipe) {
207 : 0 : script_cmd_p = script_cmd->buf;
208 [ # # ]: 0 : while (script_len > 0) {
209 [ # # # # : 0 : if ((bytes_written = write(stdin_pipe[1], script_cmd_p,
# # ]
210 : 0 : script_len)) == -1) {
211 [ # # ]: 0 : if (errno == EINTR)
212 : 0 : continue;
213 : 0 : ret = EPKG_FATAL;
214 : 0 : goto cleanup;
215 : : }
216 : 0 : script_cmd_p += bytes_written;
217 : 0 : script_len -= bytes_written;
218 : : }
219 : 0 : close(stdin_pipe[1]);
220 : 0 : }
221 : :
222 : 21 : unsetenv("PKG_PREFIX");
223 : :
224 : 21 : close(cur_pipe[1]);
225 : 21 : cur_pipe[1] = -1;
226 : :
227 : 21 : ret = pkg_script_run_child(pid, &pstat, cur_pipe[0], map[i].arg);
228 : :
229 : 21 : close(cur_pipe[0]);
230 : 21 : cur_pipe[0] = -1;
231 : 21 : }
232 : 612 : }
233 : :
234 : : cleanup:
235 : :
236 : 564 : xstring_free(script_cmd);
237 [ + - ]: 564 : if (stdin_pipe[0] != -1)
238 : 0 : close(stdin_pipe[0]);
239 [ + - ]: 564 : if (stdin_pipe[1] != -1)
240 : 0 : close(stdin_pipe[1]);
241 [ + - ]: 564 : if (cur_pipe[0] != -1)
242 : 0 : close(cur_pipe[0]);
243 [ + - ]: 564 : if (cur_pipe[1] != -1)
244 : 0 : close(cur_pipe[1]);
245 : :
246 : : #ifdef PROC_REAP_KILL
247 : : /*
248 : : * If the prior PROCCTL_REAP_ACQUIRE call failed, the kernel
249 : : * probably doesn't support this, so don't try.
250 : : */
251 [ + - ]: 564 : if (!do_reap)
252 : 0 : return (ret);
253 : :
254 : 564 : procctl(P_PID, mypid, PROC_REAP_STATUS, &info);
255 [ + + ]: 564 : if (info.rs_children != 0) {
256 : 1 : killemall.rk_sig = SIGKILL;
257 : 1 : killemall.rk_flags = 0;
258 [ + - ]: 1 : if (procctl(P_PID, mypid, PROC_REAP_KILL, &killemall) != 0) {
259 [ # # # # ]: 0 : if (errno != ESRCH || killemall.rk_killed != 0 ) {
260 : 0 : pkg_errno("%s", "Fail to kill all processes");
261 : 0 : }
262 : 0 : }
263 : 1 : }
264 : 564 : procctl(P_PID, mypid, PROC_REAP_RELEASE, NULL);
265 : : #endif
266 : :
267 : 564 : return (ret);
268 : 564 : }
269 : :
270 : :
271 : : int
272 : 46 : pkg_script_run_child(int pid, int *pstat, int inputfd, const char* script_name) {
273 : : struct pollfd pfd;
274 : : bool wait_for_child;
275 : : char msgbuf[16384+1];
276 : :
277 : :
278 : 46 : memset(&pfd, 0, sizeof(pfd));
279 : 46 : pfd.events = POLLIN | POLLERR | POLLHUP;
280 : 46 : pfd.fd = inputfd;
281 : :
282 : : // Wait for child to exit, and read input, including all queued input on child exit.
283 : 46 : wait_for_child = true;
284 : 27343 : do {
285 : 27343 : pfd.revents = 0;
286 : 27343 : errno = 0;
287 : : // Check if child is running, get exitstatus if newly terminated.
288 : 27343 : pid_t p = 0;
289 [ + - + - : 27343 : while (wait_for_child && (p = waitpid(pid, pstat, WNOHANG)) == -1) {
- + ]
290 [ # # ]: 0 : if (errno != EINTR) {
291 : 0 : pkg_emit_error("waitpid() failed: %s",
292 : 0 : strerror(errno));
293 : 0 : return (EPKG_FATAL);
294 : : }
295 : : }
296 [ + + ]: 27343 : if (p > 0) {
297 : 46 : wait_for_child = false;
298 : 46 : }
299 : : // Check for input from child, but only wait for more if child is still running.
300 : : // Read/print all available input.
301 : : ssize_t readsize;
302 : 27354 : do {
303 : 27354 : readsize = 0;
304 : : int pres;
305 [ - + ]: 27354 : while ((pres = poll(&pfd, 1, wait_for_child ? 1000 : 0)) == -1) {
306 [ # # ]: 0 : if (errno != EINTR) {
307 : 0 : pkg_emit_error("poll() failed: %s",
308 : 0 : strerror(errno));
309 : 0 : return (EPKG_FATAL);
310 : : }
311 : : }
312 [ + + - + ]: 27354 : if (pres > 0 && pfd.revents & POLLIN) {
313 [ + - ]: 27352 : while ((readsize = read(inputfd, msgbuf, sizeof msgbuf - 1)) < 0) {
314 : : // MacOS gives us ECONNRESET on child exit
315 [ # # # # ]: 0 : if (errno == EAGAIN || errno == ECONNRESET) {
316 : 0 : break;
317 : : }
318 [ # # ]: 0 : if (errno != EINTR) {
319 : 0 : pkg_emit_errno(__func__, "read");
320 : 0 : return (EPKG_FATAL);
321 : : }
322 : : }
323 [ + + ]: 27352 : if (readsize > 0) {
324 : 11 : msgbuf[readsize] = '\0';
325 : 11 : pkg_emit_message(msgbuf);
326 : 11 : }
327 : 27352 : }
328 [ + + ]: 27354 : } while (readsize > 0);
329 [ + + ]: 27343 : } while (wait_for_child);
330 : :
331 [ + + ]: 46 : if (WEXITSTATUS(*pstat) != 0) {
332 [ + - ]: 5 : if (WEXITSTATUS(*pstat) == 3)
333 : 0 : exit(0);
334 : :
335 : 5 : pkg_emit_error("%s script failed", script_name);
336 : 5 : return (EPKG_FATAL);
337 : : }
338 : 41 : return (EPKG_OK);
339 : 46 : }
|