Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg - main program for package management | |
3 | * configure.c - configure packages | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 1999, 2002 Wichert Akkerman <wichert@deephackmode.org> | |
7 | * Copyright © 2007-2015 Guillem Jover <guillem@debian.org> | |
8 | * Copyright © 2011 Linaro Limited | |
9 | * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> | |
10 | * | |
11 | * This is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or | |
14 | * (at your option) any later version. | |
15 | * | |
16 | * This is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
23 | */ | |
24 | ||
25 | #include <config.h> | |
26 | #include <compat.h> | |
27 | ||
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <sys/wait.h> | |
31 | ||
32 | #include <assert.h> | |
33 | #include <errno.h> | |
34 | #include <ctype.h> | |
35 | #include <string.h> | |
36 | #include <time.h> | |
37 | #include <fcntl.h> | |
38 | #include <dirent.h> | |
39 | #include <termios.h> | |
40 | #include <unistd.h> | |
41 | #include <stdint.h> | |
42 | #include <stdlib.h> | |
43 | #include <stdio.h> | |
44 | ||
45 | #include <dpkg/macros.h> | |
46 | #include <dpkg/i18n.h> | |
47 | #include <dpkg/dpkg.h> | |
48 | #include <dpkg/dpkg-db.h> | |
49 | #include <dpkg/pkg.h> | |
50 | #include <dpkg/string.h> | |
51 | #include <dpkg/buffer.h> | |
52 | #include <dpkg/file.h> | |
53 | #include <dpkg/path.h> | |
54 | #include <dpkg/subproc.h> | |
55 | #include <dpkg/command.h> | |
56 | #include <dpkg/triglib.h> | |
57 | ||
58 | #include "filesdb.h" | |
59 | #include "main.h" | |
60 | ||
61 | enum conffopt { | |
62 | CFOF_PROMPT = DPKG_BIT(0), | |
63 | CFOF_KEEP = DPKG_BIT(1), | |
64 | CFOF_INSTALL = DPKG_BIT(2), | |
65 | CFOF_BACKUP = DPKG_BIT(3), | |
66 | CFOF_NEW_CONFF = DPKG_BIT(4), | |
67 | CFOF_IS_NEW = DPKG_BIT(5), | |
68 | CFOF_IS_OLD = DPKG_BIT(6), | |
69 | CFOF_USER_DEL = DPKG_BIT(7), | |
70 | ||
71 | CFO_KEEP = CFOF_KEEP, | |
72 | CFO_IDENTICAL = CFOF_KEEP, | |
73 | CFO_INSTALL = CFOF_INSTALL, | |
74 | CFO_NEW_CONFF = CFOF_NEW_CONFF | CFOF_INSTALL, | |
75 | CFO_PROMPT = CFOF_PROMPT, | |
76 | CFO_PROMPT_KEEP = CFOF_PROMPT | CFOF_KEEP, | |
77 | CFO_PROMPT_INSTALL = CFOF_PROMPT | CFOF_INSTALL, | |
78 | }; | |
79 | ||
80 | static int conffoptcells[2][2] = { | |
81 | /* Distro !edited. */ /* Distro edited. */ | |
82 | { CFO_KEEP, CFO_INSTALL }, /* User !edited. */ | |
83 | { CFO_KEEP, CFO_PROMPT_KEEP }, /* User edited. */ | |
84 | }; | |
85 | ||
86 | static int | |
87 | show_prompt(const char *cfgfile, const char *realold, const char *realnew, | |
88 | int useredited, int distedited, enum conffopt what) | |
89 | { | |
90 | const char *s; | |
91 | int c, cc; | |
92 | ||
93 | /* Flush the terminal's input in case the user involuntarily | |
94 | * typed some characters. */ | |
95 | tcflush(STDIN_FILENO, TCIFLUSH); | |
96 | ||
97 | fputs("\n", stderr); | |
98 | if (strcmp(cfgfile, realold) == 0) | |
99 | fprintf(stderr, _("Configuration file '%s'\n"), cfgfile); | |
100 | else | |
101 | fprintf(stderr, _("Configuration file '%s' (actually '%s')\n"), | |
102 | cfgfile, realold); | |
103 | ||
104 | if (what & CFOF_IS_NEW) { | |
105 | fprintf(stderr, | |
106 | _(" ==> File on system created by you or by a script.\n" | |
107 | " ==> File also in package provided by package maintainer.\n")); | |
108 | } else { | |
109 | fprintf(stderr, !useredited ? | |
110 | _(" Not modified since installation.\n") : | |
111 | !(what & CFOF_USER_DEL) ? | |
112 | _(" ==> Modified (by you or by a script) since installation.\n") : | |
113 | _(" ==> Deleted (by you or by a script) since installation.\n")); | |
114 | ||
115 | fprintf(stderr, distedited ? | |
116 | _(" ==> Package distributor has shipped an updated version.\n") : | |
117 | _(" Version in package is the same as at last installation.\n")); | |
118 | } | |
119 | ||
120 | /* No --force-confdef but a forcible situation. */ | |
121 | /* TODO: check if this condition can not be simplified to | |
122 | * just !fc_conff_def */ | |
123 | if (!(fc_conff_def && (what & (CFOF_INSTALL | CFOF_KEEP)))) { | |
124 | if (fc_conff_new) { | |
125 | fprintf(stderr, | |
126 | _(" ==> Using new file as you requested.\n")); | |
127 | return 'y'; | |
128 | } else if (fc_conff_old) { | |
129 | fprintf(stderr, | |
130 | _(" ==> Using current old file as you requested.\n")); | |
131 | return 'n'; | |
132 | } | |
133 | } | |
134 | ||
135 | /* Force the default action (if there is one. */ | |
136 | if (fc_conff_def) { | |
137 | if (what & CFOF_KEEP) { | |
138 | fprintf(stderr, | |
139 | _(" ==> Keeping old config file as default.\n")); | |
140 | return 'n'; | |
141 | } else if (what & CFOF_INSTALL) { | |
142 | fprintf(stderr, | |
143 | _(" ==> Using new config file as default.\n")); | |
144 | return 'y'; | |
145 | } | |
146 | } | |
147 | ||
148 | fprintf(stderr, | |
149 | _(" What would you like to do about it ? Your options are:\n" | |
150 | " Y or I : install the package maintainer's version\n" | |
151 | " N or O : keep your currently-installed version\n" | |
152 | " D : show the differences between the versions\n" | |
153 | " Z : start a shell to examine the situation\n")); | |
154 | ||
155 | if (what & CFOF_KEEP) | |
156 | fprintf(stderr, | |
157 | _(" The default action is to keep your current version.\n")); | |
158 | else if (what & CFOF_INSTALL) | |
159 | fprintf(stderr, | |
160 | _(" The default action is to install the new version.\n")); | |
161 | ||
162 | s = path_basename(cfgfile); | |
163 | fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ", s, | |
164 | (what & CFOF_KEEP) ? _("[default=N]") : | |
165 | (what & CFOF_INSTALL) ? _("[default=Y]") : | |
166 | _("[no default]")); | |
167 | ||
168 | if (ferror(stderr)) | |
169 | ohshite(_("error writing to stderr, discovered before conffile prompt")); | |
170 | ||
171 | cc = 0; | |
172 | while ((c = getchar()) != EOF && c != '\n') | |
173 | if (!isspace(c) && !cc) | |
174 | cc = tolower(c); | |
175 | ||
176 | if (c == EOF) { | |
177 | if (ferror(stdin)) | |
178 | ohshite(_("read error on stdin at conffile prompt")); | |
179 | ohshit(_("end of file on stdin at conffile prompt")); | |
180 | } | |
181 | ||
182 | if (!cc) { | |
183 | if (what & CFOF_KEEP) | |
184 | return 'n'; | |
185 | else if (what & CFOF_INSTALL) | |
186 | return 'y'; | |
187 | } | |
188 | ||
189 | return cc; | |
190 | } | |
191 | ||
192 | /** | |
193 | * Show a diff between two files. | |
194 | * | |
195 | * @param old The path to the old file. | |
196 | * @param new The path to the new file. | |
197 | */ | |
198 | static void | |
199 | show_diff(const char *old, const char *new) | |
200 | { | |
201 | pid_t pid; | |
202 | ||
203 | pid = subproc_fork(); | |
204 | if (!pid) { | |
205 | /* Child process. */ | |
206 | char cmdbuf[1024]; | |
207 | ||
208 | sprintf(cmdbuf, DIFF " -Nu %.250s %.250s | %.250s", | |
209 | str_quote_meta(old), str_quote_meta(new), | |
210 | command_get_pager()); | |
211 | ||
212 | command_shell(cmdbuf, _("conffile difference visualizer")); | |
213 | } | |
214 | ||
215 | /* Parent process. */ | |
216 | subproc_reap(pid, _("conffile difference visualizer"), SUBPROC_NOCHECK); | |
217 | } | |
218 | ||
219 | /** | |
220 | * Spawn a new shell. | |
221 | * | |
222 | * Create a subprocess and execute a shell to allow the user to manually | |
223 | * solve the conffile conflict. | |
224 | * | |
225 | * @param confold The path to the old conffile. | |
226 | * @param confnew The path to the new conffile. | |
227 | */ | |
228 | static void | |
229 | spawn_shell(const char *confold, const char *confnew) | |
230 | { | |
231 | pid_t pid; | |
232 | ||
233 | fputs(_("Type 'exit' when you're done.\n"), stderr); | |
234 | ||
235 | pid = subproc_fork(); | |
236 | if (!pid) { | |
237 | /* Set useful variables for the user. */ | |
238 | setenv("DPKG_SHELL_REASON", "conffile-prompt", 1); | |
239 | setenv("DPKG_CONFFILE_OLD", confold, 1); | |
240 | setenv("DPKG_CONFFILE_NEW", confnew, 1); | |
241 | ||
242 | command_shell(NULL, _("conffile shell")); | |
243 | } | |
244 | ||
245 | /* Parent process. */ | |
246 | subproc_reap(pid, _("conffile shell"), SUBPROC_NOCHECK); | |
247 | } | |
248 | ||
249 | /** | |
250 | * Prompt the user for how to resolve a conffile conflict. | |
251 | * | |
252 | * When encountering a conffile conflict during configuration, the user will | |
253 | * normally be presented with a textual menu of possible actions. This | |
254 | * behavior is modified via various --force flags and perhaps on whether | |
255 | * or not a terminal is available to do the prompting. | |
256 | * | |
257 | * @param pkg The package owning the conffile. | |
258 | * @param cfgfile The path to the old conffile. | |
259 | * @param realold The path to the old conffile, dereferenced in case of a | |
260 | * symlink, otherwise equal to cfgfile. | |
261 | * @param realnew The path to the new conffile, dereferenced in case of a | |
262 | * symlink). | |
263 | * @param useredited A flag to indicate whether the file has been edited | |
264 | * locally. Set to nonzero to indicate that the file has been modified. | |
265 | * @param distedited A flag to indicate whether the file has been updated | |
266 | * between package versions. Set to nonzero to indicate that the file | |
267 | * has been updated. | |
268 | * @param what Hints on what action should be taken by default. | |
269 | * | |
270 | * @return The action which should be taken based on user input and/or the | |
271 | * default actions as configured by cmdline/configuration options. | |
272 | */ | |
273 | static enum conffopt | |
274 | promptconfaction(struct pkginfo *pkg, const char *cfgfile, | |
275 | const char *realold, const char *realnew, | |
276 | int useredited, int distedited, enum conffopt what) | |
277 | { | |
278 | int cc; | |
279 | ||
280 | if (!(what & CFOF_PROMPT)) | |
281 | return what; | |
282 | ||
283 | statusfd_send("status: %s : %s : '%s' '%s' %i %i ", | |
284 | cfgfile, "conffile-prompt", | |
285 | realold, realnew, useredited, distedited); | |
286 | ||
287 | do { | |
288 | cc = show_prompt(cfgfile, realold, realnew, | |
289 | useredited, distedited, what); | |
290 | ||
291 | /* FIXME: Say something if silently not install. */ | |
292 | if (cc == 'd') | |
293 | show_diff(realold, realnew); | |
294 | ||
295 | if (cc == 'z') | |
296 | spawn_shell(realold, realnew); | |
297 | } while (!strchr("yino", cc)); | |
298 | ||
299 | log_message("conffile %s %s", cfgfile, | |
300 | (cc == 'i' || cc == 'y') ? "install" : "keep"); | |
301 | ||
302 | what &= CFOF_USER_DEL; | |
303 | ||
304 | switch (cc) { | |
305 | case 'i': | |
306 | case 'y': | |
307 | what |= CFOF_INSTALL | CFOF_BACKUP; | |
308 | break; | |
309 | ||
310 | case 'n': | |
311 | case 'o': | |
312 | what |= CFOF_KEEP | CFOF_BACKUP; | |
313 | break; | |
314 | ||
315 | default: | |
316 | internerr("unknown response '%d'", cc); | |
317 | } | |
318 | ||
319 | return what; | |
320 | } | |
321 | ||
322 | /** | |
323 | * Configure the ghost conffile instance. | |
324 | * | |
325 | * When the first instance of a package set is configured, the *.dpkg-new | |
326 | * files gets installed into their destination, which makes configuration of | |
327 | * conffiles from subsequent package instances be skipped along with updates | |
328 | * to the Conffiles field hash. | |
329 | * | |
330 | * In case the conffile has already been processed, sync the hash from an | |
331 | * already configured package instance conffile. | |
332 | * | |
333 | * @param pkg The current package being configured. | |
334 | * @param conff The current conffile being configured. | |
335 | */ | |
336 | static void | |
337 | deferred_configure_ghost_conffile(struct pkginfo *pkg, struct conffile *conff) | |
338 | { | |
339 | struct pkginfo *otherpkg; | |
340 | struct conffile *otherconff; | |
341 | ||
342 | for (otherpkg = &pkg->set->pkg; otherpkg; otherpkg = otherpkg->arch_next) { | |
343 | if (otherpkg == pkg) | |
344 | continue; | |
345 | if (otherpkg->status <= PKG_STAT_HALFCONFIGURED) | |
346 | continue; | |
347 | ||
348 | for (otherconff = otherpkg->installed.conffiles; otherconff; | |
349 | otherconff = otherconff->next) { | |
350 | if (otherconff->obsolete) | |
351 | continue; | |
352 | ||
353 | /* Check if we need to propagate the new hash from | |
354 | * an already processed conffile in another package | |
355 | * instance. */ | |
356 | if (strcmp(otherconff->name, conff->name) == 0) { | |
357 | conff->hash = otherconff->hash; | |
358 | modstatdb_note(pkg); | |
359 | return; | |
360 | } | |
361 | } | |
362 | } | |
363 | } | |
364 | ||
365 | static void | |
366 | deferred_configure_conffile(struct pkginfo *pkg, struct conffile *conff) | |
367 | { | |
368 | struct filenamenode *usenode; | |
369 | char currenthash[MD5HASHLEN + 1], newdisthash[MD5HASHLEN + 1]; | |
370 | int useredited, distedited; | |
371 | enum conffopt what; | |
372 | struct stat stab; | |
373 | struct varbuf cdr = VARBUF_INIT, cdr2 = VARBUF_INIT; | |
374 | char *cdr2rest; | |
375 | int rc; | |
376 | ||
377 | usenode = namenodetouse(findnamenode(conff->name, fnn_nocopy), | |
378 | pkg, &pkg->installed); | |
379 | ||
380 | rc = conffderef(pkg, &cdr, usenode->name); | |
381 | if (rc == -1) { | |
382 | conff->hash = EMPTYHASHFLAG; | |
383 | return; | |
384 | } | |
385 | md5hash(pkg, currenthash, cdr.buf); | |
386 | ||
387 | varbuf_reset(&cdr2); | |
388 | varbuf_add_str(&cdr2, cdr.buf); | |
389 | varbuf_end_str(&cdr2); | |
390 | /* XXX: Make sure there's enough room for extensions. */ | |
391 | varbuf_grow(&cdr2, 50); | |
392 | cdr2rest = cdr2.buf + strlen(cdr.buf); | |
393 | /* From now on we can just strcpy(cdr2rest, extension); */ | |
394 | ||
395 | strcpy(cdr2rest, DPKGNEWEXT); | |
396 | /* If the .dpkg-new file is no longer there, ignore this one. */ | |
397 | if (lstat(cdr2.buf, &stab)) { | |
398 | if (errno == ENOENT) { | |
399 | /* But, sync the conffile hash value from another | |
400 | * package set instance. */ | |
401 | deferred_configure_ghost_conffile(pkg, conff); | |
402 | return; | |
403 | } | |
404 | ohshite(_("unable to stat new distributed conffile '%.250s'"), | |
405 | cdr2.buf); | |
406 | } | |
407 | md5hash(pkg, newdisthash, cdr2.buf); | |
408 | ||
409 | /* Copy the permissions from the installed version to the new | |
410 | * distributed version. */ | |
411 | if (!stat(cdr.buf, &stab)) | |
412 | file_copy_perms(cdr.buf, cdr2.buf); | |
413 | else if (errno != ENOENT) | |
414 | ohshite(_("unable to stat current installed conffile '%.250s'"), | |
415 | cdr.buf); | |
416 | ||
417 | /* Select what to do. */ | |
418 | if (strcmp(currenthash, newdisthash) == 0) { | |
419 | /* They're both the same so there's no point asking silly | |
420 | * questions. */ | |
421 | useredited = -1; | |
422 | distedited = -1; | |
423 | what = CFO_IDENTICAL; | |
424 | } else if (strcmp(currenthash, NONEXISTENTFLAG) == 0 && fc_conff_miss) { | |
425 | fprintf(stderr, | |
426 | _("\n" | |
427 | "Configuration file '%s', does not exist on system.\n" | |
428 | "Installing new config file as you requested.\n"), | |
429 | usenode->name); | |
430 | what = CFO_NEW_CONFF; | |
431 | useredited = -1; | |
432 | distedited = -1; | |
433 | } else if (strcmp(conff->hash, NEWCONFFILEFLAG) == 0) { | |
434 | if (strcmp(currenthash, NONEXISTENTFLAG) == 0) { | |
435 | what = CFO_NEW_CONFF; | |
436 | useredited = -1; | |
437 | distedited = -1; | |
438 | } else { | |
439 | useredited = 1; | |
440 | distedited = 1; | |
441 | what = conffoptcells[useredited][distedited] | | |
442 | CFOF_IS_NEW; | |
443 | } | |
444 | } else { | |
445 | useredited = strcmp(conff->hash, currenthash) != 0; | |
446 | distedited = strcmp(conff->hash, newdisthash) != 0; | |
447 | ||
448 | if (fc_conff_ask && useredited) | |
449 | what = CFO_PROMPT_KEEP; | |
450 | else | |
451 | what = conffoptcells[useredited][distedited]; | |
452 | ||
453 | if (strcmp(currenthash, NONEXISTENTFLAG) == 0) | |
454 | what |= CFOF_USER_DEL; | |
455 | } | |
456 | ||
457 | debug(dbg_conff, | |
458 | "deferred_configure '%s' (= '%s') useredited=%d distedited=%d what=%o", | |
459 | usenode->name, cdr.buf, useredited, distedited, what); | |
460 | ||
461 | what = promptconfaction(pkg, usenode->name, cdr.buf, cdr2.buf, | |
462 | useredited, distedited, what); | |
463 | ||
464 | switch (what & ~(CFOF_IS_NEW | CFOF_USER_DEL)) { | |
465 | case CFO_KEEP | CFOF_BACKUP: | |
466 | strcpy(cdr2rest, DPKGOLDEXT); | |
467 | if (unlink(cdr2.buf) && errno != ENOENT) | |
468 | warning(_("%s: failed to remove old backup '%.250s': %s"), | |
469 | pkg_name(pkg, pnaw_nonambig), cdr2.buf, | |
470 | strerror(errno)); | |
471 | ||
472 | varbuf_add_str(&cdr, DPKGDISTEXT); | |
473 | varbuf_end_str(&cdr); | |
474 | strcpy(cdr2rest, DPKGNEWEXT); | |
475 | trig_path_activate(usenode, pkg); | |
476 | if (rename(cdr2.buf, cdr.buf)) | |
477 | warning(_("%s: failed to rename '%.250s' to '%.250s': %s"), | |
478 | pkg_name(pkg, pnaw_nonambig), cdr2.buf, cdr.buf, | |
479 | strerror(errno)); | |
480 | break; | |
481 | case CFO_KEEP: | |
482 | strcpy(cdr2rest, DPKGNEWEXT); | |
483 | if (unlink(cdr2.buf)) | |
484 | warning(_("%s: failed to remove '%.250s': %s"), | |
485 | pkg_name(pkg, pnaw_nonambig), cdr2.buf, | |
486 | strerror(errno)); | |
487 | break; | |
488 | case CFO_INSTALL | CFOF_BACKUP: | |
489 | strcpy(cdr2rest, DPKGDISTEXT); | |
490 | if (unlink(cdr2.buf) && errno != ENOENT) | |
491 | warning(_("%s: failed to remove old distributed version '%.250s': %s"), | |
492 | pkg_name(pkg, pnaw_nonambig), cdr2.buf, | |
493 | strerror(errno)); | |
494 | strcpy(cdr2rest, DPKGOLDEXT); | |
495 | if (unlink(cdr2.buf) && errno != ENOENT) | |
496 | warning(_("%s: failed to remove '%.250s' (before overwrite): %s"), | |
497 | pkg_name(pkg, pnaw_nonambig), cdr2.buf, | |
498 | strerror(errno)); | |
499 | if (!(what & CFOF_USER_DEL)) | |
500 | if (link(cdr.buf, cdr2.buf)) | |
501 | warning(_("%s: failed to link '%.250s' to '%.250s': %s"), | |
502 | pkg_name(pkg, pnaw_nonambig), cdr.buf, | |
503 | cdr2.buf, strerror(errno)); | |
504 | /* Fall through. */ | |
505 | case CFO_INSTALL: | |
506 | printf(_("Installing new version of config file %s ...\n"), | |
507 | usenode->name); | |
508 | /* Fall through. */ | |
509 | case CFO_NEW_CONFF: | |
510 | strcpy(cdr2rest, DPKGNEWEXT); | |
511 | trig_path_activate(usenode, pkg); | |
512 | if (rename(cdr2.buf, cdr.buf)) | |
513 | ohshite(_("unable to install '%.250s' as '%.250s'"), | |
514 | cdr2.buf, cdr.buf); | |
515 | break; | |
516 | default: | |
517 | internerr("unknown conffopt '%d'", what); | |
518 | } | |
519 | ||
520 | conff->hash = nfstrsave(newdisthash); | |
521 | modstatdb_note(pkg); | |
522 | ||
523 | varbuf_destroy(&cdr); | |
524 | varbuf_destroy(&cdr2); | |
525 | } | |
526 | ||
527 | /** | |
528 | * Process the deferred configure package. | |
529 | * | |
530 | * The algorithm for deciding what to configure first is as follows: | |
531 | * Loop through all packages doing a ‘try 1’ until we've been round | |
532 | * and nothing has been done, then do ‘try 2’ and ‘try 3’ likewise. | |
533 | * The incrementing of ‘dependtry’ is done by process_queue(). | |
534 | * | |
535 | * Try 1: | |
536 | * Are all dependencies of this package done? If so, do it. | |
537 | * Are any of the dependencies missing or the wrong version? | |
538 | * If so, abort (unless --force-depends, in which case defer). | |
539 | * Will we need to configure a package we weren't given as an | |
540 | * argument? If so, abort ─ except if --force-configure-any, | |
541 | * in which case we add the package to the argument list. | |
542 | * If none of the above, defer the package. | |
543 | * | |
544 | * Try 2: | |
545 | * Find a cycle and break it (see above). | |
546 | * Do as for try 1. | |
547 | * | |
548 | * Try 3 (only if --force-depends-version): | |
549 | * Same as for try 2, but don't mind version number in dependencies. | |
550 | * | |
551 | * Try 4 (only if --force-depends): | |
552 | * Do anyway. | |
553 | * | |
554 | * @param pkg The package to act on. | |
555 | */ | |
556 | void | |
557 | deferred_configure(struct pkginfo *pkg) | |
558 | { | |
559 | struct varbuf aemsgs = VARBUF_INIT; | |
560 | struct conffile *conff; | |
561 | struct pkginfo *otherpkg; | |
562 | enum dep_check ok; | |
563 | ||
564 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
565 | ohshit(_("no package named '%s' is installed, cannot configure"), | |
566 | pkg_name(pkg, pnaw_nonambig)); | |
567 | if (pkg->status == PKG_STAT_INSTALLED) | |
568 | ohshit(_("package %.250s is already installed and configured"), | |
569 | pkg_name(pkg, pnaw_nonambig)); | |
570 | if (pkg->status != PKG_STAT_UNPACKED && | |
571 | pkg->status != PKG_STAT_HALFCONFIGURED) | |
572 | ohshit(_("package %.250s is not ready for configuration\n" | |
573 | " cannot configure (current status '%.250s')"), | |
574 | pkg_name(pkg, pnaw_nonambig), | |
575 | pkg_status_name(pkg)); | |
576 | ||
577 | for (otherpkg = &pkg->set->pkg; otherpkg; otherpkg = otherpkg->arch_next) { | |
578 | if (otherpkg == pkg) | |
579 | continue; | |
580 | if (otherpkg->status <= PKG_STAT_CONFIGFILES) | |
581 | continue; | |
582 | ||
583 | if (otherpkg->status < PKG_STAT_UNPACKED) | |
584 | ohshit(_("package %s cannot be configured because " | |
585 | "%s is not ready (current status '%s')"), | |
586 | pkg_name(pkg, pnaw_always), | |
587 | pkg_name(otherpkg, pnaw_always), | |
588 | pkg_status_name(otherpkg)); | |
589 | ||
590 | if (dpkg_version_compare(&pkg->installed.version, | |
591 | &otherpkg->installed.version)) | |
592 | ohshit(_("package %s %s cannot be configured because " | |
593 | "%s is at a different version (%s)"), | |
594 | pkg_name(pkg, pnaw_always), | |
595 | versiondescribe(&pkg->installed.version, | |
596 | vdew_nonambig), | |
597 | pkg_name(otherpkg, pnaw_always), | |
598 | versiondescribe(&otherpkg->installed.version, | |
599 | vdew_nonambig)); | |
600 | } | |
601 | ||
602 | if (dependtry > 1) | |
603 | if (findbreakcycle(pkg)) | |
604 | sincenothing = 0; | |
605 | ||
606 | ok = dependencies_ok(pkg, NULL, &aemsgs); | |
607 | if (ok == DEP_CHECK_DEFER) { | |
608 | varbuf_destroy(&aemsgs); | |
609 | pkg->clientdata->istobe = PKG_ISTOBE_INSTALLNEW; | |
610 | enqueue_package(pkg); | |
611 | return; | |
612 | } | |
613 | ||
614 | trigproc_reset_cycle(); | |
615 | ||
616 | /* | |
617 | * At this point removal from the queue is confirmed. This | |
618 | * represents irreversible progress wrt trigger cycles. Only | |
619 | * packages in PKG_STAT_UNPACKED are automatically added to the | |
620 | * configuration queue, and during configuration and trigger | |
621 | * processing new packages can't enter into unpacked. | |
622 | */ | |
623 | ||
624 | ok = breakses_ok(pkg, &aemsgs) ? ok : DEP_CHECK_HALT; | |
625 | if (ok == DEP_CHECK_HALT) { | |
626 | sincenothing = 0; | |
627 | varbuf_end_str(&aemsgs); | |
628 | notice(_("dependency problems prevent configuration of %s:\n%s"), | |
629 | pkg_name(pkg, pnaw_nonambig), aemsgs.buf); | |
630 | varbuf_destroy(&aemsgs); | |
631 | ohshit(_("dependency problems - leaving unconfigured")); | |
632 | } else if (aemsgs.used) { | |
633 | varbuf_end_str(&aemsgs); | |
634 | notice(_("%s: dependency problems, but configuring anyway as you requested:\n%s"), | |
635 | pkg_name(pkg, pnaw_nonambig), aemsgs.buf); | |
636 | } | |
637 | varbuf_destroy(&aemsgs); | |
638 | sincenothing = 0; | |
639 | ||
640 | if (pkg->eflag & PKG_EFLAG_REINSTREQ) | |
641 | forcibleerr(fc_removereinstreq, | |
642 | _("package is in a very bad inconsistent state; you should\n" | |
643 | " reinstall it before attempting configuration")); | |
644 | ||
645 | printf(_("Setting up %s (%s) ...\n"), pkg_name(pkg, pnaw_nonambig), | |
646 | versiondescribe(&pkg->installed.version, vdew_nonambig)); | |
647 | log_action("configure", pkg, &pkg->installed); | |
648 | ||
649 | trig_activate_packageprocessing(pkg); | |
650 | ||
651 | if (f_noact) { | |
652 | pkg_set_status(pkg, PKG_STAT_INSTALLED); | |
653 | pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; | |
654 | return; | |
655 | } | |
656 | ||
657 | if (pkg->status == PKG_STAT_UNPACKED) { | |
658 | debug(dbg_general, "deferred_configure updating conffiles"); | |
659 | /* This will not do at all the right thing with overridden | |
660 | * conffiles or conffiles that are the ‘target’ of an override; | |
661 | * all the references here would be to the ‘contested’ | |
662 | * filename, and in any case there'd only be one hash for both | |
663 | * ‘versions’ of the conffile. | |
664 | * | |
665 | * Overriding conffiles is a silly thing to do anyway :-). */ | |
666 | ||
667 | modstatdb_note(pkg); | |
668 | ||
669 | /* On entry, the ‘new’ version of each conffile has been | |
670 | * unpacked as ‘*.dpkg-new’, and the ‘installed’ version is | |
671 | * as-yet untouched in ‘*’. The hash of the ‘old distributed’ | |
672 | * version is in the conffiles data for the package. If | |
673 | * ‘*.dpkg-new’ no longer exists we assume that we've | |
674 | * already processed this one. */ | |
675 | for (conff = pkg->installed.conffiles; conff; conff = conff->next) { | |
676 | if (conff->obsolete) | |
677 | continue; | |
678 | deferred_configure_conffile(pkg, conff); | |
679 | } | |
680 | ||
681 | pkg_set_status(pkg, PKG_STAT_HALFCONFIGURED); | |
682 | } | |
683 | ||
684 | assert(pkg->status == PKG_STAT_HALFCONFIGURED); | |
685 | ||
686 | modstatdb_note(pkg); | |
687 | ||
688 | maintscript_postinst(pkg, "configure", | |
689 | dpkg_version_is_informative(&pkg->configversion) ? | |
690 | versiondescribe(&pkg->configversion, | |
691 | vdew_nonambig) : "", | |
692 | NULL); | |
693 | ||
694 | pkg_reset_eflags(pkg); | |
695 | pkg->trigpend_head = NULL; | |
696 | post_postinst_tasks(pkg, PKG_STAT_INSTALLED); | |
697 | } | |
698 | ||
699 | /** | |
700 | * Dereference a file by following all possibly used symlinks. | |
701 | * | |
702 | * @param[in] pkg The package to act on. | |
703 | * @param[out] result The dereference conffile path. | |
704 | * @param[in] in The conffile path to dereference. | |
705 | * | |
706 | * @return An error code for the operation. | |
707 | * @retval 0 Everything went ok. | |
708 | * @retval -1 Otherwise. | |
709 | */ | |
710 | int | |
711 | conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in) | |
712 | { | |
713 | static struct varbuf target = VARBUF_INIT; | |
714 | struct stat stab; | |
715 | ssize_t r; | |
716 | int loopprotect; | |
717 | ||
718 | varbuf_reset(result); | |
719 | varbuf_add_str(result, instdir); | |
720 | varbuf_add_str(result, in); | |
721 | varbuf_end_str(result); | |
722 | ||
723 | loopprotect = 0; | |
724 | ||
725 | for (;;) { | |
726 | debug(dbg_conffdetail, "conffderef in='%s' current working='%s'", | |
727 | in, result->buf); | |
728 | if (lstat(result->buf, &stab)) { | |
729 | if (errno != ENOENT) | |
730 | warning(_("%s: unable to stat config file '%s'\n" | |
731 | " (= '%s'): %s"), | |
732 | pkg_name(pkg, pnaw_nonambig), in, | |
733 | result->buf, strerror(errno)); | |
734 | debug(dbg_conffdetail, "conffderef nonexistent"); | |
735 | return 0; | |
736 | } else if (S_ISREG(stab.st_mode)) { | |
737 | debug(dbg_conff, "conffderef in='%s' result='%s'", | |
738 | in, result->buf); | |
739 | return 0; | |
740 | } else if (S_ISLNK(stab.st_mode)) { | |
741 | debug(dbg_conffdetail, "conffderef symlink loopprotect=%d", | |
742 | loopprotect); | |
743 | if (loopprotect++ >= 25) { | |
744 | warning(_("%s: config file '%s' is a circular link\n" | |
745 | " (= '%s')"), | |
746 | pkg_name(pkg, pnaw_nonambig), in, | |
747 | result->buf); | |
748 | return -1; | |
749 | } | |
750 | ||
751 | varbuf_reset(&target); | |
752 | varbuf_grow(&target, stab.st_size + 1); | |
753 | r = readlink(result->buf, target.buf, target.size); | |
754 | if (r < 0) { | |
755 | warning(_("%s: unable to readlink conffile '%s'\n" | |
756 | " (= '%s'): %s"), | |
757 | pkg_name(pkg, pnaw_nonambig), in, | |
758 | result->buf, strerror(errno)); | |
759 | return -1; | |
760 | } else if (r != stab.st_size) { | |
761 | warning(_("symbolic link '%.250s' size has " | |
762 | "changed from %jd to %zd"), | |
763 | result->buf, (intmax_t)stab.st_size, r); | |
764 | /* If the returned size is smaller, let's | |
765 | * proceed, otherwise error out. */ | |
766 | if (r > stab.st_size) | |
767 | return -1; | |
768 | } | |
769 | varbuf_trunc(&target, r); | |
770 | varbuf_end_str(&target); | |
771 | ||
772 | debug(dbg_conffdetail, | |
773 | "conffderef readlink gave %zd, '%s'", | |
774 | r, target.buf); | |
775 | ||
776 | if (target.buf[0] == '/') { | |
777 | varbuf_reset(result); | |
778 | varbuf_add_str(result, instdir); | |
779 | debug(dbg_conffdetail, | |
780 | "conffderef readlink absolute"); | |
781 | } else { | |
782 | for (r = result->used - 1; r > 0 && result->buf[r] != '/'; r--) | |
783 | ; | |
784 | if (r < 0) { | |
785 | warning(_("%s: conffile '%.250s' resolves to degenerate filename\n" | |
786 | " ('%s' is a symlink to '%s')"), | |
787 | pkg_name(pkg, pnaw_nonambig), | |
788 | in, result->buf, target.buf); | |
789 | return -1; | |
790 | } | |
791 | if (result->buf[r] == '/') | |
792 | r++; | |
793 | varbuf_trunc(result, r); | |
794 | debug(dbg_conffdetail, | |
795 | "conffderef readlink relative to '%.*s'", | |
796 | (int)result->used, result->buf); | |
797 | } | |
798 | varbuf_add_buf(result, target.buf, target.used); | |
799 | varbuf_end_str(result); | |
800 | } else { | |
801 | warning(_("%s: conffile '%.250s' is not a plain file or symlink (= '%s')"), | |
802 | pkg_name(pkg, pnaw_nonambig), in, result->buf); | |
803 | return -1; | |
804 | } | |
805 | } | |
806 | } | |
807 | ||
808 | /** | |
809 | * Generate a file contents MD5 hash. | |
810 | * | |
811 | * The caller is responsible for providing a buffer for the hash result | |
812 | * at least MD5HASHLEN + 1 characters long. | |
813 | * | |
814 | * @param[in] pkg The package to act on. | |
815 | * @param[out] hashbuf The buffer to store the generated hash. | |
816 | * @param[in] fn The filename. | |
817 | */ | |
818 | void | |
819 | md5hash(struct pkginfo *pkg, char *hashbuf, const char *fn) | |
820 | { | |
821 | struct dpkg_error err; | |
822 | static int fd; | |
823 | ||
824 | fd = open(fn, O_RDONLY); | |
825 | ||
826 | if (fd >= 0) { | |
827 | push_cleanup(cu_closefd, ehflag_bombout, NULL, 0, 1, &fd); | |
828 | if (fd_md5(fd, hashbuf, -1, &err) < 0) | |
829 | ohshit(_("cannot compute MD5 hash for file '%s': %s"), | |
830 | fn, err.str); | |
831 | pop_cleanup(ehflag_normaltidy); /* fd = open(cdr.buf) */ | |
832 | close(fd); | |
833 | } else if (errno == ENOENT) { | |
834 | strcpy(hashbuf, NONEXISTENTFLAG); | |
835 | } else { | |
836 | warning(_("%s: unable to open %s for hash: %s"), | |
837 | pkg_name(pkg, pnaw_nonambig), fn, strerror(errno)); | |
838 | strcpy(hashbuf, EMPTYHASHFLAG); | |
839 | } | |
840 | } |