2 * This file is in the public domain.
3 * You may freely use, modify, distribute, and relicense it.
6 #include "bash.preinst.h"
10 #include <sys/types.h>
15 static void backup(const char *file, const char *dest)
17 const char * const cmd[] = {"cp", "-dp", file, dest, NULL};
22 static void force_symlink(const char *target, const char *link,
26 * Forcibly create a symlink to "target" from "link".
27 * This is performed in two stages with an
28 * intermediate temporary file because symlink(2) cannot
29 * atomically replace an existing file.
31 if ((unlink(temp) && errno != ENOENT) ||
32 symlink(target, temp) ||
34 die_errno("cannot create symlink %s -> %s", link, target);
37 static void reset_diversion(const char *package, const char *file,
40 const char * const remove_old_diversion[] =
41 {"dpkg-divert", "--package", "bash", "--remove", file, NULL};
42 const char * const new_diversion[] =
43 {"dpkg-divert", "--package", package,
44 "--divert", distrib, "--add", file, NULL};
45 run(remove_old_diversion);
49 static int has_binsh_line(FILE *file)
51 char item[sizeof("/bin/sh\n")];
53 while (fgets(item, sizeof(item), file)) {
56 if (!memcmp(item, "/bin/sh\n", strlen("/bin/sh\n") + 1))
58 if (strchr(item, '\n'))
61 /* Finish the line. */
62 for (ch = 0; ch != '\n' && ch != EOF; ch = fgetc(file))
68 die_errno("cannot read pipe");
72 static int binsh_in_filelist(const char *package)
74 const char * const cmd[] = {"dpkg-query", "-L", package, NULL};
81 * dpkg -L $package 2>/dev/null | ...
83 * Redirection of stderr is for quieter output
84 * when $package is not installed. If opening /dev/null
85 * fails, no problem; leave stderr alone in that case.
87 sink = open("/dev/null", O_WRONLY);
90 in = spawn_pipe(&child, cmd, sink);
92 /* ... | grep "^/bin/sh\$" */
93 found = has_binsh_line(in);
95 die_errno("cannot close read end of pipe");
98 * dpkg -L will error out if $package is not already installed.
100 * We stopped reading early if we found a match, so
101 * tolerate SIGPIPE in that case.
103 wait_or_die(child, "dpkg-query -L", ERROR_OK |
104 (found ? SIGPIPE_OK : 0));
108 static int undiverted(const char *path)
110 const char * const cmd[] =
111 {"dpkg-divert", "--listpackage", path, NULL};
113 char packagename[sizeof("bash\n")];
115 FILE *in = spawn_pipe(&child, cmd, -1);
118 /* Is $path diverted by someone other than bash? */
120 len = fread(packagename, 1, sizeof(packagename), in);
122 die_errno("cannot read from dpkg-divert");
124 diverted = 0; /* No diversion. */
125 if (len == strlen("bash\n") && !memcmp(packagename, "bash\n", len))
126 diverted = 0; /* Diverted by bash. */
129 die_errno("cannot close read end of pipe");
130 wait_or_die(child, "dpkg-divert", ERROR_OK | SIGPIPE_OK);
134 int main(int argc, char *argv[])
136 /* /bin/sh needs to point to a valid target. */
138 if (access("/bin/sh", X_OK)) {
139 backup("/bin/sh", "/bin/sh.distrib");
140 backup("/usr/share/man/man1/sh.1.gz",
141 "/usr/share/man/man1/sh.distrib.1.gz");
143 force_symlink("bash", "/bin/sh", "/bin/sh.temp");
144 force_symlink("bash.1.gz", "/usr/share/man/man1/sh.1.gz",
145 "/usr/share/man/man1/sh.1.gz.temp");
147 if (!binsh_in_filelist("bash"))
152 * In bash (<= 4.1-3), the bash package included symlinks for
153 * /bin/sh and the sh(1) manpage in its data.tar.
155 * Unless we are careful, unpacking the new version of bash
156 * will remove them. So we tell dpkg that the files from bash
157 * to be removed are elsewhere, using a diversion on behalf of
160 * Based on an idea by Michael Stone.
161 * “You're one sick individual.” -- Anthony Towns
162 * http://bugs.debian.org/cgi-bin/bugreport.cgi?msg=85;bug=34717
164 if (undiverted("/bin/sh"))
165 reset_diversion("dash", "/bin/sh", "/bin/sh.distrib");
166 if (undiverted("/usr/share/man/man1/sh.1.gz"))
167 reset_diversion("dash", "/usr/share/man/man1/sh.1.gz",
168 "/usr/share/man/man1/sh.distrib.1.gz");