Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg - main program for package management | |
3 | * script.c - maintainer script routines | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2007-2014 Guillem Jover <guillem@debian.org> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | * | |
13 | * This is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
20 | */ | |
21 | ||
22 | #include <config.h> | |
23 | #include <compat.h> | |
24 | ||
25 | #include <sys/types.h> | |
26 | #include <sys/stat.h> | |
27 | ||
28 | #include <assert.h> | |
29 | #include <errno.h> | |
30 | #include <string.h> | |
31 | #include <unistd.h> | |
32 | #include <stdlib.h> | |
33 | ||
34 | #ifdef WITH_LIBSELINUX | |
35 | #include <selinux/selinux.h> | |
36 | #endif | |
37 | ||
38 | #include <dpkg/i18n.h> | |
39 | #include <dpkg/debug.h> | |
40 | #include <dpkg/dpkg.h> | |
41 | #include <dpkg/dpkg-db.h> | |
42 | #include <dpkg/pkg.h> | |
43 | #include <dpkg/subproc.h> | |
44 | #include <dpkg/command.h> | |
45 | #include <dpkg/triglib.h> | |
46 | ||
47 | #include "filesdb.h" | |
48 | #include "infodb.h" | |
49 | #include "main.h" | |
50 | ||
51 | void | |
52 | post_postinst_tasks(struct pkginfo *pkg, enum pkgstatus new_status) | |
53 | { | |
54 | if (new_status < PKG_STAT_TRIGGERSAWAITED) | |
55 | pkg_set_status(pkg, new_status); | |
56 | else if (pkg->trigaw.head) | |
57 | pkg_set_status(pkg, PKG_STAT_TRIGGERSAWAITED); | |
58 | else if (pkg->trigpend_head) | |
59 | pkg_set_status(pkg, PKG_STAT_TRIGGERSPENDING); | |
60 | else | |
61 | pkg_set_status(pkg, PKG_STAT_INSTALLED); | |
62 | modstatdb_note(pkg); | |
63 | ||
64 | debug(dbg_triggersdetail, "post_postinst_tasks - trig_incorporate"); | |
65 | trig_incorporate(modstatdb_get_status()); | |
66 | } | |
67 | ||
68 | static void | |
69 | post_script_tasks(void) | |
70 | { | |
71 | debug(dbg_triggersdetail, "post_script_tasks - ensure_diversions"); | |
72 | ensure_diversions(); | |
73 | ||
74 | debug(dbg_triggersdetail, "post_script_tasks - trig_incorporate"); | |
75 | trig_incorporate(modstatdb_get_status()); | |
76 | } | |
77 | ||
78 | static void | |
79 | cu_post_script_tasks(int argc, void **argv) | |
80 | { | |
81 | post_script_tasks(); | |
82 | } | |
83 | ||
84 | static void | |
85 | setexecute(const char *path, struct stat *stab) | |
86 | { | |
87 | if ((stab->st_mode & 0555) == 0555) | |
88 | return; | |
89 | if (!chmod(path, 0755)) | |
90 | return; | |
91 | ohshite(_("unable to set execute permissions on '%.250s'"), path); | |
92 | } | |
93 | ||
94 | /** | |
95 | * Returns the path to the script inside the chroot. | |
96 | */ | |
97 | static const char * | |
98 | maintscript_pre_exec(struct command *cmd) | |
99 | { | |
100 | const char *admindir = dpkg_db_get_dir(); | |
101 | const char *changedir; | |
102 | size_t instdirlen = strlen(instdir); | |
103 | ||
104 | if (instdirlen > 0 && fc_script_chrootless) | |
105 | changedir = instdir; | |
106 | else | |
107 | changedir = "/"; | |
108 | ||
109 | if (instdirlen > 0 && !fc_script_chrootless) { | |
110 | if (strncmp(admindir, instdir, instdirlen) != 0) | |
111 | ohshit(_("admindir must be inside instdir for dpkg to work properly")); | |
112 | if (setenv("DPKG_ADMINDIR", admindir + instdirlen, 1) < 0) | |
113 | ohshite(_("unable to setenv for subprocesses")); | |
114 | if (setenv("DPKG_ROOT", "", 1) < 0) | |
115 | ohshite(_("unable to setenv for subprocesses")); | |
116 | ||
117 | if (chroot(instdir)) | |
118 | ohshite(_("failed to chroot to '%.250s'"), instdir); | |
119 | } | |
120 | /* Switch to a known good directory to give the maintainer script | |
121 | * a saner environment, also needed after the chroot(). */ | |
122 | if (chdir(changedir)) | |
123 | ohshite(_("failed to chdir to '%.255s'"), changedir); | |
124 | if (debug_has_flag(dbg_scripts)) { | |
125 | struct varbuf args = VARBUF_INIT; | |
126 | const char **argv = cmd->argv; | |
127 | ||
128 | while (*++argv) { | |
129 | varbuf_add_char(&args, ' '); | |
130 | varbuf_add_str(&args, *argv); | |
131 | } | |
132 | varbuf_end_str(&args); | |
133 | debug(dbg_scripts, "fork/exec %s (%s )", cmd->filename, | |
134 | args.buf); | |
135 | varbuf_destroy(&args); | |
136 | } | |
137 | if (instdirlen == 0 || fc_script_chrootless) | |
138 | return cmd->filename; | |
139 | ||
140 | assert(strlen(cmd->filename) >= instdirlen); | |
141 | return cmd->filename + instdirlen; | |
142 | } | |
143 | ||
144 | /** | |
145 | * Set a new security execution context for the maintainer script. | |
146 | * | |
147 | * Try to create a new execution context based on the current one and the | |
148 | * specific maintainer script filename. If it's the same as the current | |
149 | * one, use the given fallback. | |
150 | */ | |
151 | static int | |
152 | maintscript_set_exec_context(struct command *cmd, const char *fallback) | |
153 | { | |
154 | int rc = 0; | |
155 | ||
156 | #ifdef WITH_LIBSELINUX | |
157 | rc = setexecfilecon(cmd->filename, fallback); | |
158 | #endif | |
159 | ||
160 | return rc < 0 ? rc : 0; | |
161 | } | |
162 | ||
163 | static int | |
164 | maintscript_exec(struct pkginfo *pkg, struct pkgbin *pkgbin, | |
165 | struct command *cmd, struct stat *stab, int warn) | |
166 | { | |
167 | pid_t pid; | |
168 | int rc; | |
169 | ||
170 | setexecute(cmd->filename, stab); | |
171 | ||
172 | push_cleanup(cu_post_script_tasks, ehflag_bombout, NULL, 0, 0); | |
173 | ||
174 | pid = subproc_fork(); | |
175 | if (pid == 0) { | |
176 | char *pkg_count; | |
177 | const char *maintscript_debug; | |
178 | ||
179 | pkg_count = str_fmt("%d", pkgset_installed_instances(pkg->set)); | |
180 | ||
181 | maintscript_debug = debug_has_flag(dbg_scripts) ? "1" : "0"; | |
182 | ||
183 | if (setenv("DPKG_MAINTSCRIPT_PACKAGE", pkg->set->name, 1) || | |
184 | setenv("DPKG_MAINTSCRIPT_PACKAGE_REFCOUNT", pkg_count, 1) || | |
185 | setenv("DPKG_MAINTSCRIPT_ARCH", pkgbin->arch->name, 1) || | |
186 | setenv("DPKG_MAINTSCRIPT_NAME", cmd->argv[0], 1) || | |
187 | setenv("DPKG_MAINTSCRIPT_DEBUG", maintscript_debug, 1) || | |
188 | setenv("DPKG_RUNNING_VERSION", PACKAGE_VERSION, 1)) | |
189 | ohshite(_("unable to setenv for maintainer script")); | |
190 | ||
191 | cmd->filename = cmd->argv[0] = maintscript_pre_exec(cmd); | |
192 | ||
193 | if (maintscript_set_exec_context(cmd, "dpkg_script_t") < 0) | |
194 | ohshite(_("cannot set security execution context for " | |
195 | "maintainer script")); | |
196 | ||
197 | command_exec(cmd); | |
198 | } | |
199 | subproc_signals_ignore(cmd->name); | |
200 | rc = subproc_reap(pid, cmd->name, warn); | |
201 | subproc_signals_restore(); | |
202 | ||
203 | pop_cleanup(ehflag_normaltidy); | |
204 | ||
205 | return rc; | |
206 | } | |
207 | ||
208 | static int | |
209 | vmaintscript_installed(struct pkginfo *pkg, const char *scriptname, | |
210 | const char *desc, va_list args) | |
211 | { | |
212 | struct command cmd; | |
213 | const char *scriptpath; | |
214 | struct stat stab; | |
215 | char buf[100]; | |
216 | ||
217 | scriptpath = pkg_infodb_get_file(pkg, &pkg->installed, scriptname); | |
218 | sprintf(buf, _("installed %s script"), desc); | |
219 | ||
220 | command_init(&cmd, scriptpath, buf); | |
221 | command_add_arg(&cmd, scriptname); | |
222 | command_add_argv(&cmd, args); | |
223 | ||
224 | if (stat(scriptpath, &stab)) { | |
225 | command_destroy(&cmd); | |
226 | if (errno == ENOENT) { | |
227 | debug(dbg_scripts, | |
228 | "vmaintscript_installed nonexistent %s", | |
229 | scriptname); | |
230 | return 0; | |
231 | } | |
232 | ohshite(_("unable to stat %s '%.250s'"), buf, scriptpath); | |
233 | } | |
234 | maintscript_exec(pkg, &pkg->installed, &cmd, &stab, 0); | |
235 | ||
236 | command_destroy(&cmd); | |
237 | ||
238 | return 1; | |
239 | } | |
240 | ||
241 | /* | |
242 | * All ...'s in maintscript_* are const char *'s. | |
243 | */ | |
244 | ||
245 | int | |
246 | maintscript_installed(struct pkginfo *pkg, const char *scriptname, | |
247 | const char *desc, ...) | |
248 | { | |
249 | va_list args; | |
250 | int rc; | |
251 | ||
252 | va_start(args, desc); | |
253 | rc = vmaintscript_installed(pkg, scriptname, desc, args); | |
254 | va_end(args); | |
255 | ||
256 | if (rc) | |
257 | post_script_tasks(); | |
258 | ||
259 | return rc; | |
260 | } | |
261 | ||
262 | int | |
263 | maintscript_postinst(struct pkginfo *pkg, ...) | |
264 | { | |
265 | va_list args; | |
266 | int rc; | |
267 | ||
268 | va_start(args, pkg); | |
269 | rc = vmaintscript_installed(pkg, POSTINSTFILE, "post-installation", args); | |
270 | va_end(args); | |
271 | ||
272 | if (rc) | |
273 | ensure_diversions(); | |
274 | ||
275 | return rc; | |
276 | } | |
277 | ||
278 | int | |
279 | maintscript_new(struct pkginfo *pkg, const char *scriptname, | |
280 | const char *desc, const char *cidir, char *cidirrest, ...) | |
281 | { | |
282 | struct command cmd; | |
283 | struct stat stab; | |
284 | va_list args; | |
285 | char buf[100]; | |
286 | ||
287 | strcpy(cidirrest, scriptname); | |
288 | sprintf(buf, _("new %s script"), desc); | |
289 | ||
290 | va_start(args, cidirrest); | |
291 | command_init(&cmd, cidir, buf); | |
292 | command_add_arg(&cmd, scriptname); | |
293 | command_add_argv(&cmd, args); | |
294 | va_end(args); | |
295 | ||
296 | if (stat(cidir, &stab)) { | |
297 | command_destroy(&cmd); | |
298 | if (errno == ENOENT) { | |
299 | debug(dbg_scripts, | |
300 | "maintscript_new nonexistent %s '%s'", | |
301 | scriptname, cidir); | |
302 | return 0; | |
303 | } | |
304 | ohshite(_("unable to stat %s '%.250s'"), buf, cidir); | |
305 | } | |
306 | maintscript_exec(pkg, &pkg->available, &cmd, &stab, 0); | |
307 | ||
308 | command_destroy(&cmd); | |
309 | post_script_tasks(); | |
310 | ||
311 | return 1; | |
312 | } | |
313 | ||
314 | int | |
315 | maintscript_fallback(struct pkginfo *pkg, | |
316 | const char *scriptname, const char *desc, | |
317 | const char *cidir, char *cidirrest, | |
318 | const char *ifok, const char *iffallback) | |
319 | { | |
320 | struct command cmd; | |
321 | const char *oldscriptpath; | |
322 | struct stat stab; | |
323 | char buf[100]; | |
324 | ||
325 | oldscriptpath = pkg_infodb_get_file(pkg, &pkg->installed, scriptname); | |
326 | sprintf(buf, _("old %s script"), desc); | |
327 | ||
328 | command_init(&cmd, oldscriptpath, buf); | |
329 | command_add_args(&cmd, scriptname, ifok, | |
330 | versiondescribe(&pkg->available.version, vdew_nonambig), | |
331 | NULL); | |
332 | ||
333 | if (stat(oldscriptpath, &stab)) { | |
334 | if (errno == ENOENT) { | |
335 | debug(dbg_scripts, | |
336 | "maintscript_fallback nonexistent %s '%s'", | |
337 | scriptname, oldscriptpath); | |
338 | command_destroy(&cmd); | |
339 | return 0; | |
340 | } | |
341 | warning(_("unable to stat %s '%.250s': %s"), | |
342 | cmd.name, oldscriptpath, strerror(errno)); | |
343 | } else { | |
344 | if (!maintscript_exec(pkg, &pkg->installed, &cmd, &stab, SUBPROC_WARN)) { | |
345 | command_destroy(&cmd); | |
346 | post_script_tasks(); | |
347 | return 1; | |
348 | } | |
349 | } | |
350 | notice(_("trying script from the new package instead ...")); | |
351 | ||
352 | strcpy(cidirrest, scriptname); | |
353 | sprintf(buf, _("new %s script"), desc); | |
354 | ||
355 | command_destroy(&cmd); | |
356 | command_init(&cmd, cidir, buf); | |
357 | command_add_args(&cmd, scriptname, iffallback, | |
358 | versiondescribe(&pkg->installed.version, vdew_nonambig), | |
359 | versiondescribe(&pkg->available.version, vdew_nonambig), | |
360 | NULL); | |
361 | ||
362 | if (stat(cidir, &stab)) { | |
363 | command_destroy(&cmd); | |
364 | if (errno == ENOENT) | |
365 | ohshit(_("there is no script in the new version of the package - giving up")); | |
366 | else | |
367 | ohshite(_("unable to stat %s '%.250s'"), buf, cidir); | |
368 | } | |
369 | ||
370 | maintscript_exec(pkg, &pkg->available, &cmd, &stab, 0); | |
371 | notice(_("... it looks like that went OK")); | |
372 | ||
373 | command_destroy(&cmd); | |
374 | post_script_tasks(); | |
375 | ||
376 | return 1; | |
377 | } |