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 <paths.h>
39 : #include <spawn.h>
40 : #include <stdlib.h>
41 : #include <limits.h>
42 : #include <string.h>
43 :
44 : #include "pkg.h"
45 : #include "private/pkg.h"
46 : #include "private/event.h"
47 :
48 : extern char **environ;
49 :
50 : int
51 74 : pkg_script_run(struct pkg * const pkg, pkg_script type)
52 : {
53 74 : struct sbuf * const script_cmd = sbuf_new_auto();
54 : size_t i, j;
55 : int error, pstat;
56 : pid_t pid;
57 : const char *script_cmd_p;
58 : const char *argv[4];
59 : char **ep;
60 74 : int ret = EPKG_OK;
61 74 : int stdin_pipe[2] = {-1, -1};
62 : posix_spawn_file_actions_t action;
63 74 : bool use_pipe = 0;
64 74 : bool debug = false;
65 : ssize_t bytes_written;
66 : size_t script_cmd_len;
67 : long argmax;
68 : #ifdef PROC_REAP_KILL
69 : bool do_reap;
70 : struct procctl_reaper_status info;
71 : struct procctl_reaper_kill killemall;
72 : #endif
73 :
74 : struct {
75 : const char * const arg;
76 : const pkg_script b;
77 : const pkg_script a;
78 74 : } const map[] = {
79 : /* a implies b with argument arg */
80 : {"PRE-INSTALL", PKG_SCRIPT_INSTALL, PKG_SCRIPT_PRE_INSTALL},
81 : {"POST-INSTALL", PKG_SCRIPT_INSTALL, PKG_SCRIPT_POST_INSTALL},
82 : {"PRE-UPGRADE", PKG_SCRIPT_UPGRADE, PKG_SCRIPT_PRE_UPGRADE},
83 : {"POST-UPGRADE", PKG_SCRIPT_UPGRADE, PKG_SCRIPT_POST_UPGRADE},
84 : {"DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_PRE_DEINSTALL},
85 : {"POST-DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_POST_DEINSTALL},
86 : };
87 :
88 74 : if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS")))
89 0 : return (EPKG_OK);
90 :
91 159 : for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
92 159 : if (map[i].a == type)
93 74 : break;
94 : }
95 :
96 74 : assert(i < sizeof(map) / sizeof(map[0]));
97 :
98 : #ifdef PROC_REAP_KILL
99 74 : do_reap = procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) == 0;
100 : #endif
101 740 : for (j = 0; j < PKG_NUM_SCRIPTS; j++) {
102 666 : if (pkg_script_get(pkg, j) == NULL)
103 642 : continue;
104 24 : if (j == map[i].a || j == map[i].b) {
105 12 : sbuf_reset(script_cmd);
106 12 : setenv("PKG_PREFIX", pkg->prefix, 1);
107 12 : if (pkg_rootdir != NULL)
108 0 : setenv("PKG_ROOTDIR", pkg_rootdir, 1);
109 12 : debug = pkg_object_bool(pkg_config_get("DEBUG_SCRIPTS"));
110 12 : if (debug)
111 0 : sbuf_printf(script_cmd, "set -x\n");
112 12 : pkg_sbuf_printf(script_cmd, "set -- %n-%v", pkg, pkg);
113 :
114 12 : if (j == map[i].b) {
115 : /* add arg **/
116 0 : sbuf_cat(script_cmd, " ");
117 0 : sbuf_cat(script_cmd, map[i].arg);
118 : }
119 :
120 12 : sbuf_cat(script_cmd, "\n");
121 12 : sbuf_cat(script_cmd, pkg_script_get(pkg, j));
122 12 : sbuf_finish(script_cmd);
123 :
124 : /* Determine the maximum argument length for the given
125 : script to determine if /bin/sh -c can be used, or
126 : if a pipe is required to /bin/sh -s. Similar to
127 : find(1) determination */
128 12 : if ((argmax = sysconf(_SC_ARG_MAX)) == -1)
129 0 : argmax = _POSIX_ARG_MAX;
130 12 : argmax -= 1024;
131 1032 : for (ep = environ; *ep != NULL; ep++)
132 1020 : argmax -= strlen(*ep) + 1 + sizeof(*ep);
133 12 : argmax -= 1 + sizeof(*ep);
134 :
135 12 : pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", sbuf_data(script_cmd));
136 12 : if (sbuf_len(script_cmd) > argmax) {
137 0 : if (pipe(stdin_pipe) < 0) {
138 0 : ret = EPKG_FATAL;
139 0 : goto cleanup;
140 : }
141 :
142 0 : posix_spawn_file_actions_init(&action);
143 0 : posix_spawn_file_actions_adddup2(&action, stdin_pipe[0],
144 : STDIN_FILENO);
145 0 : posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
146 :
147 0 : argv[0] = _PATH_BSHELL;
148 0 : argv[1] = "-s";
149 0 : argv[2] = NULL;
150 :
151 0 : use_pipe = 1;
152 : } else {
153 12 : argv[0] = _PATH_BSHELL;
154 12 : argv[1] = "-c";
155 12 : argv[2] = sbuf_data(script_cmd);
156 12 : argv[3] = NULL;
157 :
158 12 : use_pipe = 0;
159 : }
160 :
161 12 : if ((error = posix_spawn(&pid, _PATH_BSHELL,
162 : use_pipe ? &action : NULL,
163 : NULL, __DECONST(char **, argv),
164 : environ)) != 0) {
165 0 : errno = error;
166 0 : pkg_emit_errno("Cannot run script",
167 : map[i].arg);
168 0 : goto cleanup;
169 : }
170 :
171 12 : if (use_pipe) {
172 0 : script_cmd_p = sbuf_data(script_cmd);
173 0 : script_cmd_len = sbuf_len(script_cmd);
174 0 : while (script_cmd_len > 0) {
175 0 : if ((bytes_written = write(stdin_pipe[1], script_cmd_p,
176 : script_cmd_len)) == -1) {
177 0 : if (errno == EINTR)
178 0 : continue;
179 0 : ret = EPKG_FATAL;
180 0 : goto cleanup;
181 : }
182 0 : script_cmd_p += bytes_written;
183 0 : script_cmd_len -= bytes_written;
184 : }
185 0 : close(stdin_pipe[1]);
186 : }
187 :
188 12 : unsetenv("PKG_PREFIX");
189 :
190 12 : while (waitpid(pid, &pstat, 0) == -1) {
191 0 : if (errno != EINTR)
192 0 : goto cleanup;
193 : }
194 :
195 12 : if (WEXITSTATUS(pstat) != 0) {
196 0 : pkg_emit_error("%s script failed", map[i].arg);
197 0 : goto cleanup;
198 : }
199 : }
200 : }
201 :
202 : cleanup:
203 :
204 74 : sbuf_delete(script_cmd);
205 74 : if (stdin_pipe[0] != -1)
206 0 : close(stdin_pipe[0]);
207 74 : if (stdin_pipe[1] != -1)
208 0 : close(stdin_pipe[1]);
209 :
210 : #ifdef PROC_REAP_KILL
211 : /*
212 : * If the prior PROCCTL_REAP_ACQUIRE call failed, the kernel
213 : * probably doesn't support this, so don't try.
214 : */
215 74 : if (!do_reap)
216 0 : return (ret);
217 :
218 74 : procctl(P_PID, getpid(), PROC_REAP_STATUS, &info);
219 74 : if (info.rs_children != 0) {
220 0 : killemall.rk_sig = SIGKILL;
221 0 : killemall.rk_flags = 0;
222 0 : if (procctl(P_PID, getpid(), PROC_REAP_KILL, &killemall) != 0)
223 0 : pkg_emit_errno("procctl", "PROC_REAP_KILL");
224 : }
225 74 : procctl(P_PID, getpid(), PROC_REAP_RELEASE, NULL);
226 : #endif
227 :
228 74 : return (ret);
229 : }
230 :
|